Browse Source

Merge remote-tracking branch 'origin/master'

yusm 5 tháng trước cách đây
mục cha
commit
255c9f61b0
19 tập tin đã thay đổi với 519 bổ sung130 xóa
  1. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/fontFile/PingFang Medium.ttf
  2. 6 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/fontFile/font.css
  3. 26 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/formForm/formItem.vue
  4. 99 65
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/pullDownSelector.vue
  5. 27 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/translationComponent.vue
  6. 14 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/layout/Page.vue
  7. 15 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js
  8. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useToast.js
  9. 8 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/main.js
  10. 98 16
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue
  11. 92 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue
  12. 85 16
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue
  13. 3 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/style.scss
  14. 22 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js
  15. 5 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/vite.config.js
  16. 9 3
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  17. 1 0
      fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json
  18. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json
  19. 7 8
      fhKeeper/formulahousekeeper/timesheet/src/views/leave/list.vue

BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/fontFile/PingFang Medium.ttf


+ 6 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/fontFile/font.css

@@ -0,0 +1,6 @@
+@font-face {
+  font-family: 'PF';
+  src: url('./PingFang Medium.ttf');
+  font-weight: normal;
+  font-style: normal;
+}

+ 26 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/formForm/formItem.vue

@@ -11,7 +11,9 @@
       :readonly="element.options?.readonly"
       :required="element.options?.rules?.required"
       :rules="[{ validator: characterVerification, message: '' }]"
-      :class="`${element.options?.rules?.required ? '' : 'resetStyles'} resetStyles-right`"
+      :class="`${
+        element.options?.rules?.required ? '' : 'resetStyles'
+      } resetStyles-right`"
     />
   </template>
   <template v-if="element.type === 'number'">
@@ -27,7 +29,9 @@
       :readonly="element.options?.readonly"
       :required="element.options?.rules?.required"
       :rules="[{ validator: characterVerification, message: '' }]"
-      :class="`${element.options?.rules?.required ? '' : 'resetStyles'} resetStyles-right`"
+      :class="`${
+        element.options?.rules?.required ? '' : 'resetStyles'
+      } resetStyles-right`"
     />
   </template>
   <!-- 正常的下拉框 -->
@@ -68,7 +72,7 @@
       :class="element.options?.rules?.required ? '' : 'resetStyles'"
     >
       <template #input v-if="element.options.defaultValue">
-        {{ selectedLabel }}
+        <TranslationComponent :openId="selectedLabel" />
       </template>
     </van-field>
   </template>
@@ -104,7 +108,9 @@
       :readonly="element.options?.readonly"
       :required="element.options?.rules?.required"
       :rules="[{ validator: characterVerification, message: '' }]"
-      :class="`${element.options?.rules?.required ? '' : 'resetStyles'} resetStyles-right`"
+      :class="`${
+        element.options?.rules?.required ? '' : 'resetStyles'
+      } resetStyles-right`"
     />
   </template>
 
@@ -132,6 +138,7 @@
     <PullDownSelector
       :options="element.options.options"
       :multipleChoice="element.options.multiple"
+      :doYouNeedTranslation="distinguishComponents"
       @change="selectChange"
     />
   </van-popup>
@@ -142,6 +149,7 @@ import { ref, reactive, computed, onMounted, defineEmits } from "vue";
 import dayjs from "dayjs";
 import requests from "@common/requests";
 import PullDownSelector from "@components/common/pullDownSelector.vue";
+import useFixedData from "@store/useFixedData";
 import { relatedField } from "./formCorrespondenceProcessing";
 
 const emit = defineEmits(["validateASingleForm", "cascadeProcessing"]);
@@ -152,6 +160,7 @@ const props = defineProps({
   },
 });
 
+const fixedData = useFixedData();
 const dateOfTheDay = dayjs().format("YYYY-MM-DD");
 const distinguishComponents = ref(false);
 const showPicker = ref(false);
@@ -234,14 +243,26 @@ function processingData() {
 // 发起接口请求
 function requestData(str = "") {
   const url = str.replace(/^(\/?api)/, "").trim();
+  if(url.indexOf("getSimpleActiveUserListNew") > -1 && fixedData.allUserData.length) {
+    props.element.options.options = fixedData.allUserData
+    return
+  }
+
   requests.post(url, {}).then(({ data }) => {
-    props.element.options.options = data.map((item) => {
+    const newData = data.map((item) => {
       const { props: setProps } = props.element.options;
       return {
         label: item[setProps.label || "label"],
         value: item[setProps.value || "value"],
       };
     });
+    props.element.options.options = newData
+    if(distinguishComponents.value && fixedData.allUserData.length == 0) {
+      fixedData.updateState({
+        allUserData: [ ...newData ]
+      })
+      return
+    }
   });
 }
 

+ 99 - 65
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/pullDownSelector.vue

@@ -1,106 +1,115 @@
 <template>
   <div class="w-full h-full flex flex-col">
     <div class="w-full pt-2">
-      <van-search
-        v-model.trim="searchForValue"
-        shape="round"
-        placeholder="请输入搜索关键词"
-        @update:model-value="debouncedSearchOptions"
-      />
+      <van-search v-model.trim="searchForValue" shape="round" placeholder="请输入搜索关键词"
+        @update:model-value="debouncedSearchOptions" />
     </div>
     <div class="flex-1 my-2 overflow-y-auto">
-      <template v-if="!multipleChoice">
-        <!-- 单选 -->
-        <van-radio-group v-model="selectChecked">
-          <template v-for="item in renderingOptions">
+      <!-- 加载 -->
+      <template v-if="listLoading">
+        <van-skeleton :row="10" />
+      </template>
+      <template v-else>
+        <template v-if="!multipleChoice">
+          <!-- 单选 -->
+          <van-radio-group v-model="selectChecked">
+            <template v-for="item in renderingOptions">
+              <van-cell-group inset>
+                <van-cell clickable @click="selectChecked = item.value">
+                  <template #right-icon>
+                    <van-radio :name="item.value" />
+                  </template>
+                  <template #title>
+                    <TranslationComponent :openId="item.label" />
+                  </template>
+                </van-cell>
+              </van-cell-group>
+            </template>
+          </van-radio-group>
+        </template>
+        <!-- 多选 -->
+        <template v-if="multipleChoice">
+          <van-checkbox-group v-model="selectChecked">
             <van-cell-group inset>
-              <van-cell clickable @click="selectChecked = item.value">
+              <van-cell v-for="(item, index) in renderingOptions" clickable :key="index" @click="toggle(index)">
                 <template #right-icon>
-                  <van-radio :name="item.value" />
+                  <van-checkbox :name="item.value" :ref="(el) => (checkboxRefs[index] = el)" @click.stop />
                 </template>
                 <template #title>
-                  {{ item.label }}
+                  <TranslationComponent :openId="item.label" />
                 </template>
               </van-cell>
             </van-cell-group>
-          </template>
-        </van-radio-group>
-      </template>
-      <!-- 多选 -->
-      <template v-if="multipleChoice">
-        <van-checkbox-group v-model="selectChecked">
-          <van-cell-group inset>
-            <van-cell
-              v-for="(item, index) in renderingOptions"
-              clickable
-              :key="index"
-              @click="toggle(index)"
-            >
-              <template #right-icon>
-                <van-checkbox
-                  :name="item.value"
-                  :ref="(el) => (checkboxRefs[index] = el)"
-                  @click.stop
-                />
-              </template>
-              <template #title>
-                {{ item.label }}
-              </template>
-            </van-cell>
-          </van-cell-group>
-        </van-checkbox-group>
+          </van-checkbox-group>
+        </template>
       </template>
     </div>
     <div class="w-full pb-2 px-4">
-      <van-button
-        type="primary"
-        round
-        class="w-full"
-        :disabled="!selectChecked"
-        @click="confirmClick"
-        >确定</van-button
-      >
+      <van-button type="primary" round class="w-full" :disabled="multipleChoice ? !selectChecked.length : !selectChecked" @click="confirmClick">确定</van-button>
     </div>
   </div>
 </template>
 
 <script setup>
 import { ref, onBeforeUpdate, reactive, watch, onMounted } from "vue";
-import { manualCopying, useDebounce } from "@hooks/useCommon"
+import { manualCopying, useDebounce } from "@hooks/useCommon";
+import requests from "@common/requests";
+import { GET_ALL_PERSONNEL } from "@hooks/useApi";
+import useFixedData from "@store/useFixedData";
 
 const props = defineProps({
   options: {
     type: Array,
-    required: true,
     default: () => [],
   },
   value: {
     type: [String, Array],
-    default: () => null
+    default: () => null,
   },
-  multipleChoice: {
+  doYouNeedTranslation: {
     type: Boolean,
     default: () => true,
   },
+  multipleChoice: {
+    type: Boolean,
+    default: () => false,
+  },
 });
 
-const emit = defineEmits(['change'])
+const emit = defineEmits(["change"]);
+const fixedData = useFixedData();
 
+const listLoading = ref(false);
 const selectChecked = ref();
 const checkboxRefs = ref([]);
 const searchForValue = ref("");
-const allOptions = ref([])
-const renderingOptions = ref([])
+const allOptions = ref([]);
+const renderingOptions = ref([]);
 
 const debouncedSearchOptions = useDebounce(searchOptions, 500);
 
 function searchOptions(val) {
-  if(!val) {
-    renderingOptions.value = manualCopying(allOptions.value)
+  listLoading.value = true
+  if (!props.doYouNeedTranslation) {
+    setTimeout(() => {
+      listLoading.value = false
+    }, 200)
+    if (!val) {
+      renderingOptions.value = manualCopying(allOptions.value);
+      return;
+    }
+    const list = manualCopying(allOptions.value);
+    renderingOptions.value = list.filter((item) => item.label.indexOf(val) > -1);
     return
   }
-  const list = manualCopying(allOptions.value)
-  renderingOptions.value = list.filter(item => item.label.indexOf(val) > -1)
+
+  // 转译人员搜索
+  requests.post(GET_ALL_PERSONNEL, { keyword: val }).then((res) => {
+    const { data } = res
+    renderingOptions.value = [ ...data ]
+  }).finally(() => {
+    listLoading.value = false
+  })
 }
 
 function toggle(index) {
@@ -108,15 +117,35 @@ function toggle(index) {
 }
 
 function valueTaking(val) {
-  if(Array.isArray(val)) {
-    return allOptions.value.filter(item => val.includes(item.value)).map(item => item.label)
+  if (Array.isArray(val)) {
+    return allOptions.value
+      .filter((item) => val.includes(item.value))
+      .map((item) => item.label);
   } else {
-    return allOptions.value.find(item => item.value === val)?.label
+    return allOptions.value.find((item) => item.value === val)?.label;
+  }
+}
+
+function obtainPersonnelData() {
+  if (fixedData.allUserData && fixedData.allUserData.length) {
+    renderingOptions.value = [...fixedData.allUserData];
+    allOptions.value = [...fixedData.allUserData];
+    return;
   }
+  listLoading.value = true;
+  requests.post(GET_ALL_PERSONNEL, {}).then(({ data = [] }) => {
+    renderingOptions.value = [...data];
+    allOptions.value = [...data];
+    fixedData.updateState({
+      allUserData: [...data]
+    })
+  }).finally(() => {
+    listLoading.value = false;
+  })
 }
 
 function confirmClick() {
-  emit('change', selectChecked.value, valueTaking(selectChecked.value))
+  emit("change", selectChecked.value, valueTaking(selectChecked.value));
 }
 
 onBeforeUpdate(() => {
@@ -124,8 +153,13 @@ onBeforeUpdate(() => {
 });
 
 onMounted(() => {
-  selectChecked.value = props.multipleChoice ? [] : "";
-  renderingOptions.value = manualCopying(props.options)
-  allOptions.value = manualCopying(props.options)
+  selectChecked.value = props.multipleChoice ? [] : null;
+  const isItAnArray = Array.isArray(props.options);
+  if (isItAnArray && props.options.length > 0) {
+    renderingOptions.value = manualCopying(props.options);
+    allOptions.value = manualCopying(props.options);
+  } else {
+    obtainPersonnelData();
+  }
 });
 </script>

+ 27 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/translationComponent.vue

@@ -1,17 +1,40 @@
 <template>
-  <Page title='首页'>
-    <template v-slot:body>
+  <template v-if="userInfo.userNameNeedTranslate == 1">
+    <template v-if="Array.isArray(openId)">
+      <template v-for="(item, index) in openId">
+        <ww-open-data :type='type' :openid='item'></ww-open-data>
+        <span v-if="index < openId.length - 1">,</span>
+      </template>
     </template>
-  </Page>
+    <ww-open-data :type='type' :openid='openId' v-else></ww-open-data>
+  </template>
+  <template v-else>
+    {{ Array.isArray(openId) ? openId.join(',') : openId }} 😔
+  </template>
 </template>
 
 <script setup>
 import { ref } from 'vue';
-import { useLifecycle } from '@hooks/useCommon.js';
+import { useLifecycle, useEnv } from '@hooks/useCommon.js';
+import useInfoStore from '@store/useInfoStore'
+
+const props = defineProps({
+  openId: {
+    type: [String, Number],
+    default: () => '',
+  },
+  type: {
+    type: String,
+    default: () => 'userName',
+  }
+});
+const userInfo = useInfoStore()
+
 
 useLifecycle({
   load: () => {
     // 添加加载逻辑
+    console.log(useEnv(), '<==== useEnv')
   }
 });
 </script>

+ 14 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/layout/Page.vue

@@ -1,5 +1,5 @@
 <template>
-    <div :class="`page ${!props.title? 'page-no-title':''}`">
+    <div :class="`page ${!props.title? 'page-no-title':''} ${styleReset}`">
         <van-nav-bar class="header"
                      v-bind:title="props.title"
                      v-if="showHeader"
@@ -50,6 +50,13 @@ const props = defineProps({
         type: [String, Number],
         default: 44
     },
+    /**
+     * @property headerHeight {String} 外盒子样式重置
+     * */
+    styleReset: {
+      type: String,
+      default: ''
+    },
     /**
      * @property title {String} 页面顶部标题
      * */
@@ -107,4 +114,10 @@ const goBack = ()=>{
 :deep(.van-nav-bar__content) {
   height: 100% !important;
 }
+
+.headerClass {
+  :deep(.van-nav-bar__content) {
+    background: $themeColor;
+  }
+}
 </style>

+ 15 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js

@@ -2,6 +2,7 @@ export const LOGIN_INTERFACE = `/user/loginAdmin` // 登录接口
 export const USER_ID_LOGIN = `/user/loginByUserId` // userId 登录
 export const WE_CHAT_LOGIN = `/wxcorp/corpWeiXinLogin` // 微信登录
 export const GET_CUSTOM_FORM_JSON = `/sys-form/getListByCode` // 获取自定义表单json
+export const GET_ALL_PERSONNEL = `/user/getSimpleActiveUserListNew` // 获取所有人员
 
 export const GET_A_LIST_OF_BUSINESS_OPPORTUNITIES = '/business-opportunity/list' // 获取商机列表
 export const GET_A_LIST_OF_CLUES = '/clue/listClue' // 获取线索列表
@@ -10,4 +11,17 @@ export const GET_CUSTOMER_LIST = '/custom/list' // 获取客户列表
 export const GET_TASK_LIST = '/tasks/pageTask' // 获取任务列表
 export const GET_PRODUCT_LIST = '/product/list' // 获取产品列表
 export const GET_CONTRACT_LIST = '/contract/getContractPage' // 获取合同列表
-export const GET_SALES_ORDER_LIST = '/order/list' // 获取销售订单列表
+export const GET_SALES_ORDER_LIST = '/order/list' // 获取销售订单列表
+
+export const DELETE_BUSINESS_OPPORTUNITY = '/business-opportunity/delete' // 删除商机
+export const DELETE_CLUES = '/clue/delete' // 删除线索
+export const DELETE_CUSTOMER = '/custom/deleter' // 删除客户
+export const DELETE_CONTACTS = '/contacts/deleteContacts' // 删除联系人
+export const DELETE_TASK = '/tasks/deleteTasks' // 删除任务
+export const DELETE_PRODUCT = '/product/delete' // 删除产品
+export const DELETE_CONTRACT = '/contract/deleteContract' // 删除合同
+export const DELETE_ORDER = '/order/delete' // 删除订单
+
+export const BUSINESS_OPPORTUNITY_TRANSFER = '/business-opportunity/claim' // 转移商机
+export const TRANSFER_CLUES = '/clue/claim' // 转移线索
+export const TRANSFER_CUSTOMERS = '/custom/claim' // 转移客户

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useToast.js

@@ -1,5 +1,5 @@
 import 'vant/es/notify/style';
-import { Toast, showLoadingToast, showSuccessToast, showFailToast, closeToast } from "vant";
+import { Toast, showToast, showLoadingToast, showSuccessToast, showFailToast, closeToast } from "vant";
 
 /**
  * 文字:Toast 提示组件

+ 8 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/main.js

@@ -2,17 +2,24 @@ import { createApp } from "vue";
 import App from "@/App.vue";
 import router from "@/router.js";
 import pinia from "@store/pinia.js";
-import Page from "@components/layout/Page.vue";
 import customize from "@utility/customInstructions.js"
 
+// 引入页面
+import Page from "@components/layout/Page.vue";
+import TranslationComponent from "@components/common/translationComponent.vue"
+
 // 引入样式
 import "@/style.scss";
 import "@/assets/tailwind.css"
 import "vant/es/image-preview/style";
 import "vant/es/toast/style";
+import 'vant/lib/index.css'
 
 const app = createApp(App);
 
+// 注册全局转译组件
+app.component('TranslationComponent', TranslationComponent);
+
 // 注册自定义指令
 for (const [key, value] of Object.entries(customize)) {
    app.directive(value.key, value.directive)

+ 98 - 16
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue

@@ -1,10 +1,11 @@
 <template>
-  <Page :title='`${queryParameters?.name}列表`'>
+  <Page :title='`${queryParameters.name}列表`'>
     <template v-slot:body>
       <div class="flex flex-col h-full">
         <!-- 搜索 -->
-        <div class="mx-1">
-          <van-search v-model="searchVal" background="#F8F8F8" :placeholder="`请输入${queryParameters?.name}关键词`">
+        <div class="mx-1 headerModeuleList">
+          <van-search v-model.trim="searchVal" background="#F8F8F8" :placeholder="`请输入${queryParameters?.name}关键词`"
+            @search="onRefresh(true)" @clear="onRefresh(true)">
             <template v-slot:left-icon>
               <van-icon name="search" class="themeTextColor font-bold" />
             </template>
@@ -12,7 +13,7 @@
         </div>
         <!-- 主题内容 -->
         <div class="flex-1 overflow-y-auto">
-          <template v-if="listData?.records && listData.records.length">
+          <template v-if="listData?.records && listData.records.length && !loadingList">
             <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
               <van-list v-model:loading="isLoading" :finished="finished" finished-text="没有更多了" @load="onLoad">
                 <div v-for="item in listData.records" :key="item.id" @click="toDetail(item)">
@@ -55,28 +56,50 @@
               </van-list>
             </van-pull-refresh>
           </template>
+          <template v-else>
+            <van-skeleton title :row="20" class="w-full h-full" />
+          </template>
         </div>
       </div>
 
       <!-- 可拖拽添加 -->
       <DragBox>
-        <div class="addButton backgroundThemeColor rounded-full flex items-center justify-center text-white" @click="toAddEditor">
+        <div class="addButton backgroundThemeColor rounded-full flex items-center justify-center text-white"
+          @click="toAddEditor()">
           添加
         </div>
       </DragBox>
+
+      <!-- 转移弹窗 -->
+      <van-dialog v-model:show="showDialog" :title="`转移${queryParameters?.name || ''}`" show-cancel-button
+        @confirm="confirmTransfer" :before-close="dialogCloseBefo">
+        <van-cell title="转移至" is-link @click="showSelect = true">
+          <template #value>
+            {{ dialogSelection.label }}
+          </template>
+        </van-cell>
+        <div class="themeTextColor text-size-small pl-4 pt-2 pb-2">转移后,将看不到此{{ queryParameters?.name || '' }}</div>
+      </van-dialog>
+
+      <!-- select 选择器 -->
+      <van-popup v-model:show="showSelect" destroy-on-close position="bottom" :style="{ height: '80%' }">
+        <PullDownSelector @change="selectChange" />
+      </van-popup>
     </template>
   </Page>
 </template>
 
 <script setup>
 import { ref, onActivated } from 'vue';
+import { showConfirmDialog } from 'vant';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { resetListData, getListFieldKey } from '@components/common/formForm/formCorrespondenceProcessing'
+import useShowToast from '@hooks/useToast'
 import { GET_CUSTOM_FORM_JSON } from '@hooks/useApi'
 import requests from "@common/requests";
 import useRouterStore from "@store/useRouterStore.js";
 import useFixedData from "@store/useFixedData.js"
-import ElementLongPress from "@components/common/elementLongPress.vue";
+// import ElementLongPress from "@components/common/elementLongPress.vue";
 import DragBox from '@components/common/dragBox.vue';
 
 const TRANSFER = 'transfer';
@@ -84,16 +107,20 @@ const DELETE = 'delete';
 const EDIT = 'edit';
 const TOP_MOUNTED = 'topMounted';
 
+const { toastSuccess, toastFail, toastText } = useShowToast()
 const router = useRouterStore()
 const fixedData = useFixedData()
 const searchVal = ref()
-const queryParameters = ref()
+const queryParameters = ref({})
+const loadingList = ref(false)
 const popUpWindowArray = ref([
   { text: '转移', event: TRANSFER, bg: '#FFA359', removeModule: ['contacts', 'tasks', 'product', 'contract', 'order'] },
   { text: '顶置', event: TOP_MOUNTED, bg: '#075985', removeModule: [] },
   { text: '编辑', event: EDIT, bg: '#07C160', removeModule: [] },
   { text: '删除', event: DELETE, bg: '#FF6A6A', removeModule: [] },
 ])
+const showSelect = ref(false)
+const showDialog = ref(false)
 const refreshing = ref(false);
 const isLoading = ref(false);
 const finished = ref(false);
@@ -104,6 +131,8 @@ const listData = ref({
   total: 0,
   totalPage: 0,
 });
+const excessiveData = ref({});
+const dialogSelection = ref({});
 
 // 按钮触发的事件: row 为当前点击的行数据, item 为当前点击的按钮
 function longPress(row, item) {
@@ -136,12 +165,38 @@ function edit(row) {
 
 // 转移事件
 function transfer(row) {
-  console.log(row, '<======= 转移事件')
+  dialogSelection.value = {}
+  excessiveData.value = row
+  showDialog.value = true
+}
+
+function confirmTransfer() {
+  if(!dialogSelection.value.label) {
+    return toastText('请选择要转移的人员')
+  }
+  const { id } = excessiveData.value
+  const { value } = dialogSelection.value
+  requests.post(queryParameters?.value.transferInterface, { ids: id, inchargerId: value }).then((res) => {
+    toastSuccess('转移成功')
+    onRefresh(true)
+    showDialog.value = false
+  })
 }
 
 // 删除事件
 function deleteRow(row) {
-  console.log(row, '<======= 删除事件')
+  const { name = '', searchFiled = {}, deteleFiled = '' } = queryParameters.value
+  showConfirmDialog({
+    title: `删除${name}`,
+    message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
+  }).then(() => {
+    requests.post(deteleFiled, { ids: row.id }).then((res) => {
+      toastSuccess('删除成功')
+      onRefresh(true)
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
 }
 
 // 顶置事件
@@ -173,11 +228,11 @@ function toAddEditor(value) {
   })
 }
 
-function onRefresh() {
+function onRefresh(flag = false) {
   finished.value = false;
   isLoading.value = true;
   listData.value.pageIndex = 1;
-  fetchListData();
+  fetchListData(flag);
 }
 
 function onLoad() {
@@ -186,7 +241,7 @@ function onLoad() {
   fetchListData()
 }
 
-async function fetchListData() {
+async function fetchListData(flag = false) {
   console.log(listData.value.totalPage, listData.value.pageIndex)
   if (
     // 如果总页数小于等于现页数,并且不是第一次加载, 或者正在加载数据 直接跳出不请求
@@ -196,7 +251,7 @@ async function fetchListData() {
     finished.value = true;
     return;
   }
-  const res = await getListData()
+  const res = await getListData(flag)
   if (res.code === 'ok') {
     const list = res.data.data || res.data.records || res.data.record
     const total = res.data.total
@@ -214,12 +269,20 @@ async function fetchListData() {
   }
 }
 
-async function getListData() {
+async function getListData(flag = false) {
   const url = queryParameters.value.listUrl
+  if (flag) {
+    loadingList.value = true
+  }
   const res = await requests.post(url, {
     pageIndex: listData.value.pageIndex,
     pageSize: listData.value.pageSize,
-    pageFrom: listData.value.pageSize
+    pageFrom: listData.value.pageSize,
+    [queryParameters.value?.searchFiled?.search]: searchVal.value
+  }).finally(() => {
+    if (flag) {
+      loadingList.value = false
+    }
   })
   return res
 }
@@ -249,6 +312,21 @@ function getFormJson(info = {}) {
   })
 }
 
+function selectChange(value, label) {
+  dialogSelection.value = {
+    value, label
+  }
+  showSelect.value = false
+}
+
+function dialogCloseBefo(val) {
+  if(val == 'confirm' && showDialog.value) {
+    return false
+  }
+
+  return true
+}
+
 useLifecycle({
   load: () => {
     router.on('moduleListDetailParameter', (data) => {
@@ -265,6 +343,10 @@ useLifecycle({
   width: 37px;
   height: 37px;
   margin-left: 10px;
+
+  &:last-child {
+    margin-right: 10px;
+  }
 }
 
 .addButton {
@@ -283,7 +365,7 @@ useLifecycle({
   }
 }
 
-::v-deep .van-search__content {
+.headerModeuleList  ::v-deep .van-search__content {
   background: #fff !im\portant;
   height: 42px;
   align-items: center;

+ 92 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue

@@ -1,7 +1,55 @@
 <template>
-  <div>
-    <div class="p-[10px]" v-for="item in 30">
-      工作台
+  <div class="w-full h-full workbench">
+    <!-- 日历 -->
+    <div class="w-full p16 backgroundThemeColor rounded-b-lg">
+      <van-calendar :show-title="false" :show-subtitle="false" :show-mark="false" :poppable="false" :show-confirm="false" :row-height="'2.5rem'"
+        :style="{ height: '45.174vh', borderRadius: '2vw' }" :min-date="minDate" />
+    </div>
+
+    <!-- 日程安排 -->
+    <div class="h-52 overflow-y-auto mt-5">
+      <!-- 有数据的情况 -->
+      <div></div>
+      <!-- 没有数据的情况下 -->
+      <div class="w-full h-full flex flex-col items-center justify-center">
+        <div class="schedulePicture bg-black mb-5"></div>
+        <div class="text-center text-[#C4C4C4] mb-5">您今天还没安排日程哦!</div>
+        <van-button type="primary" class="m-auto w-3/5">马上安排</van-button>
+      </div>
+    </div>
+
+    <!-- 常用表单 -->
+    <div class="mt-5">
+      <div class="text-size-large text-[#000] pl16">常用表单</div>
+      <div class="p16 pt-0 pb-0 flex justify-between overflow-x-auto">
+        <div class="flex">
+          <template v-for="(item, index) in 10">
+            <div class="w80 bg-[#FFA359] h-28 rounded-md flex flex-col items-center justify-center">
+              <div class="formImage"></div>
+              <div class="text-white">表单{{ index }}</div>
+            </div>
+          </template>
+          
+          <div class="w80 bg-[#357AF4] h-28 rounded-md flex flex-col items-center justify-center">
+            <div class="formImage"></div>
+            <div class="text-white">更多</div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 常用联系人 -->
+    <div class="mt-3">
+      <div class="text-size-large text-[#000] pl16">常用联系人</div>
+      <div class="p16 pt-0 pb-0">
+        <template v-for="item in 10">
+          <div class="flex flex-row items-center rounded-md p-4 bg-white mb-5">
+            <div class="contactImage"></div>
+            <div class="flex-1">张三</div>
+            <div class="rightArrow"></div>
+          </div>
+        </template>
+      </div>
     </div>
   </div>
 </template>
@@ -9,6 +57,9 @@
 <script setup>
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import dayjs from 'dayjs';
+
+const minDate = ref(new Date('2024-01-01'))
 
 useLifecycle({
   load: () => {
@@ -18,5 +69,42 @@ useLifecycle({
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+.p16 {
+  padding: 16px;
+}
+
+.pl16 {
+  padding-left: 16px;
+}
+
+.w80 {
+  width: 80px;
+  margin-right: 12px;
+}
+
+.formImage {
+  width: 24px;
+  height: 24px;
+  margin-bottom: 12px;
+  background: #000;
+}
+
+.contactImage {
+  width: 29px;
+  height: 29px;
+  border-radius: 50%;
+  background: #000;
+  margin-right: 12px;
+}
+
+.rightArrow {
+  width: 24px;
+  height: 27px;
+  background: #000;
+}
+
+.schedulePicture {
+  width: 48px;
+  height: 51px;
+}
 </style>

+ 85 - 16
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue

@@ -1,27 +1,48 @@
 <template>
-  <Page :title="'首页'">
+  <Page styleReset="headerClass">
+    <template v-slot:headerLeft>
+      <div class="text-white">客户管家</div>
+    </template>
     <template v-slot:headerRight>
-      <div @click="showModule = true">新建</div>
+      <div class="newButton" @click="showModule = true">新建</div>
     </template>
 
     <template v-slot:body>
-      <van-tabs v-model:active="homepageType" class="w-full h-full flex flex-col">
-        <van-tab title="工作台" name="workbench" class="w-full h-full">
-          <Workbench />
-        </van-tab>
-        <van-tab title="数据分析" name="dataAnalysis" class="w-full h-full">
-          <DataAnalysis />
-        </van-tab>
-      </van-tabs>
+      <div class="w-full h-full flex flex-col overflow-hidden">
+        <!-- 头部 -->
+        <div class="custom-tabs rounded-b-lg z-10">
+          <div class="custom-tabs-box text-size-in custom-tabs-rad flex items-center overflow-hidden">
+            <div :class="`w-1/2 h-full rounded-r-lg items-justify-center ${homepageType == 'workbench' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'workbench'">工作台</div>
+            <div :class="`w-1/2 h-full rounded-l-lg items-justify-center ${homepageType == 'dataAnalysis' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'dataAnalysis'">数据分析</div>
+          </div>
+        </div>
+        <!-- 内容 -->
+        <div class="flex-1 overflow-y-auto relative aopiuyt">
+          <template v-if="homepageType == 'workbench'">
+            <Workbench />
+          </template>
+          <template v-if="homepageType == 'dataAnalysis'">
+            <DataAnalysis />
+          </template>
+        </div>
+      </div>
+      
+
 
       <!-- 显示对应的模块 -->
-      <van-overlay :show="showModule" class="flex items-center" z-index="100"  @click="showModule = false">
-        <div class="w-3/4 h-3/4 m-auto flex flex-wrap items-center" @click.stop>
-          <div class="text-white w-1/2 text-center" v-for="(item) in moduleList" :key="item.id" @click.stop="toAddEditor(item)">
-            {{ item.name }}
+      <van-popup v-model:show="showModule" closeable position="bottom" round>
+        <div class="newModuleAdded relative">
+          <div class="text-size-large text-[#474A56] absolute topTitle">客户管家</div>
+          <div class="flex flex-wrap">
+            <template v-for="(item) in moduleList" :key="item.id">
+              <div class="w-16 flex flex-col items-center mrSpacing" @click.stop="toAddEditor(item)">
+                <div class="newModuleImage"></div>
+                <div class="mt-3 text-[#474A56]">{{ item.name }}</div>
+              </div>
+            </template>
           </div>
         </div>
-      </van-overlay>
+      </van-popup>
     </template>
 
     <template v-slot:footer>
@@ -61,7 +82,7 @@ function toAddEditor(rows) {
 
 useLifecycle({
   load: () => {
-    
+
   }
 });
 
@@ -73,4 +94,52 @@ useLifecycle({
   flex: 1;
   overflow-y: auto;
 }
+::v-deep .van-tabs__wrap {
+  background-color: $themeColor;
+}
+.newButton {
+  width: 86px;
+  line-height: 30px;
+  background: #fff;
+  color: $themeColor;
+  border-radius: 75px;
+}
+ 
+.custom-tabs {
+  background: $themeColor;
+  padding: 22px 15px;
+  color: #fff;
+}
+
+.custom-tabs-box {
+  width: 100%;
+  border: 1.5px solid #fff; 
+  height: 50px;
+}
+.custom-tabs-rad {
+  border-radius: 6px;
+}
+.aopiuyt {
+  margin-top: -6px;
+}
+.newModuleAdded {
+  padding: 45px 25px 45px 25px;
+}
+
+.newModuleImage {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+  background: #000;
+}
+.mrSpacing {
+  margin-top: 16px;
+  margin-right: 33px;
+  &:nth-child(4n) {
+    margin-right: 0;
+  }
+}
+.topTitle {
+  top: 14px;
+}
 </style>

+ 3 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/style.scss

@@ -1,5 +1,5 @@
 @import "./assets/font/iconfont.css";
-
+@import "./assets/fontFile/font.css";
 
 html,body {
   width: 100%;
@@ -7,6 +7,8 @@ html,body {
   background: #F8F8F8;
   overflow: hidden;
   font-size: 14px;
+  font-family: Helvetica Neue, Helvetica,PF, PingFang SC, Hiragino Sans GB, Microsoft YaHei,
+  '\5FAE\8F6F\96C5\9ED1', Arial, sans-serif !important;
 }
 
 #app,.van-config{

+ 22 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js

@@ -1,4 +1,4 @@
-import { GET_A_LIST_OF_BUSINESS_OPPORTUNITIES, GET_A_LIST_OF_CLUES, GET_CONTACT_LIST, GET_CUSTOMER_LIST, GET_TASK_LIST, GET_CONTRACT_LIST, GET_SALES_ORDER_LIST, GET_PRODUCT_LIST } from '@hooks/useApi'
+import { GET_A_LIST_OF_BUSINESS_OPPORTUNITIES, GET_A_LIST_OF_CLUES, GET_CONTACT_LIST, GET_CUSTOMER_LIST, GET_TASK_LIST, GET_CONTRACT_LIST, GET_SALES_ORDER_LIST, GET_PRODUCT_LIST, DELETE_BUSINESS_OPPORTUNITY, DELETE_CLUES, DELETE_CUSTOMER, DELETE_CONTACTS, DELETE_TASK, DELETE_PRODUCT, DELETE_CONTRACT, DELETE_ORDER, BUSINESS_OPPORTUNITY_TRANSFER, TRANSFER_CLUES, TRANSFER_CUSTOMERS } from '@hooks/useApi'
 
 export const routingInfos = {
   'business': {
@@ -6,6 +6,9 @@ export const routingInfos = {
     key: 'business', // 唯一标识
     icon: 'icon-shangpin', // 图标
     listUrl: GET_A_LIST_OF_BUSINESS_OPPORTUNITIES, // 列表请求接口
+    deteleFiled: DELETE_BUSINESS_OPPORTUNITY, // 删除请求接口
+    transferInterface: BUSINESS_OPPORTUNITY_TRANSFER, // 转移请求接口
+    searchFiled: { search: 'name' }, // 搜索字段
     image: '', // 图片
   },
   'thread': {
@@ -13,6 +16,9 @@ export const routingInfos = {
     key: 'thread', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_A_LIST_OF_CLUES,
+    deteleUrl: DELETE_CLUES,
+    transferInterface: TRANSFER_CLUES,
+    searchFiled: { search: 'clueName' },
     image: '',
   },
   'customer': {
@@ -20,6 +26,9 @@ export const routingInfos = {
     key: 'customer', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_CUSTOMER_LIST,
+    deteleFiled: DELETE_CUSTOMER,
+    transferInterface: TRANSFER_CUSTOMERS,
+    searchFiled: { search: 'customName' },
     image: '',
   },
   'contacts': {
@@ -27,6 +36,8 @@ export const routingInfos = {
     key: 'contacts', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_CONTACT_LIST,
+    deteleFiled: DELETE_CONTACTS,
+    searchFiled: { search: 'name' },
     image: '',
   },
   'tasks': {
@@ -34,20 +45,26 @@ export const routingInfos = {
     key: 'tasks', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_TASK_LIST,
+    deteleFiled: DELETE_TASK,
+    searchFiled: { search: 'taskName' },
     image: '',
   },
   'product': {
-    name: '产品管理',
+    name: '产品',
     key: 'product', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_PRODUCT_LIST,
+    deteleFiled: DELETE_PRODUCT,
+    searchFiled: { search: 'productName' },
     image: '',
   },
   'contract': {
-    name: '合同管理',
+    name: '合同',
     key: 'contract', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_CONTRACT_LIST,
+    deteleFiled: DELETE_CONTRACT,
+    searchFiled: { search: 'name' },
     image: '',
   },
   'order': {
@@ -55,6 +72,8 @@ export const routingInfos = {
     key: 'order', // 唯一标识
     icon: 'icon-shangpin',
     listUrl: GET_SALES_ORDER_LIST,
+    deteleFiled: DELETE_ORDER,
+    searchFiled: { search: 'orderName' },
     image: '',
   }
 }

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/vite.config.js

@@ -44,6 +44,11 @@ export default defineConfig({
   build: {
     chunkSizeWarningLimit: 1600,
   },
+  vue: {
+    compilerOptions: {
+      isCustomElement: (tag) => tag == 'ww-open-data'  // 排除 ww-open-data
+    }
+  },
   resolve: {
     alias: [
       { find: "@", replacement: path.resolve(__dirname, "src") },

+ 9 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

@@ -1583,10 +1583,16 @@
                 </where>
             )tmp1
                 left join (
-                select creator_id userId,IFNULL(sum(overtime_hours),0) as overTimeHour
+                select report.creator_id userId,IFNULL(sum(overtime_hours),0) as overTimeHour
                 from report
-                where company_id = #{companyId}
-                group by creator_id
+                LEFT JOIN report_write_off rwo ON rwo.`report_id`=report.id
+                <where>
+                    company_id = #{companyId} AND (rwo.`write_off` IS NULL OR rwo.`write_off`=0)
+                    <if test="userId != null and userId !='' ">
+                        and report.creator_id=#{userId}
+                    </if>
+                </where>
+                group by report.creator_id
             ) tmp2 on tmp1.id = tmp2.userId
     </select>
 

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json

@@ -770,6 +770,7 @@
   "annualleavemanagement": "Annual leave management",
   "for": "leave statistics",
   "leaveApprovalProcess": "Leave approval process",
+  "restTime":"Rest time list",
   "myleaveform": "my leave form",
   "scheduleofremainingLeave": "Holiday Remaining Table",
   "singlelistofleave": "List of leave requests",

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json

@@ -773,7 +773,7 @@
   "myleaveform": "我的请假单",
   "for": "请假统计",
   "leaveApprovalProcess": "请假审批流程",
-  "restTime":"调休统计",
+  "restTime":"调休额度列表",
   "annualleavemanagement": "年假管理",
   "scheduleofremainingLeave": "假期剩余表",
   "aketimeoffto": "请假填报",

+ 7 - 8
fhKeeper/formulahousekeeper/timesheet/src/views/leave/list.vue

@@ -49,6 +49,13 @@
               </template>
           </el-menu-item>
 
+          <el-menu-item index="9" v-if="permissions.leaveStatistical">
+              <template slot="title">
+                <i class="iconfont firerock-icongongshitongji"></i>
+                <span slot="title">{{ $t('restTime') }}</span>
+              </template>
+          </el-menu-item>
+
           <el-menu-item index="5" v-if="permissions.leaveProcess">
               <template slot="title">
                 <i class="iconfont firerock-iconliucheng"></i>
@@ -80,14 +87,6 @@
             </el-menu-item>
           </template>
 
-          <el-menu-item index="9" v-if="permissions.leaveStatistical">
-              <template slot="title">
-                <i class="iconfont firerock-icongongshitongji"></i>
-                <span slot="title">{{ $t('restTime') }}</span>
-              </template>
-          </el-menu-item>
-
-
           </el-menu>
       </el-col>
     </div>