Преглед изворни кода

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper

zhouyy пре 1 месец
родитељ
комит
3ba7a7eaa4
51 измењених фајлова са 2330 додато и 1032 уклоњено
  1. 6 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/login.vue
  2. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue
  3. 3 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue
  4. 54 50
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/addEditor.vue
  5. 20 6
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/detail.vue
  6. 33 6
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/salesBriefings.vue
  7. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue
  8. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue
  9. 34 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/my/index.vue
  10. 2 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/corpWXparam.js
  11. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/vite.config.js
  12. 4 1
      fhKeeper/formulahousekeeper/customerBuler-crm/package.json
  13. 30 25
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/index.vue
  14. 23 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/element-plus.d.ts
  15. 33 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/api.ts
  16. 449 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/components/AIChat.vue
  17. 275 258
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/index.vue
  18. 77 42
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  19. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/kanbanView.vue
  20. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  21. 4 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/deteleTables.vue
  22. 6 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue
  23. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue
  24. 24 9
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/index.vue
  25. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/index.vue
  26. 21 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/aitest.vue
  27. 5 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts
  28. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/router/routerGuards.ts
  29. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts
  30. 6 1
      fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json
  31. 5 0
      fhKeeper/formulahousekeeper/management-crm/pom.xml
  32. 15 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CompanyController.java
  33. 43 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SysFormController.java
  34. 6 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Company.java
  35. 54 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Task.java
  36. 4 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/dto/TaskDto.java
  37. 8 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/TasKVo.java
  38. 6 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/AIQuestionServiceImpl.java
  39. 13 17
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java
  40. 926 537
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/TaskServiceImpl.java
  41. 23 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  42. 52 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/VisitPlanServiceImpl.java
  43. 0 7
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/task/TimingTask.java
  44. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/util/CodeGenerator.java
  45. 2 5
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/application.yml
  46. 3 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CompanyMapper.xml
  47. 12 3
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskMapper.xml
  48. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  49. 26 23
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  50. 3 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  51. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserCorpwxTimeMapper.xml

+ 6 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/login.vue

@@ -38,8 +38,10 @@ const { toastLoading, toastSuccess, toastFail } = useShowToast();
 
 const router = useRouterStore()
 const userInfo = useInfoStore()
-const username = ref("18122222222");
-const password = ref("000000");
+// const username = ref("18122222222");
+// const password = ref("000000");
+const username = ref("");
+const password = ref("");
 const rules = ref([{ required: true }]);
 
 const isCorpWX = ref(false)
@@ -67,7 +69,7 @@ function verifyLoginEnvironment() {
     loginByUserId(loginUserId);
   } else {
     // 判断是否为企业微信授权
-    if (isCorpWX.value) {
+    if (isCorpWX.value || isWX.value) {
       // 判断企业微信,是否存在授权
       if (href.includes("com/?code")) {
         bindIfNessary()
@@ -111,7 +113,7 @@ function tryAutoLogin() {
 
 function bindIfNessary() {
   const href = window.location.href;
-  const requestUrl = isCorpWX.value ? '/wxcorp/bindCorpWeiXin' : isWX ? '/wechat/bindWeiXin' : '';
+  const requestUrl = isCorpWX.value ? '/wxcorp/bindCorpWeiXin' : isWX.value ? '/wechat/bindWeiXin' : '';
   if (requestUrl.length > 0) {
     //url包括 com/?code 证明为从微信跳转回来的
     if (href.includes("com/?code")) {

+ 2 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue

@@ -99,7 +99,7 @@
                               <div class="text-[#B9B9B9] text-size-small w-2/5 truncate">
                                 {{ item.businessName || item.orderName || item.clueName || item.customName }}
                               </div>
-                              <div class="text-[#B9B9B9] text-size-small w-2/5 truncate text-right">优先级:
+                              <div class="text-[#B9B9B9] text-size-small w-2/5 truncate text-right" v-if="user.company.isSimple != 1">优先级:
                                 {{ fixedFieldPriority.find(subItem => subItem.value == item.priority)?.label || '' }}
                               </div>
                             </div>
@@ -255,6 +255,7 @@ const { toastSuccess, toastFail, toastText } = useShowToast()
 const router = useRouterStore()
 const fixedData = useFixedData()
 const userInfo = useInfoStore()
+const user = userInfo.userInfo
 const searchVal = ref()
 const queryParameters = ref({})
 const loadingList = ref(false)

+ 3 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue

@@ -3,7 +3,7 @@
     <div class="bg-white info flex-1 overflow-y-auto cellnormall">
       <van-cell :title="`${businessLabel}名称`" :value="infoData.name" />
       <van-cell title="客户名称" :value="infoData.customerName" />
-      <van-cell title="联系人姓名" :value="infoData.contactsName" />
+      <van-cell title="联系人姓名" :value="infoData.contactsName" v-if="user.company.isSimple != 1" />
       <van-cell :title="`${businessLabel}金额`">
         <template #default>
           <span class="text-[#FF8B32]" v-if="infoData.amountOfMoney">¥ {{ infoData.amountOfMoney }}</span>
@@ -19,7 +19,7 @@
       <van-cell title="备注" :value="infoData.remark" />
     </div>
     <div class="bottomButton">
-      <van-button type="primary" class="w-full block" v-if="!infoData.contactsName"
+      <van-button type="primary" class="w-full block" v-if="!infoData.contactsName && user.company.isSimple != 1"
         @click="shoContactDialag()">关联联系人</van-button>
       <van-button type="warning" class="w-full block" v-if="infoData.inchargerName"
         @click="showDialogCli()">转移{{ businessLabel }}</van-button>
@@ -76,6 +76,7 @@ import { routingInfos } from "@utility/generalVariables"
 const router = useRouterStore()
 const fixedData = useFixedData()
 const userInfo = useInfoStore()
+const user = userInfo.userInfo
 const { toastSuccess, toastLoading, toastFail, toastText, clearToast } = useShowToast()
 const props = defineProps({
   info: {

+ 54 - 50
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/addEditor.vue

@@ -5,63 +5,65 @@
         class="bg-white" @submit="onSubmit">
         <van-field v-model.trim="vantFormVal.taskName" name="taskName" rows="2" label="任务名称" maxlength="100" required
           show-word-limit type="textarea" placeholder="请输入" />
-        <van-field v-model="vantFormVal.priority" name="priority" label="优先级" placeholder="请选择" is-link readonly
-          required @click="showSelectionBox('priority', fixedFieldPriority)">
-          <template #input v-if="vantFormVal.priority || vantFormVal.priority == 0">
-            {{ vantFormVal.priorityName }}
-          </template>
-        </van-field>
-        <van-field v-model="vantFormVal.taskType" name="taskType" label="任务类型" placeholder="请选择" is-link readonly
-          @click="showSelectionBox('taskType', fixedFieldTaskType)" class="resetStyles">
-          <template #input v-if="vantFormVal.taskType || vantFormVal.taskType == 0">
-            {{ vantFormVal.taskTypeName }}
-          </template>
-        </van-field>
-        <!-- 客户选择 -->
-        <template v-if="vantFormVal.taskType == 0">
-          <van-field v-model="vantFormVal.customId" name="customId" label="客户" placeholder="请选择" is-link readonly
-            @click="showSelectionBox('customId', allCustomersList)" class="resetStyles">
-            <template #input v-if="vantFormVal.customId">
-              {{ vantFormVal.customIdName }}
-            </template>
-          </van-field>
-        </template>
-        <!-- 商机选择 -->
-        <template v-if="vantFormVal.taskType == 1">
-          <van-field v-model="vantFormVal.businessOpportunityId" name="businessOpportunityId" :label="`${businessLabel}`"
-            placeholder="请选择" is-link readonly class="resetStyles"
-            @click="showSelectionBox('businessOpportunityId', allBusinessOpportunities)">
-            <template #input v-if="vantFormVal.businessOpportunityId">
-              {{ vantFormVal.businessOpportunityIdName }}
+        <template v-if="userInfo?.company?.isSimple != 1">
+          <van-field v-model="vantFormVal.priority" name="priority" label="优先级" placeholder="请选择" is-link readonly
+            required @click="showSelectionBox('priority', fixedFieldPriority)">
+            <template #input v-if="vantFormVal.priority || vantFormVal.priority == 0">
+              {{ vantFormVal.priorityName }}
             </template>
           </van-field>
-        </template>
-        <!-- 销售订单选择 -->
-        <template v-if="vantFormVal.taskType == 2">
-          <van-field v-model="vantFormVal.orderId" name="orderId" label="销售订单" placeholder="请选择" is-link readonly
-            class="resetStyles" @click="showSelectionBox('orderId', allSalesOrdersList)">
-            <template #input v-if="vantFormVal.orderId">
-              {{ vantFormVal.orderIdName }}
+          <van-field v-model="vantFormVal.taskType" name="taskType" label="任务类型" placeholder="请选择" is-link readonly
+            @click="showSelectionBox('taskType', fixedFieldTaskType)" class="resetStyles">
+            <template #input v-if="vantFormVal.taskType || vantFormVal.taskType == 0">
+              {{ vantFormVal.taskTypeName }}
             </template>
           </van-field>
-        </template>
-        <!-- 线索选择 -->
-        <template v-if="vantFormVal.taskType == 3">
-          <van-field v-model="vantFormVal.clueId" name="clueId" label="线索" placeholder="请选择" is-link readonly
-            class="resetStyles" @click="showSelectionBox('clueId', allCluesList)">
-            <template #input v-if="vantFormVal.clueId">
-              {{ vantFormVal.clueIdName }}
+          <!-- 客户选择 -->
+          <template v-if="vantFormVal.taskType == 0">
+            <van-field v-model="vantFormVal.customId" name="customId" label="客户" placeholder="请选择" is-link readonly
+              @click="showSelectionBox('customId', allCustomersList)" class="resetStyles">
+              <template #input v-if="vantFormVal.customId">
+                {{ vantFormVal.customIdName }}
+              </template>
+            </van-field>
+          </template>
+          <!-- 商机选择 -->
+          <template v-if="vantFormVal.taskType == 1">
+            <van-field v-model="vantFormVal.businessOpportunityId" name="businessOpportunityId" :label="`${businessLabel}`"
+              placeholder="请选择" is-link readonly class="resetStyles"
+              @click="showSelectionBox('businessOpportunityId', allBusinessOpportunities)">
+              <template #input v-if="vantFormVal.businessOpportunityId">
+                {{ vantFormVal.businessOpportunityIdName }}
+              </template>
+            </van-field>
+          </template>
+          <!-- 销售订单选择 -->
+          <template v-if="vantFormVal.taskType == 2">
+            <van-field v-model="vantFormVal.orderId" name="orderId" label="销售订单" placeholder="请选择" is-link readonly
+              class="resetStyles" @click="showSelectionBox('orderId', allSalesOrdersList)">
+              <template #input v-if="vantFormVal.orderId">
+                {{ vantFormVal.orderIdName }}
+              </template>
+            </van-field>
+          </template>
+          <!-- 线索选择 -->
+          <template v-if="vantFormVal.taskType == 3">
+            <van-field v-model="vantFormVal.clueId" name="clueId" label="线索" placeholder="请选择" is-link readonly
+              class="resetStyles" @click="showSelectionBox('clueId', allCluesList)">
+              <template #input v-if="vantFormVal.clueId">
+                {{ vantFormVal.clueIdName }}
+              </template>
+            </van-field>
+          </template>
+          <van-field v-model="vantFormVal.contactsId" name="contactsId" label="联系人" placeholder="请选择" is-link readonly
+            :disabled="contactDisabled"
+            v-if="fixedFieldTaskType.find(v => v.value == (vantFormVal.taskType || '1'))?.show" class="resetStyles"
+            @click="showSelectionBox('contactsId', allContactsList)">
+            <template #input v-if="vantFormVal.contactsId">
+              {{ vantFormVal.contactsIdName }}
             </template>
           </van-field>
         </template>
-        <van-field v-model="vantFormVal.contactsId" name="contactsId" label="联系人" placeholder="请选择" is-link readonly
-          :disabled="contactDisabled"
-          v-if="fixedFieldTaskType.find(v => v.value == (vantFormVal.taskType || '1'))?.show" class="resetStyles"
-          @click="showSelectionBox('contactsId', allContactsList)">
-          <template #input v-if="vantFormVal.contactsId">
-            {{ vantFormVal.contactsIdName }}
-          </template>
-        </van-field>
         <van-field v-model="vantFormVal.executorId" name="executorId" label="执行人" placeholder="请选择" is-link readonly
           class="resetStyles" @click="showSelectionToBox('executorId')">
           <template #input v-if="vantFormVal.executorIdName && vantFormVal.executorIdName.length > 0">
@@ -200,7 +202,9 @@ import CustomerForm from '@components/common/formForm/formView.vue'
 import TranslationComponent from '@components/common/translationComponent.vue';
 import useRouterStore from "@store/useRouterStore.js";
 import commonUtil from "@utility/commonUtil"
+import useInfoStore from '@store/useInfoStore'
 
+const userInfo = useInfoStore().userInfo
 const router = useRouterStore()
 const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
 const props = defineProps({

+ 20 - 6
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/detail.vue

@@ -2,7 +2,7 @@
   <div class="w-full h-full flex flex-col">
     <div class="bg-white info flex-1 overflow-y-auto">
       <van-cell title="任务名称" :value="infoData.taskName" />
-      <van-cell title="优先级">
+      <van-cell title="优先级" v-if="user.company.isSimple != 1">
         <template #default>
           {{ fixedFieldPriority.find(subItem => subItem.value == infoData.priority)?.label || '' }}
         </template>
@@ -22,11 +22,24 @@
       <van-cell title="开始时间" :value="infoData.startDate" />
       <van-cell title="截至时间" :value="infoData.endDate" />
       <van-cell title="客户名称" :value="infoData.customName" v-if="infoData.customName" />
-      <van-cell :title="`${businessLabel}名称`" :value="infoData.businessName" v-if="infoData.businessName" />
-      <van-cell title="销售订单" :value="infoData.orderName" v-if="infoData.orderName" />
-      <van-cell title="线索名称" :value="infoData.clueName" v-if="infoData.clueName" />
-      <van-cell title="联系人名称" :value="infoData.contactsName" v-if="!infoData.clueId" />
-      <van-cell title="联系人号码" :value="infoData.contactsPhone" v-if="!infoData.clueId" />
+      <template v-if="user.company.isSimple != 1">
+        <van-cell :title="`${businessLabel}名称`" :value="infoData.businessName" v-if="infoData.businessName" />
+        <van-cell title="销售订单" :value="infoData.orderName" v-if="infoData.orderName" />
+        <van-cell title="线索名称" :value="infoData.clueName" v-if="infoData.clueName" />
+        <van-cell title="联系人名称" :value="infoData.contactsName" v-if="!infoData.clueId" />
+      </template>
+      <template v-if="user.company.isSimple == 1">
+        <van-cell title="联系人号码" :value="infoData.contactsPhone" />
+        <van-cell title="客户类型" :value="infoData.customTypeName" v-if="infoData.customType" />
+        <van-cell title="预约工作内容" :value="infoData.appointContent" />
+        <van-cell title="预约金额" :value="infoData.appointMoney" />
+        <van-cell title="实际工作" :value="infoData.reallyContent" />
+        <van-cell title="实际金额" :value="infoData.reallyMoney" />
+        <van-cell title="付款状态" :value="infoData.payTypeName" />
+        <van-cell title="付款方式" :value="infoData.payWayName" />
+        <van-cell title="用户反馈" :value="infoData.userBack" />
+        <van-cell title="备注" :value="infoData.remark" />
+      </template>
     </div>
     <div class="bottomButton">
       <van-button type="success" class="w-full block" @click="completeTheTask(2)"
@@ -93,6 +106,7 @@ import dayjs from 'dayjs';
 const router = useRouterStore()
 const fixedData = useFixedData()
 const userInfo = useInfoStore()
+const user = userInfo.userInfo
 const { toastSuccess, toastLoading, toastFail, toastText, clearToast } = useShowToast()
 const props = defineProps({
   info: {

+ 33 - 6
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/salesBriefings.vue

@@ -9,8 +9,8 @@
     </div>
 
     <div class="content overflow-hidden transitionEffect"
-      :style="`height: ${expandAndCollapse ? usePxToVwView(298) : '0'}`">
-      <div class="flex items-center w-full">
+      :style="`height: ${expandAndCollapse ? usePxToVwView(user.company.isSimple == 1 ? 180 : 298) : '0'}`">
+      <div class="flex items-center w-full" >
         <div class="item">
           <img src="/src/assets/image/salesKitkehu.png" class="item-img" />
           <div class="flex flex-col justify-center text=[#999999]">
@@ -22,7 +22,7 @@
             </div>
           </div>
         </div>
-        <div class="item">
+        <div class="item" v-if="user.company.isSimple != 1">
           <img src="/src/assets/image/salesKitlianxir.png" class="item-img" />
           <div class="flex flex-col justify-center text=[#999999]">
             <div>新增联系人</div>
@@ -33,9 +33,21 @@
             </div>
           </div>
         </div>
+
+        <div class="item" v-if="user.company.isSimple == 1">
+          <img src="/src/assets/image/salesKitlishi.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增{{ businessLabel }}</div>
+            <div class="flex items-end">
+              <span class="text-[#F3893C] text-size-in font-bold mr-1">
+                {{ salesBriefings?.businessOpportunity?.businessOpportunityCount || 0 }}
+              </span>个
+            </div>
+          </div>
+        </div>
       </div>
 
-      <div class="flex items-center w-full">
+      <div class="flex items-center w-full" v-if="user.company.isSimple != 1">
         <div class="item">
           <img src="/src/assets/image/salesKitlishi.png" class="item-img" />
           <div class="flex flex-col justify-center text=[#999999]">
@@ -61,7 +73,7 @@
       </div>
 
       <div class="flex items-center w-full">
-        <div class="item">
+        <div class="item" v-if="user.company.isSimple != 1">
           <img src="/src/assets/image/salesKitjer.png" class="item-img" />
           <div class="flex flex-col justify-center text=[#999999]">
             <div>销售订单金额</div>
@@ -83,9 +95,21 @@
             </div>
           </div>
         </div>
+
+        <div class="item" v-if="user.company.isSimple == 1">
+          <img src="/src/assets/image/salesKitxianshuo.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增线索</div>
+            <div class="flex items-end">
+              <span class="text-[#F44873] text-size-in font-bold mr-1">
+                {{ salesBriefings?.clue?.clueCount || 0 }}
+              </span>个
+            </div>
+          </div>
+        </div>
       </div>
 
-      <div class="flex items-center w-full">
+      <div class="flex items-center w-full" v-if="user.company.isSimple != 1">
         <div class="item">
           <img src="/src/assets/image/salesKitxianshuo.png" class="item-img" />
           <div class="flex flex-col justify-center text=[#999999]">
@@ -114,6 +138,7 @@ import { ref, computed } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { GET_SALES_BRIEFINGS } from '@hooks/useApi.js';
 import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import useInfoStore from "@store/useInfoStore.js";
 
 import requests from "@common/requests"
 import usePxToVwView from "@hooks/usePxTransform.js";
@@ -127,6 +152,8 @@ const filterCriteria = ref({
   dateSelection: 0,
   customTime: []
 })
+const useInfo = useInfoStore()
+const user = useInfo.userInfo
 const isExistBusiness = sessionStorage.getItem("isExistBusiness");
 const businessLabel = isExistBusiness === "1" ? "商机" : "项目"; 
 const scopeText = computed(() => {

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

@@ -62,7 +62,7 @@
                       {{ taskStatus[item.status]?.label }}
                     </div>
                   </div>
-                  <div class="w-full flex items-center justify-between mt-4">
+                  <div class="w-full flex items-center justify-between mt-4" v-if="user.company.isSimple != 1">
                     <div class="text-[#505050]" style="width: 100%;">优先级: {{ ['低','中','高'][item.priority] }}</div>
                   </div>
                   <div class="w-full flex items-center justify-between mt-4">
@@ -222,6 +222,7 @@ const taskStatus = [
 
 const router = useRouterStore()
 const useInfo = useInfoStore()
+const user = useInfo.userInfo
 const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
 const expandAndCollapse = ref(true)
 const calendarHeight = ref(0)

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

@@ -63,12 +63,12 @@ import Workbench from "./component/workbench.vue";
 import DataAnalysis from "./component/dataAnalysis.vue";
 import requests from "@common/requests"
 
-
 const userInfo = useInfoStore()
+const modulListss = userInfo.modularList.filter(item => item.path != '/biReport')
 const router = useRouterStore()
 const homepageType = ref('workbench')
 const showModule = ref(false)
-const moduleList = ref(userInfo.modularList)
+const moduleList = ref(modulListss)
 
 function toAddEditor(rows) {
   const jumpTo = routingInfos[rows.path.replace('/', '')]

+ 34 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/my/index.vue

@@ -14,9 +14,18 @@
             <div class="text-center">角色: {{ userInfo.userInfo?.roleName }}</div>
 
             <div class="text-center text-[#075985] mt-2" @click="applicationMarket()">应用市场</div>
+
+            <div class="text-center text-[#075985] mt-8"
+              v-if="userInfo.userInfo.userNameNeedTranslate != '1' && (isCorpWX || isWX)" @click="bindWeiXin">
+              {{ `绑定${isCorpWX ? '企业' : ''}微信` }}
+              (
+              <span v-if="(isCorpWX && userInfo.userInfo.corpwxUserid == null) || (isWX && userInfo.userInfo.wxOpenid == null)">未绑定</span>
+              <span v-if="(isCorpWX && userInfo.userInfo.corpwxUserid != null) || (isWX && userInfo.userInfo.wxOpenid != null)">已绑定</span>
+              )
+            </div>
           </div>
         </div>
-        
+
         <div class="w-full mb-40 px-24" v-if="!isCorpWX">
           <van-button type="primary" @click="signOut" class="w-full">退出登录</van-button>
         </div>
@@ -40,6 +49,29 @@ import Footer from "@components/page/footer.vue";
 const router = useRouterStore()
 const userInfo = useInfoStore()
 const isCorpWX = ref(false)
+const isWX = ref(false)
+
+function bindWeiXin() {
+  //企业微信
+  if (isCorpWX.value && userInfo.userInfo.corpwxUserid != null) {
+    return;
+  }
+  //微信
+  else if (isWX.value && userInfo.userInfo.wxOpenid != null) {
+    return;
+  }
+
+  var appId = "wx1c1d8fc81bc073a8";//智能客户管家公众号
+  var url = "https://mobcrm.ttkuaiban.com/api/wechat/bindWeiXin2?userId=" + userInfo.userInfo.id;//工时管家公众号授权回调页面
+  if (isCorpWX.value) {
+    appId = "ww4e237fd6abb635af"; //企业微信第三方的SUIT ID
+    url = "https://crm.ttkuaiban.com/api/wxcorp/bindCorpWeiXin?userId=" + userInfo.userInfo.id;//授权回调页面
+  }
+
+  var weixinUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appId + "&redirect_uri=" + encodeURI(url) + "&response_type=code&scope=snsapi_base&state=0#wechat_redirect";
+  window.location.href = weixinUrl;
+}
+
 function signOut() {
   router.redirectTo({
     pathName: 'login',
@@ -54,6 +86,7 @@ function signOut() {
 function judgingTheEnvironment() {
   const currentEnvironment = navigator.userAgent.toLowerCase();
   isCorpWX.value = currentEnvironment.indexOf("wxwork") > 0 ? true : false
+  isWX.value = currentEnvironment.indexOf("micromessenger") > 0 ? true : false
 }
 
 const applicationMarket = () => {

+ 2 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/corpWXparam.js

@@ -1,10 +1,9 @@
 import requests from "@common/requests";
 
-const wxTokenUrl = 'http://mobworktime.ttkuaiban.com/api/wechat/loginByWXCode'
+const wxTokenUrl = 'http://mobcrm.ttkuaiban.com/api/wechat/loginByWXCode'
 const coprWxTokenUrl = 'https://mobcrm.ttkuaiban.com/api/corpWXAuth'
 
-export const WX_APPID = 'wx749c84daac654e1e' // 微信appId
-// export const CORP_WX_APPID = 'ww4e237fd6abb635af' // 企业微信appId
+export const WX_APPID = 'wx1c1d8fc81bc073a8' // 微信appId
 export const CORP_WX_APPID = 'wwdd1137a65ce0fc87' // 企业微信appId
 
 /**

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

@@ -6,7 +6,7 @@ import { VantResolver } from "unplugin-vue-components/resolvers";
 import { postcssConfig } from "./postcss.config.js";
 
 // const target = 'http://192.168.2.40:10099';
-const target = 'http://192.168.2.40:10010';
+const target = 'http://192.168.2.5:10010';
 // const target = 'http://1.94.62.58:10014';
 // const target = 'http://192.168.2.17:10010';
 

+ 4 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/package.json

@@ -20,18 +20,21 @@
     "@fullcalendar/vue3": "^6.1.15",
     "@vue-flow/background": "^1.3.2",
     "@vue-flow/core": "^1.42.2",
-    "@zmjs/form-design": "file:../plugIn/form-design-master/update",
+    "@zmjs/form-design": "file:C://Users/37313/Downloads/zmjs-form-design-0.1.16.tgz",
     "animate.css": "^4.1.1",
     "axios": "^1.6.7",
     "browserslist": "^4.24.2",
     "caniuse-lite": "^1.0.30001680",
+    "docx": "^9.3.0",
     "echarts": "^5.5.0",
     "element-plus": "^2.5.6",
+    "marked": "^15.0.7",
     "pinia": "^2.1.7",
     "pinia-plugin-persistedstate": "^3.2.1",
     "vue": "^3.4.19",
     "vue-draggable-plus": "^0.6.0",
     "vue-router": "^4.3.0",
+    "vuedraggable": "^4.1.0",
     "vuex": "^4.1.0"
   },
   "devDependencies": {

+ 30 - 25
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/index.vue

@@ -18,33 +18,35 @@
           <el-input v-model="form.taskName" type="textarea" placeholder="请输入任务名称" clearable maxlength="100"
             show-word-limit :disabled="disabledList && disabledList.includes('taskName')" />
         </el-form-item>
-        <el-form-item prop="priority" label="优先级:" required>
-          <el-select v-model="form.priority" placeholder="请选择" clearable
-            :disabled="disabledList && disabledList.includes('priority')">
-            <el-option v-for="item in PRIORITY " :key="item.value" :value="item.value" :label="item.label" />
-          </el-select>
-        </el-form-item>
-        <el-form-item :label="form.taskType">
-          <template #label>
-            <el-select v-model="form.taskType" class="border resetSelect" style="width: 100px" @change="changeTaskType"
-              :disabled="disabledList && disabledList.includes('taskType')">
-              <el-option v-for="item in TASK_TYPE" :key="item.value" :value="item.value" :label="item.label" />
+        <template v-if="userInfo.company.isSimple != 1">
+          <el-form-item prop="priority" label="优先级:" required >
+            <el-select v-model="form.priority" placeholder="请选择" clearable
+              :disabled="disabledList && disabledList.includes('priority')">
+              <el-option v-for="item in PRIORITY " :key="item.value" :value="item.value" :label="item.label" />
             </el-select>
-          </template>
-          <template v-for="item in TASK_TYPE_FIELD">
-            <el-select v-model="form[item.field]" v-if="form.taskType == item.type" placeholder="请选择" clearable
-              filterable :disabled="disabledList && disabledList.includes(item.field)" @change="(e: any) => {taskFormChange(e, item.field)}">
-              <el-option v-for="v in taskTypeValueData" :key="v.id" :value="v[item.valueIndex]"
-                :label="v[item.labelIndex]" />
+          </el-form-item>
+          <el-form-item :label="form.taskType">
+            <template #label>
+              <el-select v-model="form.taskType" class="border resetSelect" style="width: 100px" @change="changeTaskType"
+                :disabled="disabledList && disabledList.includes('taskType')">
+                <el-option v-for="item in TASK_TYPE" :key="item.value" :value="item.value" :label="item.label" />
+              </el-select>
+            </template>
+            <template v-for="item in TASK_TYPE_FIELD">
+              <el-select v-model="form[item.field]" v-if="form.taskType == item.type" placeholder="请选择" clearable
+                filterable :disabled="disabledList && disabledList.includes(item.field)" @change="(e: any) => {taskFormChange(e, item.field)}">
+                <el-option v-for="v in taskTypeValueData" :key="v.id" :value="v[item.valueIndex]"
+                  :label="v[item.labelIndex]" />
+              </el-select>
+            </template>
+          </el-form-item>
+          <el-form-item label="联系人:" v-if="TASK_TYPE.find(v => v.value == (form.taskType || '1'))?.show">
+            <el-select v-model="form.contactsId" placeholder="请选择" clearable filterable
+              :disabled="(disabledList && disabledList.includes('contactsId')) || !form[TASK_TYPE_FIELD[form.taskType].field]">
+              <el-option v-for="item in contactValueData" :key="item.id" :value="item.id" :label="item.name" />
             </el-select>
-          </template>
-        </el-form-item>
-        <el-form-item label="联系人:" v-if="TASK_TYPE.find(v => v.value == (form.taskType || '1'))?.show">
-          <el-select v-model="form.contactsId" placeholder="请选择" clearable filterable
-            :disabled="(disabledList && disabledList.includes('contactsId')) || !form[TASK_TYPE_FIELD[form.taskType].field]">
-            <el-option v-for="item in contactValueData" :key="item.id" :value="item.id" :label="item.name" />
-          </el-select>
-        </el-form-item>
+          </el-form-item>
+        </template>
         <el-form-item label="执行人:">
           <!-- <el-select v-model="form.executorId" placeholder="请选择" clearable multiple filterable
             :disabled="disabledList && disabledList.includes('executorId')">
@@ -150,6 +152,9 @@ import { Props, Emits } from './type';
 import { URL_GETALL } from '@/pages/contacts/api';
 import personnelSearch from '@/components/translationComponent/personnelSearch/personnelSearch.vue';
 
+import { useStore } from "../../store/index"
+const { userInfo } = useStore()
+
 const props = defineProps<Props>()
 const emits = defineEmits<Emits>();
 watch(() => props.saveLoading, (val) => {

+ 23 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/element-plus.d.ts

@@ -0,0 +1,23 @@
+declare module 'element-plus' {
+  import { Plugin } from 'vue'
+  const ElementPlus: Plugin
+  export default ElementPlus
+}
+
+declare module '@element-plus/icons-vue' {
+  import { DefineComponent } from 'vue'
+  
+  export const ChatLineRound: DefineComponent
+  export const User: DefineComponent  
+  export const Download: DefineComponent
+  export const CopyDocument: DefineComponent
+  export const Delete: DefineComponent
+  export const CirclePlus: DefineComponent
+  export const Edit: DefineComponent
+  export const CirclePlusFilled: DefineComponent
+  export const Search: DefineComponent
+  export const QuestionFilled: DefineComponent
+  
+  const component: DefineComponent
+  export default component
+}

+ 33 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/api.ts

@@ -1,4 +1,4 @@
-import { post, get } from '@/utils/request';
+import { post, get, uploadFile as requestUploadFile } from '@/utils/request';
 
 export type RequestProps = {
   /**
@@ -92,3 +92,35 @@ export async function getBulletinData(
 ): Promise<BulletinData> {
   return await post('/order/salesKit', payload);
 }
+
+export type AIQuestionParams = {
+  questionDataSource: number;
+  sourceContent: string;
+  content: string;
+  startDate: string;
+  endDate: string;
+  url?: string;
+};
+
+export type AIQuestionResponse = {
+  code: string;
+  data: string;
+};
+
+export async function askAIQuestion(
+  payload: AIQuestionParams
+): Promise<AIQuestionResponse> {
+  return await post('/aiQuestion/ask', payload);
+}
+
+export type UploadFileResponse = {
+  code: string;
+  data: string;
+};
+
+export async function uploadFileApi(file: File): Promise<UploadFileResponse> {
+  const formData = new FormData();
+  formData.append('multipartFile', file);
+  
+  return await requestUploadFile('/common/uploadFile', formData);
+}

+ 449 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/components/AIChat.vue

@@ -0,0 +1,449 @@
+<template>
+  <div class="border-gray-200 border rounded p-3 h-full flex flex-col" style="min-height: 750px; max-height: 750px;">
+    <div class="text-sm font-medium mb-3">DeepSeek大模型CRM数据分析</div>
+    
+    <!-- Chat messages container with fixed height and scrolling -->
+    <div class="mb-3 border-gray-200 border rounded overflow-y-auto flex-grow" style="min-height: 0;">
+      <div class="p-3 flex flex-col gap-3">
+        <div v-for="(message, index) in messages" :key="index" class="flex" :class="{'justify-end': message.role === 'user', 'justify-start': message.role === 'assistant'}">
+          <div v-if="message.role === 'assistant'" class="flex items-start gap-2">
+            <el-avatar :size="24" class="bg-gray-200 flex items-center justify-center">
+              <el-icon><ChatLineRound /></el-icon>
+            </el-avatar>
+            <div class="border-gray-200 border rounded p-2 text-sm max-w-[80%] bg-gray-50 relative">
+              <div class="markdown-body" v-html="renderMarkdown(message.content)"></div>
+              <div v-if="message.loading" class="loading-dots">
+                <span></span>
+                <span></span>
+                <span></span>
+              </div>
+              <el-button 
+                v-if="!message.loading && message.role === 'assistant' && index > 0"
+                @click="exportToWord(message.content)"
+                size="small" 
+                type="text" 
+                class="absolute -bottom-3 -right-3"
+                :icon="Download"
+              />
+            </div>
+          </div>
+          <div v-if="message.role === 'user'" class="flex items-start gap-2 justify-end">
+            <div class="border-gray-200 border rounded p-2 text-sm max-w-[80%] bg-blue-50 markdown-body" v-html="renderMarkdown(message.content)">
+            </div>
+            <el-avatar :size="24" class="bg-gray-200 flex items-center justify-center">
+              <el-icon><User /></el-icon>
+            </el-avatar>
+          </div>
+        </div>
+      </div>
+    </div>
+    
+    <!-- Data source selection -->
+    <div class="mb-3 flex gap-2">
+      <div>
+        <span class="text-sm mr-2">数据来源</span>
+        <el-select v-model="dataSource" size="small" style="width: 120px">
+          <el-option label="系统表" value="system" />
+          <el-option label="自定义报表" value="custom" />
+          <el-option label="本地上传" value="upload" />
+          <el-option label="自由交流" value="free" />
+        </el-select>
+      </div>
+      
+      <div v-if="dataSource === 'system'">
+        <el-select v-model="systemTable" size="small" style="width: 120px">
+          <el-option label="线索" value="clue" />
+          <el-option label="商机" value="business_opportunity" />
+          <el-option label="客户" value="customer" />
+          <el-option label="联系人" value="contact" />
+          <el-option label="合同" value="contract" />
+          <el-option label="销售订单" value="order" />
+          <el-option label="产品" value="product" />
+        </el-select>
+      </div>
+      
+      <div v-if="dataSource === 'upload'">
+        <el-upload
+          :show-file-list="false"
+          :before-upload="beforeUpload"
+          :http-request="handleUpload"
+          accept=".xlsx,.xls"
+        >
+          <el-button size="small" type="primary">上传Excel</el-button> <span style="margin-left:5px;color:orange">{{ uploadedFilePath?'上传成功':'' }}</span>
+        </el-upload>
+      </div>
+      
+      <div class="ml-auto">
+        <span class="text-sm mr-2">时间段</span>
+        <el-date-picker
+          v-model="dateRange"
+          type="daterange"
+          size="small"
+          range-separator="/"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          format="YYYY-MM-DD"
+        />
+      </div>
+    </div>
+    
+    <!-- Input area -->
+    <div class="flex gap-2 items-end mt-auto">
+      <el-input
+        v-model="inputMessage"
+        type="textarea"
+        :rows="2"
+        placeholder="请进行数据分析,给一个总结报告,不超过300字"
+        class="flex-1"
+        resize="none"
+      />
+      <el-button
+        type="primary"
+        @click="sendMessage"
+        :disabled="loading || !inputMessage.trim()"
+        size="small"
+        style="height: 53px; "
+      >
+        发送
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, nextTick, computed } from 'vue';
+import { marked } from 'marked';
+import { ChatLineRound, User, Download } from '@element-plus/icons-vue';
+import { Document, Paragraph, TextRun, Packer } from 'docx';
+import { askAIQuestion, uploadFileApi, type AIQuestionParams, type UploadFileResponse } from '../api';
+import { ElMessage } from 'element-plus/es'
+const renderMarkdown = (content: string): string => {
+  // Configure marked with options
+  marked.setOptions({
+    breaks: true,
+    gfm: true
+  });
+
+  try {
+    return marked.parse(content) as string;
+  } catch (error) {
+    console.error('Markdown parsing error:', error);
+    return content.replace(/</g, '<').replace(/>/g, '>'); // Escape HTML if parsing fails
+  }
+};
+
+const exportToWord = async (content: string) => {
+  // 先将markdown转换为HTML
+  let htmlContent = '';
+  try {
+    const result = await marked.parse(content, {
+      breaks: true,
+      gfm: true
+    });
+    htmlContent = result;
+  } catch (error) {
+    console.error('Markdown parsing error:', error);
+    htmlContent = content; // Fallback to raw content
+  }
+  
+  // 创建一个临时的div来解析HTML
+  const tempDiv = document.createElement('div');
+  tempDiv.innerHTML = htmlContent;
+  
+  // 准备Word文档的段落
+  const docChildren: Paragraph[] = [];
+  
+  // 处理每个HTML元素并转换为Word文档元素
+  Array.from(tempDiv.childNodes).forEach((node) => {
+    if (node.nodeType === Node.TEXT_NODE) {
+      // 处理纯文本节点
+      if (node.textContent && node.textContent.trim()) {
+        docChildren.push(
+          new Paragraph({
+            children: [new TextRun({ text: node.textContent.trim() })],
+          })
+        );
+      }
+    } else if (node.nodeType === Node.ELEMENT_NODE) {
+      const element = node as HTMLElement;
+      
+      // 根据HTML标签类型创建不同的Word元素
+      if (element.tagName === 'H1' || element.tagName === 'H2' || element.tagName === 'H3') {
+        docChildren.push(
+          new Paragraph({
+            heading: element.tagName === 'H1' ? 'Heading1' : element.tagName === 'H2' ? 'Heading2' : 'Heading3',
+            children: [
+              new TextRun({
+                text: element.textContent || '',
+                bold: true,
+                size: element.tagName === 'H1' ? 36 : element.tagName === 'H2' ? 32 : 28,
+              }),
+            ],
+          })
+        );
+      } else if (element.tagName === 'P') {
+        docChildren.push(
+          new Paragraph({
+            children: [new TextRun({ text: element.textContent || '' })],
+            spacing: { after: 200 },
+          })
+        );
+      } else if (element.tagName === 'UL' || element.tagName === 'OL') {
+        // 处理列表
+        Array.from(element.children).forEach((li, index) => {
+          docChildren.push(
+            new Paragraph({
+              children: [
+                new TextRun({ text: element.tagName === 'UL' ? '• ' : `${index + 1}. ` }),
+                new TextRun({ text: li.textContent || '' }),
+              ],
+              indent: { left: 720 }, // 缩进
+              spacing: { after: 120 },
+            })
+          );
+        });
+      } else if (element.tagName === 'BLOCKQUOTE') {
+        docChildren.push(
+          new Paragraph({
+            children: [new TextRun({ text: element.textContent || '', italics: true })],
+            indent: { left: 720 },
+            border: { left: { color: '#CCCCCC', size: 6, space: 15, style: 'single' } },
+            spacing: { after: 200 },
+          })
+        );
+      } else if (element.tagName === 'PRE') {
+        // 代码块
+        docChildren.push(
+          new Paragraph({
+            children: [new TextRun({ 
+              text: element.textContent || '', 
+              font: { name: 'Courier New' } 
+            })],
+            shading: { 
+              fill: '#F6F8FA',
+              type: 'clear'
+            },
+            spacing: { after: 200 },
+          })
+        );
+      } else {
+        // 其他元素默认处理
+        docChildren.push(
+          new Paragraph({
+            children: [new TextRun({ text: element.textContent || '' })],
+          })
+        );
+      }
+    }
+  });
+  
+  // 创建Word文档
+  const doc = new Document({
+    sections: [{
+      properties: {},
+      children: docChildren,
+    }],
+  });
+
+  const blob = await Packer.toBlob(doc);
+  const url = URL.createObjectURL(blob);
+  const a = document.createElement('a');
+  a.href = url;
+  a.download = 'AI分析报告.docx';
+  a.click();
+  URL.revokeObjectURL(url);
+};
+
+type DataSourceType = 'system' | 'custom' | 'upload' | 'free';
+type SystemTableType = 'clue' | 'business_opportunity' | 'customer' | 'contact' | 'contract' | 'order' | 'product';
+
+interface ChatMessage {
+  role: 'user' | 'assistant';
+  content: string;
+  loading?: boolean;
+}
+
+// Data source selection
+const dataSource = ref<DataSourceType>('system');
+const systemTable = ref<SystemTableType>('clue');
+const getFirstDayOfMonth = () => {
+  const date = new Date();
+  return new Date(date.getFullYear(), date.getMonth(), 1);
+};
+
+const uploadFile = ref<File | null>(null);
+const uploadedFilePath = ref<string>('');
+
+const beforeUpload = (file: File) => {
+  uploadFile.value = file;
+  uploadedFilePath.value = '';
+  return true;
+};
+
+const handleUpload = async (options: any) => {
+  try {
+    const result = await uploadFileApi(options.file);
+    if (result.code === 'ok') {
+      uploadedFilePath.value = result.data;
+      // Show upload success message
+      ElMessage.success("上传成功,请输入问题并发送")
+    } else {
+      console.error('Upload failed:', result);
+    }
+  } catch (error) {
+    console.error('Upload error:', error);
+  }
+};
+
+const dateRange = ref([getFirstDayOfMonth(), new Date()]);
+
+// Chat functionality
+const inputMessage = ref('请进行数据分析,给一个总结报告,不超过300字');
+const loading = ref(false);
+const messages = reactive<ChatMessage[]>([
+  { role: 'assistant', content: '你好,需要分析查询哪些数据,请交给我' },
+]);
+
+const sendMessage = async () => {
+  if (!inputMessage.value.trim() || loading.value) return;
+
+  loading.value = true;
+  const userMessage: ChatMessage = { role: 'user', content: inputMessage.value };
+  messages.push(userMessage);
+  const thinkingIndex = messages.length;
+  messages.push({ role: 'assistant', content: 'AI正在思考', loading: true });
+  
+  try {
+    type DataSourceType = 'system' | 'custom' | 'upload' | 'free';
+    const dataSourceMap: Record<DataSourceType, number> = {
+      'system': 1,
+      'custom': 2,
+      'upload': 3, 
+      'free': 4
+    };
+
+    const params: AIQuestionParams = {
+      questionDataSource: dataSourceMap[dataSource.value],
+      sourceContent: dataSource.value === 'system' ? systemTable.value : '',
+      content: inputMessage.value,
+      startDate: dateRange.value[0]?.toISOString().split('T')[0],
+      endDate: dateRange.value[1]?.toISOString().split('T')[0],
+      url: dataSource.value === 'upload' ? uploadedFilePath.value : ''
+    };
+
+    const result = await askAIQuestion(params);
+    messages[thinkingIndex] = {
+      role: 'assistant',
+      content: result.data || '根据您的请求,我已分析了相关数据。分析结果显示...',
+      loading: false
+    };
+  } catch (error) {
+    console.error('API error:', error);
+    messages[thinkingIndex] = {
+      role: 'assistant',
+      content: '抱歉,请求处理失败,请稍后再试',
+      loading: false
+    };
+  }
+
+  inputMessage.value = '';
+  loading.value = false;
+  // 触发滚动到底部
+  nextTick(() => {
+    const container = document.querySelector('.overflow-y-auto');
+    if (container) {
+      container.scrollTop = container.scrollHeight;
+    }
+  });
+};
+</script>
+
+<style scoped>
+:deep(.el-textarea__inner) {
+  resize: none;
+}
+
+.markdown-body {
+  line-height: 1.5;
+  word-wrap: break-word;
+}
+
+.markdown-body :deep(h1),
+.markdown-body :deep(h2),
+.markdown-body :deep(h3),
+.markdown-body :deep(h4),
+.markdown-body :deep(h5),
+.markdown-body :deep(h6) {
+  margin-top: 0.5em;
+  margin-bottom: 0.5em;
+  font-weight: bold;
+}
+
+.markdown-body :deep(p) {
+  margin-bottom: 1em;
+}
+
+.markdown-body :deep(ul),
+.markdown-body :deep(ol) {
+  padding-left: 2em;
+  margin-bottom: 1em;
+}
+
+.markdown-body :deep(li) {
+  margin-bottom: 0.5em;
+}
+
+.markdown-body :deep(code) {
+  background-color: rgba(175, 184, 193, 0.2);
+  border-radius: 3px;
+  padding: 0.2em 0.4em;
+  font-family: monospace;
+}
+
+.markdown-body :deep(pre) {
+  background-color: #f6f8fa;
+  border-radius: 3px;
+  padding: 1em;
+  overflow: auto;
+  margin-bottom: 1em;
+}
+
+.markdown-body :deep(blockquote) {
+  border-left: 4px solid #dfe2e5;
+  color: #6a737d;
+  padding-left: 1em;
+  margin-left: 0;
+  margin-bottom: 1em;
+}
+
+.loading-dots {
+  display: inline-flex;
+  align-items: center;
+  gap: 2px;
+}
+
+.loading-dots span {
+  display: inline-block;
+  width: 6px;
+  height: 6px;
+  border-radius: 50%;
+  background-color: currentColor;
+  animation: bounce 1.4s infinite ease-in-out both;
+}
+
+.loading-dots span:nth-child(1) {
+  animation-delay: -0.32s;
+}
+
+.loading-dots span:nth-child(2) {
+  animation-delay: -0.16s;
+}
+
+@keyframes bounce {
+  0%, 80%, 100% { 
+    transform: scale(0);
+  } 40% { 
+    transform: scale(1.0);
+  }
+}
+</style>

+ 275 - 258
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/analysis/index.vue

@@ -3,9 +3,11 @@ import { ref, reactive, onMounted, watchEffect } from 'vue';
 import TrendCard from './components/TrendCard.vue';
 import SimpleCard from './components/SimpleCard.vue';
 import Divider from './components/Divider.vue';
+import AIChat from './components/AIChat.vue';
 import Echarts from '@/components/ReEcharts/index.vue';
 import { EChartsOption, use } from 'echarts';
 import { dayjs } from 'element-plus';
+import { QuestionFilled } from '@element-plus/icons-vue';
 import { useStore } from "../../store/index"
 const { userInfo } = useStore()
 import {
@@ -21,6 +23,7 @@ import {
 const dataAnalysisFlag = userInfo?.moduleList?.filter((item: any) => item.path == '/analysis') || []
 const isExistBusiness = sessionStorage.getItem("isExistBusiness");
 const businessLabel = isExistBusiness === "1" ? "商机" : "项目"; 
+const isSimple = sessionStorage.getItem("isSimple");
 const permissionOptions = dataAnalysisFlag.length > 0 ? [
   {
     label: '仅本人',
@@ -67,20 +70,25 @@ const select1 = ref<HTMLDivElement>();
 const select2 = ref<HTMLDivElement>();
 const select3 = ref<HTMLDivElement>();
 
+const getFirstDayOfMonth = () => {
+  const now = new Date();
+  return new Date(now.getFullYear(), now.getMonth(), 1);
+};
+
 const bulletinPrompt = reactive<PromptType>({
   permission: 0,
   date: 0,
-  sliceDate: [new Date(), new Date()]
+  sliceDate: [getFirstDayOfMonth(), new Date()]
 });
 const summaryPrompt = reactive<PromptType>({
   permission: 0,
   date: 0,
-  sliceDate: [new Date(), new Date()]
+  sliceDate: [getFirstDayOfMonth(), new Date()]
 });
 const stagePrompt = reactive<PromptType>({
   permission: 0,
   date: 0,
-  sliceDate: [new Date(), new Date()]
+  sliceDate: [getFirstDayOfMonth(), new Date()]
 });
 
 const requestData = reactive<{
@@ -96,7 +104,12 @@ const requestData = reactive<{
 const chartOptions: EChartsOption = reactive({
   grid: { top: 0, bottom: 20, left: 60 },
   yAxis: {
-    type: 'category'
+    type: 'category',
+    axisLabel: {
+      formatter: (value: string) => {
+        return value.replace(/(.{4})/g, "$1\n");
+      },
+    },
   },
   xAxis: {
     type: 'value'
@@ -173,284 +186,288 @@ watchEffect(() => {
 
 <template>
   <div class="m-5 bg-white min-h-full p-4 rounded">
-    <section>
-      <!-- <div class="h-[800px] w-full bg-[red] mb-5">
-        <iframe
-          src="http://47.101.180.183:9080/webapp/agent"
-          style="border: none;"
-          class="w-full h-full"
-        />
-      </div> -->
-      <div class="flex gap-3 mb-4">
-        <div class="w-40">
-          <el-select
-            size="small"
-            :model-value="bulletinPrompt.permission"
-            @change="(value: any) => (bulletinPrompt.permission = value)"
-          >
-            <el-option
-              v-for="permission in permissionOptions"
-              :label="permission.label"
-              :value="permission.value"
-            />
-          </el-select>
-        </div>
-        <div class="w-40">
-          <el-select
-            ref="select1"
-            size="small"
-            :model-value="bulletinPrompt.date"
-            @change="(value: any) => (bulletinPrompt.date = value)"
-          >
-            <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
-            <el-option label="自定义" value="ignore" disabled>
-              <div class="flex gap-2 w-80">
-                <el-date-picker
-                  size="small"
-                  :clearable="false"
-                  type="daterange"
-                  class="w-12"
-                  v-model="bulletinPrompt.sliceDate"
-                  start-placeholder="开始日期"
-                  end-placeholder="结束日期"
-                />
-                <el-button
-                  size="small"
-                  type="primary"
-                  @click="
-                    () => {
-                      select1?.blur();
-                      bulletinPrompt.date = 'ignore';
-                    }
-                  "
-                  >确认</el-button
-                >
-              </div>
-            </el-option>
-          </el-select>
-        </div>
-      </div>
-      <div class="border-gray-200 border rounded p-3">
-        <div class="flex gap-1.5 items-center mb-3">
-          <img width="16" src="../../assets/trend.png" />
-          <div class="text-sm font-medium">销售简报</div>
-          <el-icon><QuestionFilled class="text-gray-500 text-sm" /></el-icon>
-        </div>
-        <div class="grid xl:grid-cols-4 lg:grid-cols-3 grid-cols-2 gap-4">
-          <TrendCard
-            title="新增客户"
-            unit="人"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.custom.customCount"
-            :compare="requestData?.bulletin?.custom.customPromote"
-          />
-          <TrendCard
-            title="新增联系人"
-            unit="人"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.contacts.contactsCount"
-            :compare="requestData?.bulletin?.contacts.contactsPromote"
-          />
-          <TrendCard
-            :title="`新增${businessLabel}`"
-            unit="个"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.businessOpportunity.businessOpportunityCount"
-            :compare="requestData?.bulletin?.businessOpportunity.businessOpportunityPromote"
-          />
-          <TrendCard
-            title="新增销售订单"
-            unit="个"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.salesOrder.salesOrderCount"
-            :compare="requestData?.bulletin?.salesOrder.salesOrderPromote"
-          />
-          <TrendCard
-            title="销售订单金额"
-            unit="元"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.salesOrdersPrice.salesOrdersPrice"
-            :compare="requestData?.bulletin?.salesOrdersPrice.salesOrderPricePromote"
-          />
-          <TrendCard
-            :title="`${businessLabel}金额`"
-            unit="元"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPrice"
-            :compare="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPromote"
-          />
-          <TrendCard
-            title="新增线索"
-            unit="个"
-            :selectVal="bulletinPrompt"
-            :number="requestData?.bulletin?.clue.clueCount"
-            :compare="requestData?.bulletin?.clue.cluePromote"
-          />
+    <section class="flex gap-4">
+      <!-- Left side - Original functionality -->
+      <div class="w-1/2 min-w-[650px] flex-shrink-0">
+        <div class="flex gap-3 mb-4">
+          <div class="w-40">
+            <el-select
+              size="small"
+              :model-value="bulletinPrompt.permission"
+              @change="(value: any) => (bulletinPrompt.permission = value)"
+            >
+              <el-option
+                v-for="permission in permissionOptions"
+                :label="permission.label"
+                :value="permission.value"
+              />
+            </el-select>
+          </div>
+          <div class="w-40">
+            <el-select
+              ref="select1"
+              size="small"
+              :model-value="bulletinPrompt.date"
+              @change="(value: any) => (bulletinPrompt.date = value)"
+            >
+              <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
+              <el-option label="自定义" value="ignore" disabled>
+                <div class="flex gap-2 w-80">
+                      <el-date-picker
+                        size="small"
+                        :clearable="false"
+                        type="daterange"
+                        class="w-40"
+                        v-model="bulletinPrompt.sliceDate"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                      />
+                  <el-button
+                    size="small"
+                    type="primary"
+                    @click="
+                      () => {
+                        select1?.blur();
+                        bulletinPrompt.date = 'ignore';
+                      }
+                    "
+                    >确认</el-button
+                  >
+                </div>
+              </el-option>
+            </el-select>
+          </div>
         </div>
-      </div>
-      <div class="my-8 flex gap-4 items-start">
-        <div class="border-gray-200 border rounded p-3 flex-1">
-          <div class="text-sm font-medium">数据汇总</div>
-          <div class="flex gap-3 mb-8 mt-2">
-            <div class="w-40">
-              <el-select
-                clearable
-                size="small"
-                :model-value="summaryPrompt.permission"
-                @change="(value: any) => (summaryPrompt.permission = value)"
-              >
-                <el-option
-                  v-for="permission in permissionOptions"
-                  :label="permission.label"
-                  :value="permission.value"
-                />
-              </el-select>
-            </div>
-            <div class="w-40">
-              <el-select
-                ref="select2"
-                clearable
-                size="small"
-                :model-value="summaryPrompt.date"
-                @change="(value: any) => (summaryPrompt.date = value)"
-              >
-                <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
-                <el-option label="自定义" value="ignore" disabled>
-                  <div class="flex gap-2 w-80">
-                    <el-date-picker
-                      size="small"
-                      :clearable="false"
-                      type="daterange"
-                      class="w-12"
-                      v-model="summaryPrompt.sliceDate"
-                      start-placeholder="开始日期"
-                      end-placeholder="结束日期"
-                    />
-                    <el-button
-                      size="small"
-                      type="primary"
-                      @click="
-                        () => {
-                          select2?.blur();
-                          summaryPrompt.date = 'ignore';
-                        }
-                      "
-                      >确认</el-button
-                    >
-                  </div>
-                </el-option>
-              </el-select>
-            </div>
+        <div class="border-gray-200 border rounded p-3">
+          <div class="flex gap-1.5 items-center mb-3">
+            <img width="16" src="../../assets/trend.png" />
+            <div class="text-sm font-medium">销售简报</div>
+            <el-icon><QuestionFilled class="text-gray-500 text-sm" /></el-icon>
           </div>
-          <Divider title="客户汇总" />
-          <div class="my-6 grid grid-cols-4 gap-2">
-            <SimpleCard
+          <div class="grid xl:grid-cols-4 lg:grid-cols-3 grid-cols-2 gap-4">
+            <TrendCard
               title="新增客户"
-              unit="个"
-              :number="requestData.summary?.customDataSummary.newNum"
+              unit="人"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.custom.customCount"
+              :compare="requestData?.bulletin?.custom.customPromote"
             />
-            <SimpleCard
-              title="转成交客户"
-              unit="个"
-              :number="requestData.summary?.customDataSummary.closeDealNum"
-              v-if="false"
+            <TrendCard
+              title="新增联系人"
+              unit="人"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.contacts.contactsCount"
+              :compare="requestData?.bulletin?.contacts.contactsPromote"
+              v-if="isSimple != '1'"
             />
-          </div>
-          <Divider :title="`${businessLabel}汇总`" />
-          <div class="my-6 grid grid-cols-4 gap-2">
-            <SimpleCard
+            <TrendCard
               :title="`新增${businessLabel}`"
               unit="个"
-              :number="requestData.summary?.businessOpportunityDataSummary.newNum"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.businessOpportunity.businessOpportunityCount"
+              :compare="requestData?.bulletin?.businessOpportunity.businessOpportunityPromote"
             />
-            <SimpleCard
-              :title="`赢单${businessLabel}`"
+            <TrendCard
+              title="新增销售订单"
               unit="个"
-              :number="requestData.summary?.businessOpportunityDataSummary.winning"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.salesOrder.salesOrderCount"
+              :compare="requestData?.bulletin?.salesOrder.salesOrderPromote"
+              v-if="isSimple != '1'"
             />
-            <SimpleCard
-              :title="`输单${businessLabel}`"
-              unit="个"
-              :number="requestData.summary?.businessOpportunityDataSummary.losting"
+            <TrendCard
+              title="销售订单金额"
+              unit="元"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.salesOrdersPrice.salesOrdersPrice"
+              :compare="requestData?.bulletin?.salesOrdersPrice.salesOrderPricePromote"
+              v-if="isSimple != '1'"
             />
-            <SimpleCard
-              :title="`${businessLabel}总金额`"
+            <TrendCard
+              :title="`${businessLabel}金额`"
               unit="元"
-              :number="requestData.summary?.businessOpportunityDataSummary.allAmountOfMoney"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPrice"
+              :compare="requestData?.bulletin?.businessOpportunityPrice.businessOpportunityPromote"
             />
-          </div>
-          <Divider title="线索汇总" />
-          <div class="my-6 grid grid-cols-4 gap-2">
-            <SimpleCard
+            <TrendCard
               title="新增线索"
               unit="个"
-              :number="requestData.summary?.clueDataSummary.newNum"
-            />
-            <SimpleCard
-              :title="`线索转${businessLabel}`"
-              unit="个"
-              :number="requestData.summary?.clueDataSummary.changeNum"
+              :selectVal="bulletinPrompt"
+              :number="requestData?.bulletin?.clue.clueCount"
+              :compare="requestData?.bulletin?.clue.cluePromote"
             />
           </div>
         </div>
-        <div class="border-gray-200 border rounded p-3 flex-1">
-          <div class="text-sm font-medium">{{ `${businessLabel}阶段` }}</div>
-          <div class="flex gap-3 mb-8 mt-2">
-            <div class="w-40">
-              <el-select
-                clearable
-                size="small"
-                :model-value="stagePrompt.permission"
-                @change="(value: any) => (stagePrompt.permission = value)"
-              >
-                <el-option
-                  v-for="permission in permissionOptions"
-                  :label="permission.label"
-                  :value="permission.value"
-                />
-              </el-select>
+        <div class="my-8 flex gap-4 items-start">
+          <div class="border-gray-200 border rounded p-3 flex-1">
+            <div class="text-sm font-medium">数据汇总</div>
+            <div class="flex gap-3 mb-8 mt-2">
+              <div class="w-40">
+                <el-select
+                  clearable
+                  size="small"
+                  :model-value="summaryPrompt.permission"
+                  @change="(value: any) => (summaryPrompt.permission = value)"
+                >
+                  <el-option
+                    v-for="permission in permissionOptions"
+                    :label="permission.label"
+                    :value="permission.value"
+                  />
+                </el-select>
+              </div>
+              <div class="w-40">
+                <el-select
+                  ref="select2"
+                  clearable
+                  size="small"
+                  :model-value="summaryPrompt.date"
+                  @change="(value: any) => (summaryPrompt.date = value)"
+                >
+                  <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
+                  <el-option label="自定义" value="ignore" disabled>
+                    <div class="flex gap-2 w-80">
+                      <el-date-picker
+                        size="small"
+                        :clearable="false"
+                        type="daterange"
+                        class="w-40"
+                        v-model="summaryPrompt.sliceDate"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                      />
+                      <el-button
+                        size="small"
+                        type="primary"
+                        @click="
+                          () => {
+                            select2?.blur();
+                            summaryPrompt.date = 'ignore';
+                          }
+                        "
+                        >确认</el-button
+                      >
+                    </div>
+                  </el-option>
+                </el-select>
+              </div>
+            </div>
+            <Divider title="客户汇总" />
+            <div class="my-6 grid grid-cols-4 gap-2">
+              <SimpleCard
+                title="新增客户"
+                unit="个"
+                :number="requestData.summary?.customDataSummary.newNum"
+              />
+              <SimpleCard
+                title="转成交客户"
+                unit="个"
+                :number="requestData.summary?.customDataSummary.closeDealNum"
+                v-if="false"
+              />
             </div>
-            <div class="w-40">
-              <el-select
-                ref="select3"
-                clearable
-                size="small"
-                :model-value="stagePrompt.date"
-                @change="(value: any) => (stagePrompt.date = value)"
-              >
-                <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
-                <el-option label="自定义" value="ignore" disabled>
-                  <div class="flex gap-2 w-80">
-                    <el-date-picker
-                      size="small"
-                      :clearable="false"
-                      type="daterange"
-                      class="w-12"
-                      v-model="stagePrompt.sliceDate"
-                      start-placeholder="开始日期"
-                      end-placeholder="结束日期"
-                    />
-                    <el-button
-                      size="small"
-                      type="primary"
-                      @click="
-                        () => {
-                          select3?.blur();
-                          stagePrompt.date = 'ignore';
-                        }
-                      "
-                      >确认</el-button
-                    >
-                  </div>
-                </el-option>
-              </el-select>
+            <Divider :title="`${businessLabel}汇总`" />
+            <div class="my-6 grid grid-cols-4 gap-2">
+              <SimpleCard
+                :title="`新增${businessLabel}`"
+                unit="个"
+                :number="requestData.summary?.businessOpportunityDataSummary.newNum"
+              />
+              <SimpleCard
+                :title="`赢单${businessLabel}`"
+                unit="个"
+                :number="requestData.summary?.businessOpportunityDataSummary.winning"
+              />
+              <SimpleCard
+                :title="`输单${businessLabel}`"
+                unit="个"
+                :number="requestData.summary?.businessOpportunityDataSummary.losting"
+              />
+              <SimpleCard
+                :title="`${businessLabel}总金额`"
+                unit="元"
+                :number="requestData.summary?.businessOpportunityDataSummary.allAmountOfMoney"
+              />
+            </div>
+            <Divider title="线索汇总" />
+            <div class="my-6 grid grid-cols-4 gap-2">
+              <SimpleCard
+                title="新增线索"
+                unit="个"
+                :number="requestData.summary?.clueDataSummary.newNum"
+              />
+              <SimpleCard
+                :title="`线索转${businessLabel}`"
+                unit="个"
+                :number="requestData.summary?.clueDataSummary.changeNum"
+              />
             </div>
           </div>
-          <div class="h-60">
-            <Echarts :option="chartOptions"></Echarts>
+          <div class="border-gray-200 border rounded p-3 flex-1">
+            <div class="text-sm font-medium">{{ `${businessLabel}阶段` }}</div>
+            <div class="flex gap-3 mb-8 mt-2">
+              <div class="w-40">
+                <el-select
+                  clearable
+                  size="small"
+                  :model-value="stagePrompt.permission"
+                  @change="(value: any) => (stagePrompt.permission = value)"
+                >
+                  <el-option
+                    v-for="permission in permissionOptions"
+                    :label="permission.label"
+                    :value="permission.value"
+                  />
+                </el-select>
+              </div>
+              <div class="w-40">
+                <el-select
+                  ref="select3"
+                  clearable
+                  size="small"
+                  :model-value="stagePrompt.date"
+                  @change="(value: any) => (stagePrompt.date = value)"
+                >
+                  <el-option v-for="date in dateOptions" :label="date.label" :value="date.value" />
+                  <el-option label="自定义" value="ignore" disabled>
+                    <div class="flex gap-2 w-80">
+                      <el-date-picker
+                        size="small"
+                        :clearable="false"
+                        type="daterange"
+                        class="w-40"
+                        v-model="stagePrompt.sliceDate"
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                      />
+                      <el-button
+                        size="small"
+                        type="primary"
+                        @click="
+                          () => {
+                            select3?.blur();
+                            stagePrompt.date = 'ignore';
+                          }
+                        "
+                        >确认</el-button
+                      >
+                    </div>
+                  </el-option>
+                </el-select>
+              </div>
+            </div>
+            <div class="h-60">
+              <Echarts :option="chartOptions"></Echarts>
+            </div>
           </div>
         </div>
       </div>
+      
+      <!-- Right side - AI Chat -->
+      <div class="w-1/2">
+        <AIChat />
+      </div>
     </section>
   </div>
 </template>

+ 77 - 42
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts

@@ -3,54 +3,89 @@ export const MODURL = "Business";
 export const prefix = "/clue";
 export const GETSYSFILED = "/sys-dict/getListByCode";
 export const GETPERSONNEL = "/user/getSimpleActiveUserList";
-export const GETGENERATEFOEM = `/sys-form/getListByCode${MOD}`
-export const GETBUSINESSLIST = `/business-opportunity/list`
-export const UPDATEINSET = `/business-opportunity/insertAndUpdate`
-export const BUSINESSDETELE = `/business-opportunity/delete`
-export const BATCHTRANSFER = `/business-opportunity/claim`
-export const BUSIESS_DETELELIST = `/business-opportunity/deleterList`
-export const BUSIESS_ROWBACK = `/business-opportunity/rollBack`
-export const BUSIESS_PERDETELE = `/business-opportunity/deleterDelete`
-export const BUSIESS_GETSATE = `/business-opportunity/getStage`
-export const BUSIESS_SAVESAIE = `/business-opportunity/saveStage`
-export const BUSIESS_ALL = `/business-opportunity/getAll`
-export const BUSIESS_INFO = `/business-opportunity/getInfo`
-export const DETELEFILEFILE = `/business-opportunity/deleteFile`
-export const REFIENAMEFILE = `/business-opportunity/reFileName`
-export const UPLOADFILEFILE = `/business-opportunity/uploadFile`
-export const URL_IMPOERBUSINESS = `/business-opportunity/importData`
-export const URL_DETELESTAGE = `/business-opportunity/deleteStage`
-export const URL_SAVECONTACT = `/business-opportunity/saveContactsId`
-export const URL_STAGEIDNEXT = `/business-opportunity/saveStageId`
-export const URL_SAVEREASON = `/business-opportunity/saveReason`
-export const URL_EXPORTBUSINESS = `/business-opportunity/exportData`
-export const PANEL_MOBILE_DATA = `/business-opportunity/changeOrder`
+export const GETGENERATEFOEM = `/sys-form/getListByCode${MOD}`;
+export const GETBUSINESSLIST = `/business-opportunity/list`;
+export const UPDATEINSET = `/business-opportunity/insertAndUpdate`;
+export const BUSINESSDETELE = `/business-opportunity/delete`;
+export const BATCHTRANSFER = `/business-opportunity/claim`;
+export const BUSIESS_DETELELIST = `/business-opportunity/deleterList`;
+export const BUSIESS_ROWBACK = `/business-opportunity/rollBack`;
+export const BUSIESS_PERDETELE = `/business-opportunity/deleterDelete`;
+export const BUSIESS_GETSATE = `/business-opportunity/getStage`;
+export const BUSIESS_SAVESAIE = `/business-opportunity/saveStage`;
+export const BUSIESS_ALL = `/business-opportunity/getAll`;
+export const BUSIESS_INFO = `/business-opportunity/getInfo`;
+export const DETELEFILEFILE = `/business-opportunity/deleteFile`;
+export const REFIENAMEFILE = `/business-opportunity/reFileName`;
+export const UPLOADFILEFILE = `/business-opportunity/uploadFile`;
+export const URL_IMPOERBUSINESS = `/business-opportunity/importData`;
+export const URL_DETELESTAGE = `/business-opportunity/deleteStage`;
+export const URL_SAVECONTACT = `/business-opportunity/saveContactsId`;
+export const URL_STAGEIDNEXT = `/business-opportunity/saveStageId`;
+export const URL_SAVEREASON = `/business-opportunity/saveReason`;
+export const URL_EXPORTBUSINESS = `/business-opportunity/exportData`;
+export const PANEL_MOBILE_DATA = `/business-opportunity/changeOrder`;
 
 // 看板视图
-export const OBTAIN_KANBAN_VIEW_DATA = `/business-opportunity/getAllByStage`
+export const OBTAIN_KANBAN_VIEW_DATA = `/business-opportunity/getAllByStage`;
 
 // 看板类型
-export const TABLE_VIEW = 'table'
-export const KANBAN_VIEW = 'view'
-
+export const TABLE_VIEW = "table";
+export const KANBAN_VIEW = "view";
 
 export const stageStatus = [
-    { id: 1, name: "赢单", progress: "100%" },
-    { id: 2, name: "输单", progress: "0%" },
-    { id: 3, name: "无效", progress: "0%" }
-]
+  { id: 1, name: "赢单", progress: "100%" },
+  { id: 2, name: "输单", progress: "0%" },
+  { id: 3, name: "无效", progress: "0%" },
+];
 
 const isExistBusiness = sessionStorage.getItem("isExistBusiness");
-const businessLabel = isExistBusiness === "1" ? "商机" : "项目"; 
+const businessLabel = isExistBusiness === "1" ? "商机" : "项目";
+const isSimple = sessionStorage.getItem("isSimple");
 
-export const tableColumn: businessTableColumnInterface[] = [
-    { prop: "name", label: `${businessLabel}名称`, width: "180", eventName: "toClueTableDetail" },
-    { prop: "customerName", label: "客户名称", width: "180" },
-    { prop: "contactsName", label: "联系人", width: "180", eventName: "showName" },
-    { prop: "amountOfMoney", label: `${businessLabel}金额`, width: "180" },
-    { prop: "expectedTransactionDate", label: "预计成交时间", width: "180" },
-    { prop: "stageValue", label: `${businessLabel}阶段`, width: "180" },
-    { prop: "inchargerName", label: "负责人", width: "180" },
-    { prop: "creatorName", label: "创建人", width: "180" },
-    { prop: "createTime", label: "创建时间", width: "180" }
-]
+export const tableColumn: businessTableColumnInterface[] =
+  isSimple == "1"
+    ? [
+        {
+          prop: "name",
+          label: `${businessLabel}名称`,
+          width: "180",
+          eventName: "toClueTableDetail",
+        },
+        { prop: "customerName", label: "客户名称", width: "180" },
+        { prop: "amountOfMoney", label: `${businessLabel}金额`, width: "180" },
+        {
+          prop: "expectedTransactionDate",
+          label: "预计成交时间",
+          width: "180",
+        },
+        { prop: "stageValue", label: `${businessLabel}阶段`, width: "180" },
+        { prop: "inchargerName", label: "负责人", width: "180" },
+        { prop: "creatorName", label: "创建人", width: "180" },
+        { prop: "createTime", label: "创建时间", width: "180" },
+      ]
+    : [
+        {
+          prop: "name",
+          label: `${businessLabel}名称`,
+          width: "180",
+          eventName: "toClueTableDetail",
+        },
+        { prop: "customerName", label: "客户名称", width: "180" },
+        {
+          prop: "contactsName",
+          label: "联系人",
+          width: "180",
+          eventName: "showName",
+        },
+        { prop: "amountOfMoney", label: `${businessLabel}金额`, width: "180" },
+        {
+          prop: "expectedTransactionDate",
+          label: "预计成交时间",
+          width: "180",
+        },
+        { prop: "stageValue", label: `${businessLabel}阶段`, width: "180" },
+        { prop: "inchargerName", label: "负责人", width: "180" },
+        { prop: "creatorName", label: "创建人", width: "180" },
+        { prop: "createTime", label: "创建时间", width: "180" },
+      ];

+ 2 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/kanbanView.vue

@@ -9,6 +9,7 @@ import { post, get, uploadFile } from "@/utils/request";
 import SvgIcon from "@/components/svgIcon/index.vue";
 import { formatDate, formatDateTime } from "@/utils/times";
 
+const isSimple = sessionStorage.getItem("isSimple");
 const emit = defineEmits()
 const router = useRouter()
 const viewList = ref<viewListInterface[]>([])
@@ -176,7 +177,7 @@ defineExpose({
                 <SvgIcon name="kehu" :size="20" class="mr-2" />
                 {{ subItem.customerName }}
               </div>
-              <div class="flex items-center mt-4">
+              <div class="flex items-center mt-4" v-if="isSimple != '1'">
                 <SvgIcon name="lianxiren" :size="20" color="#606266" class="mr-2" />
                 {{ subItem.contactsName }}
               </div>

+ 2 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -15,7 +15,7 @@
             <el-form-item label="客户名称">
               <el-input v-model="businessOpportunityForm.customerName" clearable placeholder="请输入"></el-input>
             </el-form-item>
-            <el-form-item label="联系人">
+            <el-form-item label="联系人" v-if="isSimple != '1'">
               <el-input v-model="businessOpportunityForm.contactsName" clearable placeholder="请输入"></el-input>
             </el-form-item>
             <el-form-item label="产品">
@@ -225,6 +225,7 @@ import kanbanView from "./component/kanbanView.vue";
 
 const isExistBusiness = sessionStorage.getItem("isExistBusiness");
 const businessLabel = isExistBusiness === "1" ? "商机" : "项目"; 
+const isSimple = sessionStorage.getItem("isSimple");
 const router = useRouter()
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const businessTableRef = ref<InstanceType<typeof ElTable>>() // 商机table dom

+ 4 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/deteleTables.vue

@@ -20,7 +20,7 @@
                     <el-table-column type="selection" width="55" />
                     <el-table-column prop="customName" label="客户名称" width="180"></el-table-column>
                     <el-table-column prop="customSourceValue" label="客户来源" width="180"></el-table-column>
-                    <el-table-column prop="companyPhone" label="公司电话" width="180"></el-table-column>
+                    <el-table-column prop="companyPhone" :label="isSimpleLabel" width="180"></el-table-column>
                     <el-table-column prop="email" label="邮箱" width="200"></el-table-column>
                     <el-table-column prop="customerIndustryValue" label="客户行业" width="180"></el-table-column>
                     <el-table-column prop="customerLevelValue" label="客户级别" width="180"></el-table-column>
@@ -63,6 +63,9 @@ import { formatDate } from '@/utils/times';
 
 type operationType = '恢复' | '删除'
 
+const isSimple = sessionStorage.getItem("isSimple");
+const isSimpleLabel = isSimple == "1" ? "联系人电话" : "公司电话"; 
+
 const emits = defineEmits(['closeVisible']);
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const deteleBusinessTable = ref([])

+ 6 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue

@@ -3,7 +3,7 @@
     <div class="p-5 w-80 pr-0">
       <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
         <div class="flex-1 p-3 overflow-y-auto">
-          <el-form :model="customerCriteriaForm" label-width="70px" style="max-width: 600px">
+          <el-form :model="customerCriteriaForm" label-width="90px" style="max-width: 600px">
             <el-form-item label="客户名称">
               <el-input v-model="customerCriteriaForm.customName" clearable placeholder="请输入"></el-input>
             </el-form-item>
@@ -12,7 +12,7 @@
                 <el-option v-for="item in fixedData.CustomSources" :key="item.id" :label="item.name" :value="item.id" />
               </el-select>
             </el-form-item>
-            <el-form-item label="公司号码">
+            <el-form-item :label="isSimpleLabel">
               <el-input v-model="customerCriteriaForm.companyPhone" clearable placeholder="请输入"></el-input>
             </el-form-item>
             <el-form-item label="邮箱">
@@ -76,7 +76,7 @@
               </template>
             </el-table-column>
             <el-table-column prop="customSourceValue" label="客户来源" width="180"></el-table-column>
-            <el-table-column prop="companyPhone" label="公司电话" width="180"></el-table-column>
+            <el-table-column prop="companyPhone" :label="isSimpleLabel" width="180"></el-table-column>
             <el-table-column prop="email" label="邮箱" width="200"></el-table-column>
             <el-table-column prop="customerIndustryValue" label="客户行业" width="180"></el-table-column>
             <el-table-column prop="customerLevelValue" label="客户级别" width="180" sortable="custom"></el-table-column>
@@ -231,6 +231,9 @@ interface customerCriteriaFormType { // 线索筛选条件类型
   pageFrom: string | number
 }
 
+const isSimple = sessionStorage.getItem("isSimple");
+const isSimpleLabel = isSimple == "1" ? "联系人电话" : "公司电话"; 
+
 // 定义变量
 const router = useRouter()
 const globalPopup = inject<GlobalPopup>('globalPopup')

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue

@@ -148,6 +148,7 @@ const loginLogic = (data: any) => {
   })
   data.moduleList = data.moduleList.filter((item: any) => item.path != '/corpreport')
   sessionStorage.setItem('isExistBusiness', data.company.isExistBusiness || 0)
+  sessionStorage.setItem('isSimple', data.company.isSimple || 0)
   sessionStorage.setItem('token', data.id)
   localStorage.setItem('SUPERSONIC_TOKEN', data.supersonicToken)
   setValue(data, 'userInfo')

+ 24 - 9
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/index.vue

@@ -7,7 +7,7 @@
             <el-form-item label="任务名称:" label-width="7em" prop="taskName">
               <el-input v-model="searchForm.taskName" placeholder="请输入" />
             </el-form-item>
-            <el-form-item label="优先级:" label-width="7em" prop="priority">
+            <el-form-item label="优先级:" label-width="7em" prop="priority" v-if="userInfo.company.isSimple != 1">
               <el-select v-model="searchForm.priority" placeholder="请选择">
                 <el-option v-for="item in PRIORITY" :key="item.value" :value="item.value" :label="item.label" />
               </el-select>
@@ -27,7 +27,7 @@
             <el-form-item label="销售订单:" label-width="7em" prop="orderName">
               <el-input v-model="searchForm.orderName" placeholder="请输入" />
             </el-form-item>
-            <el-form-item label="线索名称:" label-width="7em" prop="clueName">
+            <el-form-item label="线索名称:" label-width="7em" prop="clueName" v-if="userInfo.company.isSimple != 1">
               <el-input v-model="searchForm.clueName" placeholder="请输入" />
             </el-form-item>
             <el-form-item label="任务状态:" label-width="7em" prop="status">
@@ -75,7 +75,7 @@
               <el-table-column prop="taskName" label="任务名称" header-align="center" align="center" show-overflow-tooltip
                 width="200" />
               <el-table-column prop="priority" label="优先级" width="100" :sortable="true" header-align="center"
-                align="center">
+                align="center" v-if="userInfo.company.isSimple != 1">
                 <template #default="scope">
                   {{ PRIORITY.find(item => item.value == scope.row.priority)?.label }}
                 </template>
@@ -111,7 +111,7 @@
                   </el-link>
                 </template>
               </el-table-column>
-              <el-table-column prop="businessName" :label="`${businessLabel}名称`" header-align="center" align="center" width="200">
+              <el-table-column prop="businessName" :label="`${businessLabel}名称`" header-align="center" align="center" width="200" v-if="userInfo.company.isSimple != 1">
                 <template #default="scope">
                   <el-link :underline="false" type="primary"
                     @click="goDetail(scope.row, 'business', 'businessOpportunityId')">
@@ -119,29 +119,43 @@
                   </el-link>
                 </template>
               </el-table-column>
-              <el-table-column prop="orderName" label="销售订单" header-align="center" align="center" width="200">
+              <el-table-column prop="orderName" label="销售订单" header-align="center" align="center" width="200" v-if="userInfo.company.isSimple != 1">
                 <template #default="scope">
                   <el-link :underline="false" type="primary" @click="goDetail(scope.row, 'order', 'orderId')">
                     {{ scope.row.orderName }}
                   </el-link>
                 </template>
               </el-table-column>
-              <el-table-column prop="clueName" label="线索名称" header-align="center" align="center" width="200">
+              <el-table-column prop="clueName" label="线索名称" header-align="center" align="center" width="200" v-if="userInfo.company.isSimple != 1">
                 <template #default="scope">
                   <el-link :underline="false" type="primary" @click="goDetail(scope.row, 'thread', 'clueId')">
                     {{ scope.row.clueName }}
                   </el-link>
                 </template>
               </el-table-column>
-              <el-table-column prop="contactsName" label="联系人名称" header-align="center" align="center" width="120">
+              <el-table-column prop="contactsName" label="联系人名称" header-align="center" align="center" width="120" v-if="userInfo.company.isSimple != 1">
                 <template #default="scope">
                   <el-link :underline="false" type="primary" @click="goDetail(scope.row, 'contacts', 'contactsId')">
                     {{ scope.row.contactsName }}
                   </el-link>
                 </template>
               </el-table-column>
-              <el-table-column prop="contactsTel" label="联系人号码" header-align="center" align="center" width="140" />
+              <template v-if="userInfo.company.isSimple == 1">
+                <el-table-column prop="contactsTel" label="联系人号码" header-align="center" align="center" width="140" />
+                <el-table-column prop="appointContent" label="预约工作内容" header-align="center" align="center" width="140" />
+                <el-table-column prop="appointMoney" label="预约金额" header-align="center" align="center" width="140" />
   
+                <el-table-column prop="reallyContent" label="实际工作内容" header-align="center" align="center" width="140" />
+                <el-table-column prop="reallyMoney" label="实际金额" header-align="center" align="center" width="140" />
+                <el-table-column prop="payType" label="付款状态" header-align="center" align="center" width="140">
+                  <template #default="scope">
+                    <el-text :type="scope.row.payType == 1 ? 'success' : 'danger'">
+                      {{ scope.row.payType == 1 ? '已付款' : '未付款' }}
+                    </el-text>
+                  </template>
+                </el-table-column>
+                <el-table-column prop="userBack" label="用户反馈" header-align="center" align="center" width="180" />
+              </template>
               <el-table-column fixed="right" label="操作" header-align="center" align="center" width="160" v-permission="['tasksEdit']">
   
                 <template #default="scope">
@@ -226,7 +240,7 @@ import { getFromValue, confirmAction, downloadFile, createTaskFromType } from '@
 import { tableShowOverflowTooltip } from '@/utils/globalVariables'
 import { pushMap } from './type';
 const router = useRouter()
-const { getFunctionList } = useStore()
+const { getFunctionList, userInfo } = useStore()
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const pagePermission = ref<any[]>();
 const taskModalVisible = ref(false);
@@ -284,6 +298,7 @@ function submitForm(data: any, isClose: boolean) {
     }
   }
   delete params.taskLogs
+  delete params.taskExecutors
   taskLoading.value = "2";
   let url = isEdit.value ? UPDATE_TASK : ADD_TASK
   let msg = isEdit.value ? "修改成功" : "新建成功"

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/index.vue

@@ -39,6 +39,7 @@
           </el-select>
         </div>
         <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1" @click="transitionOperation('exportUser', '')" v-permission="['teamExport']">导出人员</el-button>
+        <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1" @click="dialogFrom.newSyncWithCorpWxDayloadVisable = true">同步企微通讯录</el-button>
         <el-dropdown v-if="userInfo.userNameNeedTranslate != 1">
           <el-button type="primary">
             更多操作<el-icon class="el-icon--right"><arrow-down /></el-icon>
@@ -48,7 +49,6 @@
               <el-dropdown-item @click="addPersone(false)" v-permission="['teamAdd']">添加人员</el-dropdown-item>
               <el-dropdown-item @click="transitionOperation('exportUser', '')" v-permission="['teamExport']">导出人员</el-dropdown-item>
               <el-dropdown-item @click="transitionOperation('importUser', '')" v-permission="['teamImport']">批量导入</el-dropdown-item>
-              <el-dropdown-item v-if="userInfo.userNameNeedTranslate == 1" @click="dialogFrom.newSyncWithCorpWxDayloadVisable = true">同步企微通讯录</el-dropdown-item>
             </el-dropdown-menu>
           </template>
         </el-dropdown>

+ 21 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/test/aitest.vue

@@ -0,0 +1,21 @@
+<template>
+  <div class="m-5 bg-white min-h-full p-4 rounded">
+    <h1 class="text-xl mb-4">AI Chat Test Page</h1>
+    <div class="flex gap-4">
+      <div class="w-1/2">
+        <div class="border-gray-200 border rounded p-3">
+          <div class="text-sm font-medium mb-3">Original Content</div>
+          <p>This is a test page to verify the AI Chat component.</p>
+        </div>
+      </div>
+      
+      <div class="w-1/2">
+        <AIChat />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import AIChat from '../analysis/components/AIChat.vue';
+</script>

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts

@@ -43,6 +43,11 @@ export const routes: RouteRecordRaw[] = [
     path: "/testEcharts",
     component: () => import("../pages/test/echarts.vue"),
   },
+  {
+    name: "aitest",
+    path: "/test/aitest",
+    component: () => import("../pages/test/aitest.vue"),
+  },
 ];
 
 const router = createRouter({

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/router/routerGuards.ts

@@ -9,7 +9,7 @@ export function createRouterGuards(router: Router) {
     const routers = router.getRoutes();
     const { setAsyncRoutesMark, asyncRoutesMark, getToken } = useStore();
     const token = getToken;
-    const skipPath = ["/login", "/register", "/test", "/testEcharts"];
+    const skipPath = ["/login", "/register", "/test", "/testEcharts", "/test/aitest"];
     if (skipPath.includes(to.path)) {
       next();
     } else {
@@ -81,4 +81,4 @@ export function createRouterGuards(router: Router) {
   return {
     beforeEach,
   };
-}
+}

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts

@@ -75,7 +75,7 @@ export async function post(url: string, data?: any): Promise<any> {
     instance
       .post(url, data, {
         headers: {
-          "Content-type": " application/x-www-form-urlencoded; charset=UTF-8",
+          "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
         },
       })
       .then(({ data }: any) => {

+ 6 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/tsconfig.json

@@ -24,7 +24,12 @@
     "paths": {
       "@/*": ["./src/*"]
     },
-    "types": ["element-plus/global"]
+    "types": ["element-plus/global"],
+    "typeRoots": [
+      "./node_modules/@types",
+      "./src"
+    ],
+    "baseUrl": "."
   },
   "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
   "references": [{ "path": "./tsconfig.node.json" }]

+ 5 - 0
fhKeeper/formulahousekeeper/management-crm/pom.xml

@@ -194,6 +194,11 @@
             <artifactId>spring-boot-devtools</artifactId>
             <version>2.0.1.RELEASE</version>
         </dependency>
+         <dependency>
+             <groupId>org.apache.velocity</groupId>
+             <artifactId>velocity-engine-core</artifactId>
+             <version>2.0</version>
+         </dependency>
 
     </dependencies>
 

+ 15 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CompanyController.java

@@ -2,6 +2,7 @@ package com.management.platform.controller;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.*;
@@ -17,7 +18,6 @@ import org.springframework.web.bind.annotation.RestController;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -49,6 +49,9 @@ public class CompanyController {
     @Resource
     private ExpenseMainTypeService expenseMainTypeService;
 
+    @Resource
+    private SysFormMapper sysFormMapper;
+
     @RequestMapping("/testTimeout")
     public HttpRespMsg testTimeout(){
         HttpRespMsg msg = new HttpRespMsg();
@@ -224,5 +227,16 @@ public class CompanyController {
         return msg;
     }
 
+    @RequestMapping("/editCompanyToSimpleAndSysFormConfig")
+    public HttpRespMsg editCompanyToSimpleAndSysFormConfig(Integer simpleMode ,String config,HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("token");
+        User user = userMapper.selectById(token);
+        Company company = companyMapper.selectById(user.getCompanyId());
+        companyMapper.update(null,new UpdateWrapper<Company>().set("is_simple",simpleMode).eq("id",company.getId()));
+        sysFormMapper.update(null,new UpdateWrapper<SysForm>().set("config",config).eq("company_id",company.getId()).eq("code","tasks"));
+        return msg;
+    }
+
 }
 

+ 43 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SysFormController.java

@@ -1,6 +1,8 @@
 package com.management.platform.controller;
 
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.management.platform.entity.Company;
 import com.management.platform.entity.SysForm;
@@ -8,9 +10,11 @@ import com.management.platform.mapper.CompanyMapper;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.SysFormService;
 import com.management.platform.util.HttpRespMsg;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
-import javax.annotation.Priority;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.util.List;
@@ -73,6 +77,43 @@ public class SysFormController {
                 }
             }
         }
+        if (!sysFormList.isEmpty()&&company.getIsSimple()==1){
+//            String customerNameKey = "客户名称";
+//            String contactNameKey = "联系人姓名";
+            String companyPhoneKey = "公司电话";
+            String contactPhoneKey = "联系人电话";
+
+            sysFormList.stream()
+                    .filter(sysForm -> "Customer".equals(sysForm.getCode()))
+                    .forEach(sysForm -> {
+                        String config = sysForm.getConfig();
+                        String result = config
+//                                .replace(customerNameKey, contactNameKey)
+                                .replace(companyPhoneKey, contactPhoneKey);
+                        sysForm.setConfig(result);
+                    });
+            for (SysForm sysForm : sysFormList) {
+                if (sysForm.getCode().equals("business")){
+                    String config = sysForm.getConfig();
+                    JSONObject jsonObject = JSONArray.parseObject(config);
+                    JSONArray list = jsonObject.getJSONArray("list");
+                    JSONObject object1 = list.getJSONObject(1);
+                    JSONArray columns = object1.getJSONArray("columns");
+
+                    // 删除下标为 1 的 JSON 对象,确保下标有效
+                    if (columns.size() > 1) {
+                        columns.remove(1);
+                    }
+
+                    // 更新 columns
+                    object1.put("columns", columns);
+                    list.set(1, object1); // 更新 list 中的对象
+                    jsonObject.put("list", list); // 更新 jsonObject 中的 list
+                    sysForm.setConfig(jsonObject.toJSONString()); // 更新 sysForm 的配置
+
+                }
+            }
+        }
         msg.setData(sysFormList);
         return msg;
     }

+ 6 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Company.java

@@ -157,6 +157,12 @@ public class Company extends Model<Company> {
     @TableField("version_control")
     private Integer versionControl;
 
+    /**
+     * 默认0, 1表示简易模式
+     */
+    @TableField("is_simple")
+    private Integer isSimple;
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 54 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Task.java

@@ -11,6 +11,7 @@ import lombok.experimental.Accessors;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.Date;
@@ -268,6 +269,59 @@ public class Task extends Model<Task> {
     @TableField(exist = false)
     private List<String> executorNames;
 
+    /**
+     * 客户类型 0居民住家/ 1商业区/ 2其他类型
+     */
+    @TableField("custom_type")
+    private Integer customType;
+
+    /**
+     * 预约工作内容
+     */
+    @TableField("appoint_content")
+    private String appointContent;
+
+    /**
+     * 预约金额
+     */
+    @TableField("appoint_money")
+    private BigDecimal appointMoney;
+
+    /**
+     * 实际工作内容
+     */
+    @TableField("really_content")
+    private String reallyContent;
+
+    /**
+     * 实际金额
+     */
+    @TableField("really_money")
+    private BigDecimal reallyMoney;
+
+    /**
+     * 付款状态 0未付款 ,1付款
+     */
+    @TableField("pay_type")
+    private Integer payType;
+
+    /**
+     * 付款方式 0Paynow, 1Cash, 2Wechat
+     */
+    @TableField("pay_way")
+    private Integer payWay;
+
+    /**
+     * 用户反馈
+     */
+    @TableField("user_back")
+    private String userBack;
+
+    /**
+     * 备注
+     */
+    @TableField("remark")
+    private String remark;
 
     @Override
     protected Serializable pkVal() {

+ 4 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/dto/TaskDto.java

@@ -27,6 +27,10 @@ public class TaskDto extends Task {
     private String clueName;
     private String phone;
 
+    private String customTypeName;//客户类型名称
+    private String payTypeName;//付款状态
+    private String payWayName;//付款方式名称
+
     private Integer departmentId;
     private String departmentIdByMyselfOrNull;
 

+ 8 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/TasKVo.java

@@ -1,13 +1,12 @@
 package com.management.platform.entity.vo;
 
-import com.management.platform.entity.*;
+import com.management.platform.entity.Task;
+import com.management.platform.entity.TaskLog;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 @EqualsAndHashCode(callSuper = true)
 @Data
@@ -28,6 +27,12 @@ public class TasKVo extends Task {
     private String contactsName;
     private String contactsPhone;
 
+    private String customTypeName;//客户类型名称
+    private String payTypeName;//付款状态
+    private String payWayName;//付款方式名称
+    private String companyPhone;
+    private String contactsTel;
+
 
     private List<String> taskExecutors;
 

+ 6 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/AIQuestionServiceImpl.java

@@ -32,6 +32,7 @@ import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.sql.DataSource;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -284,14 +285,17 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
             MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
             body.add("question", questionBO.getContent());
             ByteArrayResource byteArrayResource = null;
+            File f = new File(path,questionBO.getUrl());
             try {
-                MultipartFile multipartFileFromPath = getMultipartFileFromPath(questionBO.getUrl());
+                System.out.println("本地文件路径=="+f.getAbsolutePath());
+                MultipartFile multipartFileFromPath = getMultipartFileFromPath(f.getAbsolutePath());
                 byteArrayResource = new ByteArrayResource(multipartFileFromPath.getBytes()) {
                     @Override
                     public String getFilename() {
                         return multipartFileFromPath.getOriginalFilename();
                     }
                 };
+                System.out.println("字节长度=="+byteArrayResource.contentLength());
             }catch (IOException e){
                 e.printStackTrace();
             }
@@ -326,7 +330,7 @@ public class AIQuestionServiceImpl extends ServiceImpl<AIQuestionMapper, AIQuest
             HttpEntity<MultiValueMap<String, Object>> requestEntity =
                     new HttpEntity<>(body, headers);
             ResponseEntity<String> response = restTemplate.exchange(
-                    aiFileAskUrl,
+                    aiAskUrl,
                     HttpMethod.POST,
                     requestEntity,
                     String.class);

+ 13 - 17
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SysFormServiceImpl.java

@@ -4,35 +4,24 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.*;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.entity.Company;
+import com.management.platform.entity.SysForm;
 import com.management.platform.mapper.CompanyMapper;
 import com.management.platform.mapper.SysFormMapper;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.SysFormService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MessageUtils;
-import org.apache.poi.hssf.usermodel.*;
-import org.apache.poi.ss.usermodel.*;
-import org.apache.poi.ss.util.CellRangeAddressList;
-import org.apache.poi.xssf.streaming.SXSSFCell;
-import org.apache.poi.xssf.streaming.SXSSFRow;
-import org.apache.poi.xssf.streaming.SXSSFSheet;
-import org.apache.poi.xssf.streaming.SXSSFWorkbook;
-import org.apache.poi.xssf.usermodel.XSSFColor;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
-import java.io.File;
-import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -69,6 +58,7 @@ public class SysFormServiceImpl extends ServiceImpl<SysFormMapper, SysForm> impl
             msg.setError("当前表单未配置模板,请先完成模板配置");
             return msg;
         }
+
         if (!code.equals("Contract")) {
             String config = sysForm.getConfig();
             JSONObject configOb = JSON.parseObject(config);
@@ -89,7 +79,7 @@ public class SysFormServiceImpl extends ServiceImpl<SysFormMapper, SysForm> impl
                     heads.add(item.getString("label"));
                 }
             }
-            if (code.equals("Business")){
+            if (code.equals("Business")) {
                 for (int i = 0; i < heads.size(); i++) {
                     String s = heads.get(i);
                     if (s.contains("商机")) {
@@ -98,9 +88,15 @@ public class SysFormServiceImpl extends ServiceImpl<SysFormMapper, SysForm> impl
                     }
                 }
             }
-        }else {
-            Collections.addAll(heads,"合同编号","合同名称","合同金额","合同类型","计划开始时间","计划结束时间","部门","自定义字段","备注","是否已回款","回款日期","金额","是否已开票","开票日期","发票种类","税率","税额");
+            if (company.getIsSimple()==1 &&code.equals("Task")){
+                heads.clear();
+                Collections.addAll(heads, "任务名称", "执行人", "开始时间", "截止时间", "客户名称","客户类型", "预约工作内容", "预约金额", "实际工作内容", "实际金额", "用户反馈");
+            }
+        }
+        else {
+            Collections.addAll(heads, "合同编号", "合同名称", "合同金额", "合同类型", "计划开始时间", "计划结束时间", "部门", "自定义字段", "备注", "是否已回款", "回款日期", "金额", "是否已开票", "开票日期", "发票种类", "税率", "税额");
         }
+
         List<List<String>> allList = new ArrayList<>();
         allList.add(heads);
         String title;

Разлика између датотеке није приказан због своје велике величине
+ 926 - 537
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/TaskServiceImpl.java


+ 23 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java

@@ -328,6 +328,29 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
                     }
                 }*/
+                if (company.getIsSimple()==1){
+                    List<SysModule> collect = userVO.getModuleList().stream().filter(m -> !"联系人".equals(m.getName()) && !"销售订单".equals(m.getName())).collect(Collectors.toList());
+                    userVO.setModuleList(collect);
+                }
+                if (company.getIsExistBusiness()==0) {
+                    List<SysModule> moduleList = userVO.getModuleList();
+                    for (SysModule sysModule : moduleList) {
+                        sysModule.setName(sysModule.getName().replaceAll("商机","项目"));
+
+                        List<SysFunction> sysFunctionList = sysModule.getFunctionList();
+                        for (SysFunction function : sysFunctionList) {
+                            function.setName(function.getName().replaceAll("商机","项目"));
+                        }
+                        sysModule.setFunctionList(sysFunctionList);
+                    }
+                    userVO.setModuleList(moduleList);
+
+                    List<SysFunction> list = userVO.getFunctionList();
+                    for (SysFunction function : list) {
+                        function.setName(function.getName().replaceAll("商机","项目"));
+                    }
+                    userVO.setFunctionList(list);
+                }
                 httpRespMsg.data = userVO;
             }else {
                 httpRespMsg.setError(MessageUtils.message("user.pwdError"));

+ 52 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/VisitPlanServiceImpl.java

@@ -32,6 +32,14 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
 //    @Qualifier(value = "VisitPlanThreadPool")
 //    ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
+    private final static String customTypeName0="居民住家";
+    private final static String customTypeName1="商业区";
+    private final static  String customTypeName2="其他类型";
+
+
+    private final static String payWayName0="Paynow";
+    private final static String payWayName1="Cash";
+    private final static  String payWayName2="Wechat";
 
     @Resource
     private VisitPlanMapper visitPlanMapper;
@@ -402,6 +410,28 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
                     taskDto.setTaskLogs(collect);
                 }
             }
+            if (taskDto.getCustomType()!=null&&taskDto.getCustomType()==0){
+                //0居民住家/ 1商业区/ 2其他类型
+                taskDto.setCustomTypeName(customTypeName0);
+            } else if (taskDto.getCustomType() != null && taskDto.getCustomType() == 1) {
+                taskDto.setCustomTypeName(customTypeName1);
+            }else if (taskDto.getCustomType() != null && taskDto.getCustomType() == 2) {
+                taskDto.setCustomTypeName(customTypeName2);
+            }
+
+            if (taskDto.getPayType()!=null&&taskDto.getPayType()==0){
+                taskDto.setPayTypeName("未付款");
+            } else if (taskDto.getPayType()!=null&&taskDto.getPayType()==1) {
+                taskDto.setPayTypeName("已付款");
+            }
+
+            if (taskDto.getPayWay()!=null&&taskDto.getPayWay()==0){
+                taskDto.setPayWayName(payWayName0);
+            } else if (taskDto.getPayWay() != null && taskDto.getPayWay() == 1) {
+                taskDto.setPayWayName(payWayName1);
+            }else if (taskDto.getPayWay() != null && taskDto.getPayWay() == 2) {
+                taskDto.setPayWayName(payWayName2);
+            }
         }
 
         HashMap<String, Object> map = new HashMap<>();
@@ -483,6 +513,28 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
                     taskDto.setTaskLogs(collect);
                 }
             }
+            if (taskDto.getCustomType()!=null&&taskDto.getCustomType()==0){
+                //0居民住家/ 1商业区/ 2其他类型
+                taskDto.setCustomTypeName(customTypeName0);
+            } else if (taskDto.getCustomType() != null && taskDto.getCustomType() == 1) {
+                taskDto.setCustomTypeName(customTypeName1);
+            }else if (taskDto.getCustomType() != null && taskDto.getCustomType() == 2) {
+                taskDto.setCustomTypeName(customTypeName2);
+            }
+
+            if (taskDto.getPayType()!=null&&taskDto.getPayType()==0){
+                taskDto.setPayTypeName("未付款");
+            } else if (taskDto.getPayType()!=null&&taskDto.getPayType()==1) {
+                taskDto.setPayTypeName("已付款");
+            }
+
+            if (taskDto.getPayWay()!=null&&taskDto.getPayWay()==0){
+                taskDto.setPayWayName(payWayName0);
+            } else if (taskDto.getPayWay() != null && taskDto.getPayWay() == 1) {
+                taskDto.setPayWayName(payWayName1);
+            }else if (taskDto.getPayWay() != null && taskDto.getPayWay() == 2) {
+                taskDto.setPayWayName(payWayName2);
+            }
         }
 
         Map<LocalDate, List<TaskDto>> dailyTasks = new LinkedHashMap<>();

+ 0 - 7
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/task/TimingTask.java

@@ -49,8 +49,6 @@ public class TimingTask {
 
     @Value("${wx.template_report_fill}")
     public String TEMPLATE_REPORT_FILL;
-    @Value("${wx.template_project_deadline}")
-    public String TEMPLATE_PROJECT_DEADLINE;
     @Value("${wx.app_id}")
     public String appId;
     @Value("${wx.app_secret}")
@@ -68,11 +66,6 @@ public class TimingTask {
     @Value(value = "${upload.path}")
     private String path;
 
-    @Resource
-    private LeaveSheetMapper leaveSheetMapper;
-    @Resource
-    private ProjectMapper projectMapper;
-
     @Resource
     private WxCorpInfoMapper wxCorpInfoMapper;
 

+ 1 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/util/CodeGenerator.java

@@ -92,7 +92,7 @@ public class CodeGenerator {
 
         // 数据源配置
         DataSourceConfig dsc = new DataSourceConfig();
-        dsc.setUrl("jdbc:mysql://47.101.180.183:17089/man_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
+        dsc.setUrl("jdbc:mysql://1.94.62.58:17089/man_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8");
 //        dsc.setSchemaName("public");
         dsc.setDriverName("com.mysql.cj.jdbc.Driver");
         dsc.setUsername("root");

+ 2 - 5
fhKeeper/formulahousekeeper/management-crm/src/main/resources/application.yml

@@ -109,12 +109,9 @@ picrecongnize:
 # 智能客户管家公众号参数
 wx:
   template_report_fill: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
-  app_id: wx749c84daac654e1e
-  app_secret: aacbd046ec1c790836f4f684c96fe585
+  app_id: wx1c1d8fc81bc073a8
+  app_secret: 17ad07f90ee845f99f4c1605647ef755
   template_report_pass: dbMuR2v7lxXLwRaorIWQ4T6ilvn0vzqmDDkD_3ZsaXc
-  template_project_deadline: kY2Qzec64uOANNXA0yn-PV09ZnNjCeGXwWjTaVmQiLU
-  template_report_reject: TICiRkvCpF4NCbkPOjefXTpz7jXgpt0SZGkNjCMIt3M
-  template_report_week: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
 
 #钉钉参数配置
 dingding:

+ 3 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CompanyMapper.xml

@@ -25,11 +25,13 @@
         <result column="reg_from" property="regFrom" />
         <result column="non_project_simple" property="nonProjectSimple" />
         <result column="is_exist_business" property="isExistBusiness" />
+        <result column="version_control" property="versionControl" />
+        <result column="is_simple" property="isSimple" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_name, staff_count_max, expiration_date, set_meal, package_worktime, package_project, package_contract, package_oa, package_etimecard, package_expense, package_customer, package_engineering, package_simple, package_finance, package_provider, is_international, create_date, reg_from, non_project_simple, is_exist_business
+        id, company_name, staff_count_max, expiration_date, set_meal, package_worktime, package_project, package_contract, package_oa, package_etimecard, package_expense, package_customer, package_engineering, package_simple, package_finance, package_provider, is_international, create_date, reg_from, non_project_simple, is_exist_business, version_control, is_simple
     </sql>
 
 </mapper>

Разлика између датотеке није приказан због своје велике величине
+ 12 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskMapper.xml


+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java

@@ -240,8 +240,8 @@ public interface ReportMapper extends BaseMapper<Report> {
     @Select("SELECT report.id,report.creator_id,  report.`create_date`, report.`create_time`, group_id, task_group.`name`,report.project_id, task_group.`project_id` AS error_pid FROM report LEFT JOIN task_group ON task_group.id = report.`group_id` WHERE report.`company_id`=#{companyId} AND report.project_id <> task_group.`project_id`  AND create_date BETWEEN #{startDate} AND #{endDate} ORDER BY report.id DESC")
     List<Map<String, Object>> selectErrorGroupData(Integer companyId, String startDate, String endDate);
 
-    @Update("update report set state=2,reject_reason='工时与考勤不一致,请重新提交',reject_userid=#{rejectUserid},reject_username=#{rejectUsername} where create_date=#{createDate} and creator_id=#{userId}")
-    void denyReportWithUserAndCreateDate(String userId, String createDate,String rejectUserid, String rejectUsername);
+    @Update("update report set state=2,reject_reason=#{reason},reject_userid=#{rejectUserid},reject_username=#{rejectUsername} where create_date=#{createDate} and creator_id=#{userId}")
+    void denyReportWithUserAndCreateDate(String userId, String createDate,String rejectUserid, String rejectUsername, String reason);
 
     List<Map<String, Object>> getUserDailyWorkTimeReminder(Integer companyId,String startDate, String endDate,@Param("list") List<Integer> deptIds,Integer deptId,String leaderId);
 

+ 26 - 23
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -10284,32 +10284,35 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 information.setMsg(reason);
                 informationList.add(information);
 
-                //审批流里面增加驳回的记录
-                ReportAuditLog log = new ReportAuditLog();
-                log.setAuditChannel(1);
-                log.setCompanyId(companyId);
-                //log.setResult("驳回"+(StringUtils.isEmpty(reason)?"":"("+reason+")"));
-                log.setResult(MessageUtils.message("stages.reject")+(StringUtils.isEmpty(reason)?"":"("+reason+")"));
-                log.setUserId(operator.getId());
-                log.setUserName(operator.getName());
-                reportAuditLogMapper.insert(log);
-                //员工的日期
-                ReportAlogMembdate membdate = new ReportAlogMembdate();
-                membdate.setRlogId(log.getId());
-                membdate.setState(2);//驳回
-                membdate.setCreateDate(LocalDate.parse(date,df));
-                membdate.setUserId(curUserid);
-                membdate.setUserName(userName);
-                reportAlogMembdateMapper.insert(membdate);
-
                 //查询出日报
-                List<Report> rList = reportMapper.selectList(new QueryWrapper<Report>().eq("create_date", date).eq("user_id", curUserid));
-                saveDenyReportLog(rList, curUserid, userName, reason);
-                //todo: 增加客户操作记录
-                reportMapper.denyReportWithUserAndCreateDate(String.valueOf(e.get("userId")),String.valueOf(e.get("createDate")), operator.getId(), operator.getName());
+                List<Report> rList = reportMapper.selectList(new QueryWrapper<Report>().eq("create_date", date).eq("creator_id", curUserid));
+                if (rList.size() > 0) {
+                    //审批流里面增加驳回的记录
+                    ReportAuditLog log = new ReportAuditLog();
+                    log.setAuditChannel(1);
+                    log.setCompanyId(companyId);
+                    //log.setResult("驳回"+(StringUtils.isEmpty(reason)?"":"("+reason+")"));
+                    log.setResult(MessageUtils.message("stages.reject")+(StringUtils.isEmpty(reason)?"":"("+reason+")"));
+                    log.setUserId(operator.getId());
+                    log.setUserName(operator.getName());
+                    reportAuditLogMapper.insert(log);
+                    //员工的日期
+                    ReportAlogMembdate membdate = new ReportAlogMembdate();
+                    membdate.setRlogId(log.getId());
+                    membdate.setState(2);//驳回
+                    membdate.setCreateDate(LocalDate.parse(date,df));
+                    membdate.setUserId(curUserid);
+                    membdate.setUserName(userName);
+                    reportAlogMembdateMapper.insert(membdate);
+                    saveDenyReportLog(rList, curUserid, userName, reason);
+
+                    //todo: 增加客户操作记录
+                    reportMapper.denyReportWithUserAndCreateDate(String.valueOf(e.get("userId")),String.valueOf(e.get("createDate")), operator.getId(), operator.getName(), reason);
+                }
+
                 //发送企业微信消息
                 if(wxCorpInfo!=null&&e.get("corpwxUserId")!=null){
-                    wxCorpInfoService.sendWXCorpMsg(wxCorpInfo,String.valueOf(e.get("corpwxUserId")), "您在"+String.valueOf(e.get("createDate"))+"的日报考勤填报异常,请完成填报变更", null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_ABNOEMAL);
+                    wxCorpInfoService.sendWXCorpMsg(wxCorpInfo,String.valueOf(e.get("corpwxUserId")), reason, null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_ABNOEMAL);
                 }
             });
         }

+ 3 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -338,7 +338,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         try {
             log.info("发送企业微信消息===" + corpUserid);
             System.out.println("发送企业微信消息===" + corpUserid);
-//            if (isDev) return;
+            if (isDev) return;
             String accessToken = getCorpAccessToken(corpInfo);
             String url = URL_SEND_WXCORP_MSG.replaceAll("ACCESS_TOKEN", accessToken);
             HttpHeaders headers = new HttpHeaders();
@@ -1752,8 +1752,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         }
                         if (showLog) System.out.println("工作时长==" + workHours);
 
-                        if (corpInfo.getCompanyId() == 481) {
-                            //给盛立安元0.5单位进位
+                        if (corpInfo.getCompanyId() == 481 || corpInfo.getCompanyId() == 469) {
+                            //给盛立安元和赛元微电子0.5单位进位
                             ct.setWorkHours(DateTimeUtil.getHalfHoursFromDouble(workHours));
                         } else {
                             ct.setWorkHours(DateTimeUtil.getHoursFromDouble(workHours));

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserCorpwxTimeMapper.xml

@@ -31,7 +31,7 @@
     <select id="getUserDataList" resultType="java.util.HashMap" >
         SELECT user.id as userId, department.department_name as departmentName, DATE_FORMAT(a.create_date, '%Y/%m/%d') as createDate,a.start_time as startTime, a.end_time as endTime, a.work_hours as workHours, user.name as username,user.corpwx_userid as corpwxUserid,a.week_day as weekDay,
         week_day_txt as weekDayTxt,card_time as cardTime, outdoor_time as outdoorTime, ask_leave_time as askLeaveTime FROM user_corpwx_time a
-        LEFT JOIN user ON  user.name = a.name AND a.`company_id` = user.`company_id`
+        LEFT JOIN user ON  (user.name = a.name OR user.`corpwx_userid` = a.`corpwx_userid`) AND a.`company_id` = user.`company_id`
         left join department on department.department_id = user.department_id
         WHERE a.create_date BETWEEN #{startDate} AND #{endDate}
         AND a.company_id = #{companyId}