Quellcode durchsuchen

Merge remote-tracking branch 'origin/master'

yusm vor 5 Monaten
Ursprung
Commit
726774fe7c
57 geänderte Dateien mit 2386 neuen und 287 gelöschten Zeilen
  1. 18 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/scss/iframe.scss
  2. 44 27
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/pullDownSelector.vue
  3. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/translationComponent.vue
  4. 14 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js
  5. 61 35
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleDetails/index.vue
  6. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/addEditor.vue
  7. 59 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue
  8. 49 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/detail.vue
  9. 59 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/relatedBusinessOpportunities.vue
  10. 53 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/contactsInfo.vue
  11. 48 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/detail.vue
  12. 54 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/relatedContacts.vue
  13. 60 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contract/detail.vue
  14. 56 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/customerInfo.vue
  15. 60 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/detail.vue
  16. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/addEditor.vue
  17. 61 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/detail.vue
  18. 81 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/orderInfo.vue
  19. 69 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/relatedSalesOrders.vue
  20. 50 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/detail.vue
  21. 0 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/newAndModifiedRelatedProducts.vue
  22. 58 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/productInfo.vue
  23. 68 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/relatedProducts.vue
  24. 181 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/addEditor.vue
  25. 63 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/relatedTasks.vue
  26. 21 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/tasksInfo.vue
  27. 46 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/detail.vue
  28. 56 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/threadInfo.vue
  29. 31 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js
  30. 18 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java
  31. 14 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CustomController.java
  32. 39 30
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/VisitPlanController.java
  33. 20 9
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java
  34. 16 9
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Custom.java
  35. 3 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SysDict.java
  36. 5 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/VisitPlan.java
  37. 1 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/UserCommonModuleVO.java
  38. 45 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/VisitPlanDetailVO.java
  39. 10 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/VisitPlanVO.java
  40. 7 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/BusinessOpportunityMapper.java
  41. 7 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/CustomMapper.java
  42. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/VisitPlanMapper.java
  43. 6 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/BusinessOpportunityService.java
  44. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/CustomService.java
  45. 5 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/VisitPlanService.java
  46. 51 7
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java
  47. 48 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/CustomServiceImpl.java
  48. 1 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserCommonModuleServiceImpl.java
  49. 151 64
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/VisitPlanServiceImpl.java
  50. 4 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  51. 26 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanDelayHandler.java
  52. 133 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanInit.java
  53. 117 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml
  54. 170 0
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CustomMapper.xml
  55. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/UserCommonModuleMapper.xml
  56. 21 5
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/VisitPlanMapper.xml
  57. 38 32
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

+ 18 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/scss/iframe.scss

@@ -37,4 +37,22 @@ $themeColor: #075985;
   :deep(.van-field__label) {
     padding-left: 7.35px;
   }
+}
+
+/**
+ * van-cell 样式重置
+ * 详情页面 cell 展示重置
+ */
+.cellnormall {
+  :deep(.van-cell) {
+    padding: 0 0 14px 0;
+  }
+  :deep(.van-cell:after) {
+    border: 0;
+  }
+
+  :deep(.van-cell__title) {
+    flex: none;
+    width: 86px;
+  }
 }

+ 44 - 27
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/pullDownSelector.vue

@@ -5,47 +5,53 @@
         @update:model-value="debouncedSearchOptions" />
     </div>
     <div class="flex-1 my-2 overflow-y-auto">
-      <!-- 加载 -->
-      <template v-if="listLoading">
-        <van-skeleton :row="10" />
+      <template v-if="renderingOptions.length == 1 && renderingOptions[0] && !renderingOptions[0].label">
+        <van-empty />
       </template>
+
       <template v-else>
-        <template v-if="!multipleChoice">
-          <!-- 单选 -->
-          <van-radio-group v-model="selectChecked">
-            <template v-for="item in renderingOptions">
+        <!-- 加载 -->
+        <template v-if="listLoading">
+          <van-skeleton :row="10" />
+        </template>
+        <template v-else>
+          <template v-if="!multipleChoice">
+            <!-- 单选 -->
+            <van-radio-group v-model="selectChecked">
+              <template v-for="item in renderingOptions">
+                <van-cell-group inset>
+                  <van-cell clickable @click="selectChecked = item.value">
+                    <template #right-icon>
+                      <van-radio :name="item.value" />
+                    </template>
+                    <template #title>
+                      <TranslationComponent :openId="item.label" />
+                    </template>
+                  </van-cell>
+                </van-cell-group>
+              </template>
+            </van-radio-group>
+          </template>
+          <!-- 多选 -->
+          <template v-if="multipleChoice">
+            <van-checkbox-group v-model="selectChecked">
               <van-cell-group inset>
-                <van-cell clickable @click="selectChecked = item.value">
+                <van-cell v-for="(item, index) in renderingOptions" clickable :key="index" @click="toggle(index)">
                   <template #right-icon>
-                    <van-radio :name="item.value" />
+                    <van-checkbox :name="item.value" :ref="(el) => (checkboxRefs[index] = el)" @click.stop />
                   </template>
                   <template #title>
                     <TranslationComponent :openId="item.label" />
                   </template>
                 </van-cell>
               </van-cell-group>
-            </template>
-          </van-radio-group>
-        </template>
-        <!-- 多选 -->
-        <template v-if="multipleChoice">
-          <van-checkbox-group v-model="selectChecked">
-            <van-cell-group inset>
-              <van-cell v-for="(item, index) in renderingOptions" clickable :key="index" @click="toggle(index)">
-                <template #right-icon>
-                  <van-checkbox :name="item.value" :ref="(el) => (checkboxRefs[index] = el)" @click.stop />
-                </template>
-                <template #title>
-                  <TranslationComponent :openId="item.label" />
-                </template>
-              </van-cell>
-            </van-cell-group>
-          </van-checkbox-group>
+            </van-checkbox-group>
+          </template>
         </template>
       </template>
     </div>
     <div class="w-full pb-2 px-4">
-      <van-button type="primary" round class="w-full" :disabled="multipleChoice ? !selectChecked.length : !selectChecked" @click="confirmClick">确定</van-button>
+      <van-button type="primary" round class="w-full" :disabled="multipleChoice ? !selectChecked.length : (!selectChecked && selectChecked != 0)" @click="confirmClick">确定</van-button>
     </div>
   </div>
 </template>
@@ -88,6 +94,17 @@ const renderingOptions = ref([]);
 
 const debouncedSearchOptions = useDebounce(searchOptions, 500);
 
+watch(() => props.options, (newValue) => {
+  selectChecked.value = props.multipleChoice ? [] : null;
+  const isItAnArray = Array.isArray(newValue);
+  if (isItAnArray && newValue.length > 0) {
+    renderingOptions.value = manualCopying(newValue);
+    allOptions.value = manualCopying(newValue);
+  } else {
+    obtainPersonnelData();
+  }
+})
+
 function searchOptions(val) {
   listLoading.value = true
   if (!props.doYouNeedTranslation) {

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

@@ -20,7 +20,7 @@ import useInfoStore from '@store/useInfoStore'
 
 const props = defineProps({
   openId: {
-    type: [String, Number],
+    type: [String, Number, Array],
     default: () => '',
   },
   type: {

+ 14 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js

@@ -6,10 +6,13 @@ export const GET_CUSTOM_FORM_JSON = `/sys-form/getListByCode` // 获取自定义
 export const GET_ALL_PERSONNEL = `/user/getSimpleActiveUserListNew` // 获取所有人员
 
 export const GET_A_LIST_OF_BUSINESS_OPPORTUNITIES = '/business-opportunity/list' // 获取商机列表
+export const GET_ALL_BUSINESS_OPPORTUNITIES = '/business-opportunity/getAll' // 获取所有商机
 export const GET_A_LIST_OF_CLUES = '/clue/listClue' // 获取线索列表
+export const GET_OBTAIN_ALL_CLUES = '/clue/getAll' // 获取所有线索
 export const GET_CONTACT_LIST = '/contacts/pageContacts' // 获取联系人列表
 export const GET_CUSTOMER_LIST = '/custom/list' // 获取客户列表
-export const GET_ALL_CUSTOMERS = `/custom/getAllCustom` // 获取所有客户
+export const GET_ALL_CUSTOMERS = `/custom/getAllCustom` // 获取客户列表
+export const GET_ALL_CUSTOMERSLIST = `/custom/getAll` // 获取所有客户列表 
 export const GET_TASK_LIST = '/tasks/pageTask' // 获取任务列表
 export const GET_PRODUCT_LIST = '/product/list' // 获取产品列表
 export const GET_CONTRACT_LIST = '/contract/getContractPage' // 获取合同列表
@@ -42,5 +45,14 @@ export const ORDER_ADDITION_EDITING = `/order/addOrUpdate` // 订单新增编辑
 export const PLAN_TO_ADD_EDITORS = `/visitPlan/addOrUpdateVisitPlan` // 计划新增编辑
 
 export const GET_BUSINESS_OPPORTUNITY_DETAILS = `/business-opportunity/getInfo` // 商机详情
+export const GET_CLUE_DETAILS = `/clue/getDetail` // 线索详情
+export const OBTAIN_CUSTOMER_DETAILS = `/custom/getInfo` // 获取客户想
+export const GET_CONTACT_DETAILS = `/contacts/getContactsDetail` // 获取联系人详情
+export const OBTAIN_PRODUCT_RELATED_BUSINESS_OPPORTUNITIES = `/product/businessListWithProduct` // 获取产品关联商机
+export const OBTAIN_SALES_ORDERS_RELATED_TO_THE_PRODUCT = `/product/orderWithProduct` // 获取产品关联销售订单
+export const GET_ORDER_RELATED_TASKS = `/order/taskWithOrder` // 获取销售订单关联任务
+export const OBTAIN_ORDER_RELATED_PRODUCTS = `/order/productWithOrder` // 获取销售订单关联产品
 
-export const SELL_AND_OBTAIN_RELATED_PRODUCTS = `/order/productWithOrder` // 销售订单关联产品
+export const SELL_AND_OBTAIN_RELATED_PRODUCTS = `/product/orderWithProduct` // 销售订单关联产品
+
+export const GET_CONTACTS_WITH_MORE_I_DS = `/contacts/getAllContacts` // 更具Id获取联系人

+ 61 - 35
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleDetails/index.vue

@@ -1,38 +1,40 @@
 <template>
-  <Page :title="`${currentRoutingInformation?.name}详情`">
+  <Page :title="`${currentRoutingInformation?.name}详情`" styleReset="backNone">
     <template v-slot:body>
-      <!-- 商机 -->
-      <template v-if="currentRoutingInformation?.key == 'business'">
-        <Business />
-      </template>
-      <!-- 线索 -->
-      <template v-if="currentRoutingInformation?.key == 'thread'">
-        <Thread />
-      </template>
-      <!-- 客户 -->
-      <template v-if="currentRoutingInformation?.key == 'customer'">
-        <Customer />
-      </template>
-      <!-- 联系人 -->
-      <template v-if="currentRoutingInformation?.key == 'contacts'">
-        <Contacts />
-      </template>
-      <!-- 任务 -->
-      <template v-if="currentRoutingInformation?.key == 'tasks'">
-        <Tasks />
-      </template>
-      <!-- 产品管理 -->
-      <template v-if="currentRoutingInformation?.key == 'product'">
-        <Product />
-      </template>
-      <!-- 合同管理 -->
-      <template v-if="currentRoutingInformation?.key == 'contract'">
-        <Contract />
-      </template>
-      <!-- 销售订单 -->
-      <template v-if="currentRoutingInformation?.key == 'order'">
-        <Order />
-      </template>
+      <div class="w-full h-full detailsClass">
+        <!-- 商机 -->
+        <template v-if="currentRoutingInformation?.key == 'business'">
+          <Business :info="queryParameters" />
+        </template>
+        <!-- 线索 -->
+        <template v-if="currentRoutingInformation?.key == 'thread'">
+          <Thread :info="queryParameters" />
+        </template>
+        <!-- 客户 -->
+        <template v-if="currentRoutingInformation?.key == 'customer'">
+          <Customer :info="queryParameters" />
+        </template>
+        <!-- 联系人 -->
+        <template v-if="currentRoutingInformation?.key == 'contacts'">
+          <Contacts :info="queryParameters" />
+        </template>
+        <!-- 任务 -->
+        <template v-if="currentRoutingInformation?.key == 'tasks'">
+          <Tasks :info="queryParameters" />
+        </template>
+        <!-- 产品管理 -->
+        <template v-if="currentRoutingInformation?.key == 'product'">
+          <Product :info="queryParameters" />
+        </template>
+        <!-- 合同管理 -->
+        <template v-if="currentRoutingInformation?.key == 'contract'">
+          <Contract :info="queryParameters" />
+        </template>
+        <!-- 销售订单 -->
+        <template v-if="currentRoutingInformation?.key == 'order'">
+          <Order :info="queryParameters" />
+        </template>
+      </div>
     </template>
   </Page>
 </template>
@@ -42,7 +44,7 @@ import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
 import useRouterStore from "@store/useRouterStore.js";
 
-import Business from "@pages/pageComponents/business/detail.vue" 
+import Business from "@pages/pageComponents/business/detail.vue"
 import Thread from "@pages/pageComponents/thread/detail.vue"
 import Customer from "@pages/pageComponents/customer/detail.vue"
 import Contacts from "@pages/pageComponents/contacts/detail.vue"
@@ -70,5 +72,29 @@ useLifecycle({
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+.backNone {
+  background: linear-gradient(to bottom, #E0EFFF, #F8F8F8 60%) !important;
+
+  :deep(.van-nav-bar) {
+    background: none;
+  }
+}
+
+.detailsClass {
+  :deep(.van-tabs) {
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+  }
+  :deep(.van-tabs__content) {
+    flex: 1;
+    overflow: hidden;
+  }
+  :deep(.van-tab__panel) {
+    height: 100%;
+  }
+  :deep(.van-tabs__nav) {
+    background: none;
+  }
+}
 </style>

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/addEditor.vue

@@ -40,7 +40,7 @@ import requests from "@common/requests";
 import useToast from "@hooks/useToast"
 import CustomerForm from '@components/common/formForm/formView.vue'
 import FoldingPanel from '@components/common/foldingPanel.vue';
-import NewAndModifiedRelatedProducts from '@pages/pageComponents/order/newAndModifiedRelatedProducts.vue'
+import NewAndModifiedRelatedProducts from '@pages/pageComponents/product/newAndModifiedRelatedProducts.vue'
 
 const props = defineProps({
   formJson: { required: true },

+ 59 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="商机名称" :value="info.name" />
+      <van-cell title="客户名称" :value="info.customerName" />
+      <van-cell title="联系人姓名" :value="info.contactsName" />
+      <van-cell title="商机金额">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.amountOfMoney">¥ {{ info.amountOfMoney }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="预计成交" :value="info.expectedTransactionDate" />
+      <van-cell title="商机阶段" :value="info.stageValue" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.inchargerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.remark" />
+    </div>
+    <div class="bottomButton">
+      <van-button type="primary" class="w-full block">关联联系人</van-button>
+      <van-button type="warning" class="w-full block">转移商机</van-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+  .bottomButton {
+    margin: 0 14px;
+    padding-bottom: 30px;
+    :deep(.van-button) {
+      margin-bottom: 20px;
+    }
+  }
+  .info {
+    margin: 8px 14px 30px 14px;
+    padding: 14px;
+  }
+</style>

+ 49 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/detail.vue

@@ -1,20 +1,64 @@
 <template>
   <div class="w-full h-full">
-    商机详情
+    <van-tabs v-model:active="tabActive">
+      <van-tab title="商机阶段" name="商机阶段">商机阶段 1</van-tab>
+      <van-tab title="商机信息" name="商机信息">
+        <BusinessInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关产品" name="相关产品">
+        <RelatedProducts :infoList="relatedProductsList" />
+      </van-tab>
+      <van-tab title="相关任务" name="相关任务">
+        <RelatedTasks :infoList="relatedTasksList" />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, onActivated, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_BUSINESS_OPPORTUNITY_DETAILS } from "@hooks/useApi"
+import requests from "@common/requests";
+import RelatedProducts from '../product/relatedProducts.vue';
+import BusinessInfo from './businessInfo.vue';
+import RelatedTasks from '../tasks/relatedTasks.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('商机信息');
+const relatedProductsList = ref([]);
+const relatedTasksList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '商机信息';
+  processingData(newValue.id)
+})
+
+function getBusinessOpportunityDetails(id) {
+  requests.post(GET_BUSINESS_OPPORTUNITY_DETAILS, { id }).then(({ data }) => {
+    relatedProductsList.value = data.businessItemProducts || []
+    relatedTasksList.value = data.taskList || []
+  })
+}
+
+function processingData(id) {
+  getBusinessOpportunityDetails(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '商机信息';
+    processingData(props.info.id)
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+/* 样式代码 */
 </style>

+ 59 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/relatedBusinessOpportunities.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="flex flex-col h-full overflow-y-auto">
+    <div class="info h-full cellnormall" v-if="infoList.length">
+      <div v-for="(item, index) in infoList">
+        <FoldingPanel :title="`相关商机(${item.name})`">
+          <template #foldContainer>
+            <div class="p-5 bg-white ">
+              <van-cell title="商机名称" :value="item.name" />
+              <van-cell title="客户名称" :value="item.customerName" />
+              <van-cell title="负责人">
+                <template #default>
+                  <TranslationComponent :openId="item.inchargerName" />
+                </template>
+              </van-cell>
+              <van-cell title="商机金额" :value="item.amountOfMoney">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.amountOfMoney">¥ {{ item.amountOfMoney }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="预计成交时间" :value="item.expectedTransactionDate" />
+              <van-cell title="商机阶段" :value="item.stageValue" />
+              <van-cell title="预计成交时间" :value="item.creatorName" />
+            </div>
+          </template>
+        </FoldingPanel>
+      </div>
+    </div>
+    <div v-else class="items-justify-center h-2/3">
+      <van-empty description="暂无商机" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPriority, fixedFieldTaskStatus } from "@utility/defaultData.js"
+import FoldingPanel from '@components/common/foldingPanel.vue';
+
+const props = defineProps({
+  infoList: {
+    type: Array,
+    required: true,
+    default: () => ([])
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.info {
+  margin: 8px 14px 30px 14px;
+}
+</style>

+ 53 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/contactsInfo.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="联系人姓名" :value="info.name" />
+      <van-cell title="电话" :value="info.phone" />
+      <van-cell title="邮箱" :value="info.email" />
+      <van-cell title="职务" :value="info.position" />
+      <van-cell title="地址" :value="info.address" />
+      <van-cell title="性别" :value="info.phone == 1 ? '男' : '女'" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.ownerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.remark" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.bottomButton {
+  margin: 0 14px;
+  padding-bottom: 30px;
+
+  :deep(.van-button) {
+    margin-bottom: 20px;
+  }
+}
+
+.info {
+  margin: 8px 14px 30px 14px;
+  padding: 14px;
+}
+</style>

+ 48 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/detail.vue

@@ -1,20 +1,63 @@
 <template>
   <div class="w-full h-full">
-    联系人详情
+    <van-tabs v-model:active="tabActive">
+      <van-tab title="联系人信息" name="联系人信息">
+        <ContactsInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关任务" name="相关任务">
+        <RelatedTasks :infoList="relatedTasksList" />
+      </van-tab>
+      <van-tab title="相关商机" name="相关商机">
+        <RelatedBusinessOpportunities :infoList="relatedBusinessOpportunitiesList"  />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, onActivated, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_CONTACT_DETAILS } from "@hooks/useApi"
+import requests from "@common/requests";
+import ContactsInfo from './contactsInfo.vue';
+import RelatedTasks from '../tasks/relatedTasks.vue';
+import RelatedBusinessOpportunities from '../business/relatedBusinessOpportunities.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('联系人信息');
+const relatedBusinessOpportunitiesList = ref([]);
+const relatedTasksList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '联系人信息';
+  processingData(newValue.id)
+})
+
+function getDetailedData(id) {
+  requests.post(GET_CONTACT_DETAILS, { id }).then(({ data }) => {
+    relatedBusinessOpportunitiesList.value = data.businessOpportunityList || []
+    relatedTasksList.value = data.taskList || []
+  })
+}
+
+function processingData(id) {
+  getDetailedData(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '联系人信息';
+    processingData(props.info.id)
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+/* 样式代码 */
 </style>

+ 54 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/relatedContacts.vue

@@ -0,0 +1,54 @@
+<template>
+  <div class="flex flex-col h-full overflow-y-auto">
+    <div class="info h-full cellnormall" v-if="infoList.length">
+      <div v-for="(item, index) in infoList">
+        <FoldingPanel :title="`相关联系人(${item.name})`">
+          <template #foldContainer>
+            <div class="p-5 bg-white ">
+              <van-cell title="联系人姓名" :value="item.name" />
+              <van-cell title="电话" :value="item.phone" />
+              <van-cell title="邮箱" :value="item.email" />
+              <van-cell title="职务" :value="item.position" />
+              <van-cell title="性别" :value="item.phone == 1 ? '男' : '女'" />
+              <van-cell title="负责人">
+                <template #default>
+                  <TranslationComponent :openId="item.ownerName" />
+                </template>
+              </van-cell>
+            </div>
+          </template>
+        </FoldingPanel>
+      </div>
+    </div>
+    <div v-else class="items-justify-center h-2/3">
+      <van-empty description="暂无联系人" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPriority, fixedFieldTaskStatus } from "@utility/defaultData.js"
+import FoldingPanel from '@components/common/foldingPanel.vue';
+
+const props = defineProps({
+  infoList: {
+    type: Array,
+    required: true,
+    default: () => ([])
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.info {
+  margin: 8px 14px 30px 14px;
+}
+</style>

+ 60 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contract/detail.vue

@@ -1,12 +1,57 @@
 <template>
-  <div class="w-full h-full">
-    合同详情
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="合同编号" :value="info.number" />
+      <van-cell title="合同名称" :value="info.name" />
+      <van-cell title="合同金额" :value="info.amounts">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.amounts">¥ {{ info.amounts }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="已回款金额" :value="info.payment">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.payment">¥ {{ info.payment }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="已回款进度" :value="info.payment">
+        <template #default>
+          {{ info.payment ? (100 * info.payment / info.amounts).toFixed(1) + '%' : '0%' }}
+        </template>
+      </van-cell>
+      <van-cell title="下笔回款日期" :value="info.nextPaymentDate">
+        <template #default>
+          {{ info.nextPaymentDate ? info.nextPaymentDate : '-' }}
+        </template>
+      </van-cell>
+      <van-cell title="下笔回款金额" :value="info.payment">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.payment">¥ {{ info.payment.toFixed(2) }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="合同类型" :value="info.typeName" />
+      <van-cell title="状态" :value="info.status">
+        <template #default>
+          <span :style="fixedFieldStatusArray[info.status].color">
+            {{ fixedFieldStatusArray[info.status].label }}
+          </span>
+        </template>
+      </van-cell>
+    </div>
   </div>
 </template>
 
 <script setup>
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldStatusArray } from '@/utility/defaultData.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
 
 useLifecycle({
   load: () => {
@@ -16,5 +61,17 @@ useLifecycle({
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+.bottomButton {
+  margin: 0 14px;
+  padding-bottom: 30px;
+
+  :deep(.van-button) {
+    margin-bottom: 20px;
+  }
+}
+
+.info {
+  margin: 8px 14px 30px 14px;
+  padding: 14px;
+}
 </style>

+ 56 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/customerInfo.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="客户名称" :value="info.customName" />
+      <van-cell title="客户来源" :value="info.customSourceValue" />
+      <van-cell title="电话号码" :value="info.telPhone" />
+      <van-cell title="邮箱" :value="info.email" />
+      <van-cell title="客户行业" :value="info.customerIndustryValue" />
+      <van-cell title="客户级别" :value="info.customerLevelValue" />
+      <van-cell title="客户地址" :value="info.address" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.inchargerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.customDesc" />
+    </div>
+    <div class="bottomButton">
+      <van-button type="primary" class="w-full block">关联联系人</van-button>
+      <van-button type="warning" class="w-full block">转移商机</van-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+  .bottomButton {
+    margin: 0 14px;
+    padding-bottom: 30px;
+    :deep(.van-button) {
+      margin-bottom: 20px;
+    }
+  }
+  .info {
+    margin: 8px 14px 30px 14px;
+    padding: 14px;
+  }
+</style>

+ 60 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/detail.vue

@@ -1,20 +1,75 @@
 <template>
   <div class="w-full h-full">
-    客户详情
+    <van-tabs v-model:active="tabActive" swipe-threshold="3">
+      <van-tab title="客户信息" name="客户信息">
+        <CustomerInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关任务" name="相关任务">
+        <RelatedTasks :infoList="relatedTasksList" />
+      </van-tab>
+      <van-tab title="相关联系人" name="相关联系人">
+        <RelatedContacts :infoList="relatedContactsList" />
+      </van-tab>
+      <van-tab title="相关商机" name="相关商机">
+        <RelatedBusinessOpportunities :infoList="relatedBusinessOpportunitiesList" />
+      </van-tab>
+      <van-tab title="相关销售订单" name="相关销售订单">
+        <RelatedSalesOrders :infoList="relatedSalesOrdersList" />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, onActivated, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { OBTAIN_CUSTOMER_DETAILS } from "@hooks/useApi"
+import requests from "@common/requests";
+import CustomerInfo from './customerInfo.vue';
+import RelatedTasks from '../tasks/relatedTasks.vue';
+import RelatedContacts from '../contacts/relatedContacts.vue';
+import RelatedBusinessOpportunities from '../business/relatedBusinessOpportunities.vue';
+import RelatedSalesOrders from '../order/relatedSalesOrders.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('客户信息');
+const relatedTasksList = ref([]);
+const relatedContactsList = ref([]);
+const relatedBusinessOpportunitiesList = ref([]);
+const relatedSalesOrdersList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '客户信息';
+  processingData(newValue.id)
+})
+
+function getDetailedData(id) {
+  requests.post(OBTAIN_CUSTOMER_DETAILS, { id }).then(({ data }) => {
+    relatedTasksList.value = data.tasks || []
+    relatedContactsList.value = data.contacts || []
+    relatedBusinessOpportunitiesList.value = data.businessOpportunitys || []
+    relatedSalesOrdersList.value = data.salesOrders || []
+  })
+}
+
+function processingData(id) {
+  getDetailedData(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '客户信息';
+    processingData(props.info.id)
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+/* 样式代码 */
 </style>

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/addEditor.vue

@@ -45,7 +45,7 @@ import requests from "@common/requests";
 import useToast from "@hooks/useToast"
 import FoldingPanel from '@components/common/foldingPanel.vue';
 import CustomerForm from '@components/common/formForm/formView.vue'
-import NewAndModifiedRelatedProducts from '@pages/pageComponents/order/newAndModifiedRelatedProducts.vue'
+import NewAndModifiedRelatedProducts from '@pages/pageComponents/product/newAndModifiedRelatedProducts.vue'
 
 const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
 const props = defineProps({

+ 61 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/detail.vue

@@ -1,20 +1,76 @@
 <template>
   <div class="w-full h-full">
-    销售订单详情
+    <van-tabs v-model:active="tabActive">
+      <van-tab title="销售订单信息" name="销售订单信息">
+        <OrderInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关任务" name="相关任务">
+        <RelatedTasks :infoList="relatedTasksList"  />
+      </van-tab>
+      <van-tab title="相关产品" name="相关产品">
+        <RelatedProducts :infoList="relatedProductsList" />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, onActivated, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_ORDER_RELATED_TASKS, OBTAIN_ORDER_RELATED_PRODUCTS } from "@hooks/useApi"
+import requests from "@common/requests";
+import OrderInfo from './orderInfo.vue';
+import RelatedTasks from '../tasks/relatedTasks.vue';
+import RelatedProducts from '../product/relatedProducts.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('销售订单信息');
+const relatedTasksList = ref([]);
+const relatedProductsList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '销售订单信息';
+  processingData(newValue.id)
+})
+
+function getDetailedData(id) {
+  requests.post(GET_ORDER_RELATED_TASKS, { id }).then(({ data }) => {
+    relatedTasksList.value = data || []
+  })
+  requests.post(OBTAIN_ORDER_RELATED_PRODUCTS, { id }).then(({ data }) => {
+    const list = (data || []).map((item) => {
+      const { id, productName, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail } = item
+      return {
+        id, productId: id, productName, productCode, unit, unitName, typeName, type, price, inventory,
+        productType: typeName,
+        quantity: +orderProductDetail?.num,
+        discount: +orderProductDetail?.discount,
+        sellingPrice: +orderProductDetail?.sealPrice,
+        totalPrice: +orderProductDetail?.totalPrice
+      }
+    })
+    relatedProductsList.value = list
+  })
+}
+
+function processingData(id) {
+  getDetailedData(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '销售订单信息';
+    processingData(props.info.id)
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+/* 样式代码 */
 </style>

+ 81 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/orderInfo.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="订单编号" :value="info.orderCode" />
+      <van-cell title="订单名称" :value="info.orderName" />
+      <van-cell title="客户名称" :value="info.customName" />
+      <van-cell title="商机名称" :value="info.businessOpportunityName" />
+      <van-cell title="订单金额" :value="info.price">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.price">¥ {{ info.price }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="回款状态" :value="info.receivedStatus" />
+      <van-cell title="已回款金额" :value="info.receivedPayment">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.receivedPayment">¥ {{ info.receivedPayment }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="未回款" :value="info.unReceivedPayment">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.unReceivedPayment">¥ {{ info.unReceivedPayment }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="订单类型" :value="info.type" />
+      <van-cell title="下单时间" :value="info.placeTime" />
+      <van-cell title="订单开始时间" :value="info.orderStartDate" />
+      <van-cell title="订单结束时间" :value="info.orderEndDate" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.inchargerName" />
+        </template>
+      </van-cell>
+      <van-cell title="客户签约人">
+        <template #default>
+          <TranslationComponent :openId="info.customSignerName" />
+        </template>
+      </van-cell>
+      <van-cell title="公司签约人">
+        <template #default>
+          <TranslationComponent :openId="info.companySignerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.remark" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.bottomButton {
+  margin: 0 14px;
+  padding-bottom: 30px;
+
+  :deep(.van-button) {
+    margin-bottom: 20px;
+  }
+}
+
+.info {
+  margin: 8px 14px 30px 14px;
+  padding: 14px;
+}
+</style>

+ 69 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/relatedSalesOrders.vue

@@ -0,0 +1,69 @@
+<template>
+  <div class="flex flex-col h-full overflow-y-auto">
+    <div class="info h-full cellnormall" v-if="infoList.length">
+      <div v-for="(item, index) in infoList">
+        <FoldingPanel :title="`相关销售订单(${item.orderName})`">
+          <template #foldContainer>
+            <div class="p-5 bg-white ">
+              <van-cell title="订单编号" :value="item.orderCode" />
+              <van-cell title="订单名称" :value="item.orderName" />
+              <van-cell title="客户名称" :value="item.customName" />
+              <van-cell title="订单金额" :value="item.price">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.price">¥ {{ item.price }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="已回款" :value="item.receivedPayment">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.receivedPayment">¥ {{ item.receivedPayment }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="未回款" :value="item.unReceivedPayment">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.unReceivedPayment">¥ {{ item.unReceivedPayment }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="订单类型" :value="item.typeName" />
+              <van-cell title="下单时间" :value="item.placeTime" />
+              <van-cell title="负责人">
+                <template #default>
+                  <TranslationComponent :openId="item.inchargerName" />
+                </template>
+              </van-cell>
+            </div>
+          </template>
+        </FoldingPanel>
+      </div>
+    </div>
+    <div v-else class="items-justify-center h-2/3">
+      <van-empty description="暂无销售订单" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPriority, fixedFieldTaskStatus } from "@utility/defaultData.js"
+import FoldingPanel from '@components/common/foldingPanel.vue';
+
+const props = defineProps({
+  infoList: {
+    type: Array,
+    required: true,
+    default: () => ([])
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.info {
+  margin: 8px 14px 30px 14px;
+}
+</style>

+ 50 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/detail.vue

@@ -1,20 +1,65 @@
 <template>
   <div class="w-full h-full">
-    产品详情
+    <van-tabs v-model:active="tabActive">
+      <van-tab title="产品信息" name="产品信息">
+        <ProductInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关商机" name="相关商机">
+        <RelatedBusinessOpportunities :infoList="relatedBusinessOpportunitiesList"  />
+      </van-tab>
+      <van-tab title="相关销售订单" name="相关销售订单">
+        <RelatedSalesOrders :infoList="relatedSalesOrdersList" />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, onActivated, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { OBTAIN_PRODUCT_RELATED_BUSINESS_OPPORTUNITIES, OBTAIN_SALES_ORDERS_RELATED_TO_THE_PRODUCT } from "@hooks/useApi"
+import requests from "@common/requests";
+import ProductInfo from './productInfo.vue';
+import RelatedSalesOrders from '../order/relatedSalesOrders.vue';
+import RelatedBusinessOpportunities from '../business/relatedBusinessOpportunities.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('产品信息');
+const relatedBusinessOpportunitiesList = ref([]);
+const relatedSalesOrdersList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '产品信息';
+  processingData(newValue.id)
+})
+
+function getDetailedData(id) {
+  requests.post(OBTAIN_PRODUCT_RELATED_BUSINESS_OPPORTUNITIES, { id }).then(({ data }) => {
+    relatedBusinessOpportunitiesList.value = data || []
+  })
+  requests.post(OBTAIN_SALES_ORDERS_RELATED_TO_THE_PRODUCT, { id }).then(({ data }) => {
+    relatedSalesOrdersList.value = data || []
+  })
+}
+
+function processingData(id) {
+  getDetailedData(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '产品信息';
+    processingData(props.info.id)
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+/* 样式代码 */
 </style>

fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/newAndModifiedRelatedProducts.vue → fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/newAndModifiedRelatedProducts.vue


+ 58 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/productInfo.vue

@@ -0,0 +1,58 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="产品编号" :value="info.productCode" />
+      <van-cell title="产品名称" :value="info.productName" />
+      <van-cell title="产品类别" :value="info.typeName" />
+      <van-cell title="单位" :value="info.unitName" />
+      <van-cell title="标准价格" :value="info.price">
+        <template #default>
+          <span class="text-[#FF8B32]" v-if="info.price">¥ {{ info.price }}</span>
+        </template>
+      </van-cell>
+      <van-cell title="库存" :value="info.unit" />
+      <van-cell title="状态" :value="info.status == 1 ? '上家' : '下架'" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.inchargerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.descs" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.bottomButton {
+  margin: 0 14px;
+  padding-bottom: 30px;
+
+  :deep(.van-button) {
+    margin-bottom: 20px;
+  }
+}
+
+.info {
+  margin: 8px 14px 30px 14px;
+  padding: 14px;
+}
+</style>

+ 68 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/relatedProducts.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="flex flex-col h-full overflow-y-auto">
+    <div class="info h-full cellnormall" v-if="infoList.length">
+      <div v-for="(item, index) in infoList">
+        <FoldingPanel :title="`相关产品(${item.productName})`">
+          <template #foldContainer>
+            <div class="p-5 bg-white ">
+              <van-cell title="产品名称" :value="item.productName" />
+              <van-cell title="产品类型" :value="item.productType" />
+              <van-cell title="单位" :value="item.unit" />
+              <van-cell title="标准价格" :value="item.price">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.price">¥ {{ item.price }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="库存" :value="item.inventory" />
+              <van-cell title="售价" :value="item.sellingPrice">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.sellingPrice">¥ {{ item.sellingPrice }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="数量" :value="item.quantity">
+                <template #default>
+                  <span class="text-[#FF8B32]" v-if="item.quantity">¥ {{ item.quantity }}</span>
+                </template>
+              </van-cell>
+              <van-cell title="折扣(%)" :value="item.discount" />
+              <van-cell title="合计" :value="item.totalPrice" /> 
+            </div>
+          </template>
+        </FoldingPanel>
+      </div>
+    </div>
+    <div v-else class="items-justify-center h-2/3">
+      <van-empty description="暂无产品" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, onActivated } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import FoldingPanel from '@components/common/foldingPanel.vue';
+
+const props = defineProps({
+  infoList: {
+    type: Array,
+    required: true,
+    default: () => ([])
+  }
+})
+
+useLifecycle({
+  load: () => {
+    
+  },
+  init: () => {
+    
+  }
+});
+
+</script>
+
+<style lang='scss' scoped>
+.info {
+  margin: 8px 14px 30px 14px;
+}
+</style>

+ 181 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/addEditor.vue

@@ -1,6 +1,66 @@
 <template>
   <div class="w-full h-full flex flex-col">
     <div class="flex-1 overflow-y-auto">
+      <van-form ref="vanFormRef" show-error :show-error-message="false" label-align="left" input-align="right"
+        class="bg-white" @submit="onSubmit">
+        <van-field v-model="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="商机"
+            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>
+      </van-form>
       <CustomerForm ref="formFormRef" :formJson="formJson" :formValue="formVal"></CustomerForm>
     </div>
     <div class="mar-20px ">
@@ -8,12 +68,24 @@
         {{ Object.keys(formVal).length > 0 ? '确定修改' : '确定添加' }}
       </van-button>
     </div>
+
+    <!-- 选择器 -->
+    <div>
+      <!-- 正常下拉框选择 -->
+      <van-popup v-model:show="showSelectionFlag" destroy-on-close position="bottom" :style="{ height: '80%' }">
+        <PullDownSelector :options="showSelectionArray" :doYouNeedTranslation="false" @change="selectChange" />
+      </van-popup>
+    </div>
   </div>
 </template>
 
 <script setup>
-import { ref, onActivated } from 'vue';
+import { ref, onActivated, computed } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldTaskType, fixedFieldPriority } from '@utility/defaultData.js';
+import { GET_ALL_CUSTOMERSLIST, GET_ALL_BUSINESS_OPPORTUNITIES, GET_SALES_ORDER_LIST, GET_OBTAIN_ALL_CLUES, GET_CONTACTS_WITH_MORE_I_DS } from "@hooks/useApi";
+import requests from "@common/requests";
+import PullDownSelector from '@components/common/pullDownSelector.vue'
 import CustomerForm from '@components/common/formForm/formView.vue'
 
 const props = defineProps({
@@ -22,7 +94,28 @@ const props = defineProps({
 });
 
 const formFormRef = ref(null)
+const vantFormVal = ref({
+  taskType: 0,
+  taskTypeName: '客户'
+})
 const formVal = ref({})
+const allBusinessOpportunities = ref([])
+const allCustomersList = ref([])
+const allCluesList = ref([])
+const allSalesOrdersList = ref([])
+const allContactsList = ref([])
+const showSelectionFlag = ref(false)
+const showSelectionFiled = ref([])
+const showSelectionArray = ref([])
+const taskTypeFiled = ['customId', 'businessOpportunityId', 'orderId', 'clueId']
+
+const contactDisabled = computed(() => {
+  const taskType = vantFormVal.value?.taskType
+  if(!taskType && taskType != 0) {
+    return true
+  }
+  return false
+})
 
 function onSubmit() {
   formFormRef.value.getJsonData().then((res) => {
@@ -30,17 +123,103 @@ function onSubmit() {
   })
 }
 
+function selectChange(value, label) {
+  if (taskTypeFiled.includes(showSelectionFiled.value)) {
+    const item = fixedFieldTaskType.find(item => item.value == vantFormVal.value.taskType)
+    console.log(item, value, )
+    if (item && item.show) {
+      getContactData(showSelectionFiled.value, value)
+    }
+  }
+  vantFormVal.value[showSelectionFiled.value] = value
+  vantFormVal.value[`${showSelectionFiled.value}Name`] = label
+  showSelectionFlag.value = false
+}
+
+function showSelectionBox(filed, list = [], event) {
+  if (taskTypeFiled.includes(filed)) {
+    const fileds = taskTypeFiled.filter(item => item !== filed)
+    fileds.forEach(item => {
+      vantFormVal.value[item] = ''
+      vantFormVal.value[`${item}Name`] = ''
+    })
+    vantFormVal.value.contactsId = ''
+    vantFormVal.value[`contactsIdName`] = ''
+  }
+  showSelectionFiled.value = filed
+  showSelectionArray.value = list
+  showSelectionFlag.value = true
+}
+
+function getContactData(filed, value) {
+  const urlType = {
+    'customId': 'customerId',
+    'businessOpportunityId': 'businessId',
+    'orderId': 'salesId',
+  }
+  const url = `${GET_CONTACTS_WITH_MORE_I_DS}?${urlType[filed]}=${value}`
+  requests.get(url).then(({ data = [] }) => {
+    let list = data.map(item => {
+      return {
+        label: item.name,
+        value: item.id,
+      }
+    })
+    if(!allContactsList.value.length) {
+      list = [{}]
+    }
+    allContactsList.value = list
+  })
+}
+
+function getAllListData() {
+  requests.post(GET_ALL_CUSTOMERSLIST, {}).then(res => {
+    allCustomersList.value = res.data.map(item => {
+      return {
+        label: item.customName,
+        value: item.id,
+      }
+    })
+  })
+  requests.post(GET_ALL_BUSINESS_OPPORTUNITIES, {}).then(res => {
+    allBusinessOpportunities.value = res.data.map(item => {
+      return {
+        label: item.name,
+        value: item.id,
+      }
+    })
+  })
+  requests.post(GET_SALES_ORDER_LIST, { pageIndex: -1, pageSize: -1 }).then(({ data }) => {
+    allSalesOrdersList.value = (data.record || []).map(item => {
+      return {
+        label: item.orderName,
+        value: item.id,
+      }
+    })
+  })
+  requests.post(GET_OBTAIN_ALL_CLUES, {}).then(res => {
+    allCluesList.value = res.data.map(item => {
+      return {
+        label: item.clueName,
+        value: item.id,
+      }
+    })
+  })
+}
+
 useLifecycle({
   load: () => {
     formVal.value = props.formValue
+    getAllListData()
   },
   init: () => {
     formVal.value = props.formValue
+    getAllListData()
   }
 });
 
 onActivated(() => {
-  
+
 })
 </script>
 

+ 63 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/relatedTasks.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="flex flex-col h-full overflow-y-auto">
+    <div class="info h-full cellnormall" v-if="infoList.length">
+      <div v-for="(item, index) in infoList">
+        <FoldingPanel :title="`相关任务(${item.taskName})`">
+          <template #foldContainer>
+            <div class="p-5 bg-white ">
+              <van-cell title="任务名称" :value="item.taskName" />
+              <van-cell title="优先级" :value="retPriorityStr(item.priority)" />
+              <van-cell title="状态" :value="retStatueStr(item.status)" />
+              <van-cell title="执行人">
+                <template #default>
+                  <TranslationComponent :openId="item.executorNames" />
+                </template>
+              </van-cell>
+              <van-cell title="开始时间" :value="item.startDate" />
+              <van-cell title="截至时间" :value="item.endDate" />
+              <van-cell title="备注" :value="item.taskDesc" />
+            </div>
+          </template>
+        </FoldingPanel>
+      </div>
+    </div>
+    <div v-else class="items-justify-center h-2/3">
+      <van-empty description="暂无任务" />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPriority, fixedFieldTaskStatus } from "@utility/defaultData.js"
+import FoldingPanel from '@components/common/foldingPanel.vue';
+
+const props = defineProps({
+  infoList: {
+    type: Array,
+    required: true,
+    default: () => ([])
+  }
+})
+
+function retPriorityStr(value) {
+  return fixedFieldPriority.find(item => item.value == value)?.label
+}
+
+function retStatueStr(value) {
+  return fixedFieldTaskStatus.find(item => item.value == value)?.label
+}
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.info {
+  margin: 8px 14px 30px 14px;
+}
+</style>

+ 21 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/tasksInfo.vue

@@ -0,0 +1,21 @@
+<template>
+  <Page title='任务详情'>
+    <template v-slot:body>
+    </template>
+  </Page>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+  /* 样式代码 */
+</style>

+ 46 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/detail.vue

@@ -1,16 +1,58 @@
 <template>
   <div class="w-full h-full">
-    线索详情
+    <van-tabs v-model:active="tabActive">
+      <van-tab title="线索信息">
+        <ThreadInfo :info="info" />
+      </van-tab>
+      <van-tab title="相关任务" name="相关任务">
+        <RelatedTasks :infoList="relatedTasksList" :key="componentKey" />
+      </van-tab>
+    </van-tabs>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_CLUE_DETAILS } from "@hooks/useApi"
+import requests from "@common/requests";
+import ThreadInfo from './threadInfo.vue';
+import RelatedTasks from '../tasks/relatedTasks.vue';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+const tabActive = ref('线索信息');
+const componentKey = ref(1);
+const relatedTasksList = ref([]);
+
+watch(() => props.info, (newValue) => {
+  tabActive.value = '线索信息';
+  processingData(newValue.id)
+})
+
+function getDetails(id) {
+  requests.post(GET_CLUE_DETAILS, { id }).then(({ data }) => {
+    relatedTasksList.value = data.taskList || []
+  }).finally(() => {
+    setTimeout(() => {
+      componentKey.value++
+    }, 10)
+  })
+}
+
+function processingData(id) {
+  getDetails(id)
+}
 
 useLifecycle({
-  load: () => {
-    // 添加加载逻辑
+  init: () => {
+    tabActive.value = '线索信息';
+    processingData(props.info.id)
   }
 });
 </script>

+ 56 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/threadInfo.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="flex flex-col h-full">
+    <div class="bg-white info flex-1 overflow-y-auto cellnormall">
+      <van-cell title="线索名称" :value="info.clueName" />
+      <van-cell title="线索来源" :value="info.clueSourceValue" />
+      <van-cell title="电话号码" :value="info.phone" />
+      <van-cell title="邮箱" :value="info.email" />
+      <van-cell title="客户行业" :value="info.customerIndustryValue" />
+      <van-cell title="客户级别" :value="info.customerLevelValue" />
+      <van-cell title="客户地址" :value="info.address" />
+      <van-cell title="负责人">
+        <template #default>
+          <TranslationComponent :openId="info.inchargerName" />
+        </template>
+      </van-cell>
+      <van-cell title="备注" :value="info.remark" />
+    </div>
+    <div class="bottomButton">
+      <van-button type="primary" class="w-full block">转移线索</van-button>
+      <van-button type="warning" class="w-full block">转为商机</van-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+  .bottomButton {
+    margin: 0 14px;
+    padding-bottom: 30px;
+    :deep(.van-button) {
+      margin-bottom: 20px;
+    }
+  }
+  .info {
+    margin: 8px 14px 30px 14px;
+    padding: 14px;
+  }
+</style>

+ 31 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js

@@ -11,4 +11,34 @@ export const defaultRelatedProductDataFields = {
   quantity: "", // 数量
   discount: "", // 折扣
   totalPrice: "", // 总价
-}
+}
+
+// 任务优先级
+export const fixedFieldPriority = [
+  { label: "高", value: 2 },
+  { label: "中", value: 1 },
+  { label: "低", value: 0 },
+]
+
+// 任务类型
+export const fixedFieldTaskType = [
+  { label: "客户", value: 0, show: true },
+  { label: "商机", value: 1, show: true },
+  { label: "销售订单", value: 2, show: true },
+  { label: "线索", value: 3, show: false },
+]
+
+//任务状态
+export const fixedFieldTaskStatus = [
+  { label: "未开始", value: "0", type: "info" },
+  { label: "进行中", value: "1", type: "primary" },
+  { label: "已完成", value: "2", type: "success" },
+  { label: "已超时", value: "3", type: "danger" },
+];
+
+// 合同状态
+export const fixedFieldStatusArray = [
+  { label: "审核通过", value: "0", color: "color:#67c23a;" },
+  { label: "待审核", value: "1", color: "color:#e6a23c;" },
+  { label: "已驳回", value: "2", color: "color:#f56c6c;" },
+]

+ 18 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java

@@ -9,22 +9,23 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.*;
-import com.management.platform.service.*;
+import com.management.platform.service.BusinessOpportunityService;
+import com.management.platform.service.StageService;
+import com.management.platform.service.SysFunctionService;
+import com.management.platform.service.WxCorpInfoService;
 import com.management.platform.service.impl.ExcelExportServiceImpl;
 import com.management.platform.util.HttpRespMsg;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
-
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
@@ -65,6 +66,8 @@ public class BusinessOpportunityController {
     private BusinessOpportunityMapper businessOpportunityMapper;
     @Resource
     private ActionLogMapper actionLogMapper;
+    @Resource
+    private BusinessOpportunityService businessOpportunityService;
 
     @RequestMapping("getAll")
     public Object getAll(HttpServletRequest request) {
@@ -410,6 +413,17 @@ public class BusinessOpportunityController {
 
     }
 
+
+    @PostMapping("/listByPin")
+    public HttpRespMsg listByPin(BusinessOpportunity bo, HttpServletRequest request) {
+        return businessOpportunityService.listByPin(bo,request);
+    }
+
+    @PostMapping("/pinBusinessOpportunity")
+    public HttpRespMsg pinBusinessOpportunity(BusinessOpportunity bo, HttpServletRequest request) {
+        return businessOpportunityService.pinBusinessOpportunity(bo,request);
+    }
+
     @RequestMapping("getAllByStage")
     public Object getAllByStage(BusinessOpportunity bo,HttpServletRequest request) {
         User user = userMapper.selectById(request.getHeader("Token"));

+ 14 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CustomController.java

@@ -5,9 +5,11 @@ 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.management.platform.mapper.*;
+import com.management.platform.mapper.CustomMapper;
+import com.management.platform.mapper.SysDictMapper;
+import com.management.platform.mapper.SysFormMapper;
+import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.ContactsService;
 import com.management.platform.service.CustomService;
 import com.management.platform.service.WxCorpInfoService;
@@ -15,8 +17,8 @@ import com.management.platform.service.impl.ExcelExportServiceImpl;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
-
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -65,6 +67,15 @@ public class CustomController {
         return customService.getList(custom, request);
     }
 
+    @PostMapping("/listByPin")
+    public HttpRespMsg listByPin(Custom custom, HttpServletRequest request) {
+        return customService.listByPin(custom, request);
+    }
+
+    @PostMapping("/pinCustom")
+    public HttpRespMsg pinCustom(Custom custom, HttpServletRequest request) {
+        return customService.pinCutom(custom,request);
+    }
 
     @RequestMapping("getAllCustom")
     public HttpRespMsg getAllCustom(HttpServletRequest request) {

+ 39 - 30
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/VisitPlanController.java

@@ -2,16 +2,16 @@ package com.management.platform.controller;
 
 import com.management.platform.entity.VisitPlan;
 import com.management.platform.service.VisitPlanService;
+import com.management.platform.time.VisitPlanDelayHandler;
+import com.management.platform.time.VisitPlanDelayItem;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.format.annotation.DateTimeFormat;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 @RestController
 @RequestMapping("/visitPlan")
@@ -34,30 +34,30 @@ public class VisitPlanController {
      * @param request
      * @return
      */
-    @PostMapping("/addVisitPlan")
-    public HttpRespMsg addVisitPlan(
+    @PostMapping("/addOrUpdateVisitPlan")
+    public HttpRespMsg addOrUpdateVisitPlan(
+            @RequestParam(value = "planId",required = false) Long planId,
             @RequestParam("planName") String planName,
             @RequestParam("customId") Integer customId,
-            @RequestParam("customName") String customName,
             @RequestParam("inchargerId") String inchargerId,
             @RequestParam("visitGoal") Integer visitGoal,
             @RequestParam("visitTime") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")Date visitTime,
             @RequestParam("remark") String remark,
             @RequestParam("remindType") Integer remindType,
-            @RequestParam(value = "remindTime",required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")Date remindTime
-            , HttpServletRequest request) {
+            @RequestParam(value = "remindTime",required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")Date remindTime,
+            HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         VisitPlan visitPlan = new VisitPlan();
+        visitPlan.setId(planId);
         visitPlan.setPlanName(planName);
         visitPlan.setCustomId(customId);
-        visitPlan.setCustomName(customName);
         visitPlan.setInchargerId(inchargerId);
         visitPlan.setVisitGoal(visitGoal);
         visitPlan.setVisitTime(visitTime);
         visitPlan.setRemark(remark);
         visitPlan.setRemindType(remindType);
         visitPlan.setRemindTime(remindTime);
-        httpRespMsg = visitPlanService.addVisitPlan(visitPlan,request);
+        httpRespMsg = visitPlanService.addOrUpdateVisitPlan(visitPlan,request);
 
         return httpRespMsg;
     }
@@ -114,31 +114,40 @@ public class VisitPlanController {
      * @param request
      * @return
      */
-    @PostMapping("/getPageVisitPlan")
-    public HttpRespMsg getPageVisitPlan(@RequestParam(value = "pageIndex",required = false) Integer pageIndex
+    @PostMapping("/getVisitPlanList")
+    public HttpRespMsg getVisitPlan(@RequestParam(value = "pageIndex",required = false) Integer pageIndex
             , @RequestParam(value = "pageSize",required = false) Integer pageSize
             ,@RequestParam("calenderDate")String calenderDate
             ,HttpServletRequest request){
         HttpRespMsg httpRespMsg = new HttpRespMsg();
-        httpRespMsg = visitPlanService.getPageVisitPlan(pageIndex,pageSize,calenderDate,request);
+        httpRespMsg = visitPlanService.getVisitPlanList(pageIndex,pageSize,calenderDate,request);
         return httpRespMsg;
     }
 
+    @PostMapping("/getVisitPlanDetail")
+    public HttpRespMsg getVisitPlan(@RequestParam("planId")Long planId
+            ,HttpServletRequest request){
+        return visitPlanService.getVisitPlanDetail(planId,request);
+    }
+
+
+
 
-//    @GetMapping("/getDelayQueue")
-//    public HttpRespMsg getDelayQueue(HttpServletRequest request) {
-//        HttpRespMsg httpRespMsg = new HttpRespMsg();
-//        VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
-//        httpRespMsg.setData(tool.getAll());
-//        Object[] all =  tool.getAll();
-//        for (Object obj : all) {
-//            VisitPlanDelayItem item = (VisitPlanDelayItem) obj;
-//            long delay = item.getDelay(TimeUnit.SECONDS);
-//            System.out.println("item== "+item.getVisitPlan().getPlanName()
-//                    +",delayTime=== "+item.getDelayTime()
-//            +",delay=== "+delay
-//            );
-//        }
-//        return httpRespMsg;
-//    }
+
+    @GetMapping("/getDelayQueue")
+    public HttpRespMsg getDelayQueue(HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
+        httpRespMsg.setData(tool.getAll());
+        Object[] all =  tool.getAll();
+        for (Object obj : all) {
+            VisitPlanDelayItem item = (VisitPlanDelayItem) obj;
+            long delay = item.getDelay(TimeUnit.SECONDS);
+            System.out.println("item== "+item.getVisitPlan().getPlanName()
+                    +",delayTime=== "+item.getDelayTime()
+            +",delay=== "+delay
+            );
+        }
+        return httpRespMsg;
+    }
 }

+ 20 - 9
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java

@@ -1,22 +1,21 @@
 package com.management.platform.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.extension.activerecord.Model;
-import com.baomidou.mybatisplus.annotation.TableId;
-
-import java.math.BigDecimal;
 import com.baomidou.mybatisplus.annotation.TableField;
-import java.io.Serializable;
-import java.time.LocalDate;
-import java.util.Date;
-import java.util.List;
-
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 import org.springframework.format.annotation.DateTimeFormat;
 
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.List;
+
 /**
  * <p>
  * 
@@ -181,6 +180,18 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("plate5")
     private String plate5;
+
+    /**是否需要置顶 0/Flase 1/True*/
+    @TableField("need_pin")
+    private Boolean needPin;
+
+    /**被置顶时间*/
+    @TableField("pin_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date pinTime;
+
+
 //    @JsonDeserialize(using = BusinessItemProductListDeserializer.class)
     @TableField(exist = false)
     private String businessItemProductList;

+ 16 - 9
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Custom.java

@@ -1,21 +1,19 @@
 package com.management.platform.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.extension.activerecord.Model;
-import com.baomidou.mybatisplus.annotation.TableId;
-
-import java.time.LocalDateTime;
-
 import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
-
 /**
  * <p>
  *
@@ -235,6 +233,15 @@ public class Custom extends Model<Custom> {
     @TableField("close_deal")
     private Integer closeDeal;
 
+    /**是否需要置顶 0/Flase 1/True*/
+    @TableField("need_pin")
+    private Boolean needPin;
+
+    /**被置顶时间*/
+    @TableField("pin_time")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date pinTime;
 
     @TableField(exist = false)
     private String ids;

+ 3 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SysDict.java

@@ -66,7 +66,7 @@ public class SysDict extends Model<SysDict> {
         return this.id;
     }
 
-    public static final String[] TYPE_LIST=new String[]{"ClueSources","CustomLevel","CustomIndustry","CustomSources","BusinessStage","ProductType","ProductUnit","OrderType","ContractType"};
+    public static final String[] TYPE_LIST=new String[]{"ClueSources","CustomLevel","CustomIndustry","CustomSources","BusinessStage","ProductType","ProductUnit","OrderType","ContractType","RemindType"};
 
     public static List<Map<String,Object>> getSysDictList(){
         List<Map<String,Object>> itemList=new ArrayList<>();
@@ -92,6 +92,8 @@ public class SysDict extends Model<SysDict> {
                     break;
                 case "ContractType":map.put("name","合同类型");
                     break;
+                case "RemindType":map.put("name","提醒类型");
+                    break;
             }
             itemList.add(map);
         }

+ 5 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/VisitPlan.java

@@ -29,6 +29,9 @@ public class VisitPlan extends Model<VisitPlan> {
     /**客户名称*/
     @TableField(value = "custom_name")
     private String customName;
+    /**公司表id*/
+    @TableField(value = "company_id")
+    private Integer companyId;
     /**负责人id(user表)*/
     @TableField(value = "incharger_id")
     private String inchargerId;
@@ -38,7 +41,7 @@ public class VisitPlan extends Model<VisitPlan> {
     /**拜访时间*/
     @TableField(value = "visit_time")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone = "GMT+8")
     private Date visitTime;
     /**备注*/
     @TableField(value = "remark")
@@ -49,7 +52,7 @@ public class VisitPlan extends Model<VisitPlan> {
     /**提醒时间[自定义时间时再填充]*/
     @TableField(value = "remind_time")
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     private Date remindTime;
 
     /**是否达到提醒时间 0未到 1已到 2不需要提醒*/

+ 1 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/UserCommonModuleVO.java

@@ -6,4 +6,5 @@ import lombok.Data;
 public class UserCommonModuleVO {
     private Integer moduleId;
     private String moduleName;
+    private String path;
 }

+ 45 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/VisitPlanDetailVO.java

@@ -0,0 +1,45 @@
+package com.management.platform.entity.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.management.platform.entity.Contacts;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class VisitPlanDetailVO {
+    private Long id;
+    private String planName;
+    private Integer customId;
+    private String customName;
+    private String inchargerId;
+    /**负责人名称(user表)*/
+    private String inchargerName;
+    /**拜访目的 字典表id*/
+    private Integer visitGoal;
+    private String visitGoalName;
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone = "GMT+8")
+    private Date visitTime;
+    /**备注*/
+    private String remark;
+    /**提醒类型 字典表id,对应字典项name类型名称 ex1时间[秒]*/
+    private Integer remindType;
+    private String remindTypeName;
+    /**提醒时间[自定义时间时再填充]*/
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date remindTime;
+    /**是否达到提醒时间 0未到 1已到 2不需要提醒*/
+    private Integer remindState;
+    /**完成状态 0未完成 1已完成*/
+    private Integer finishState;
+
+    /**客户电话*/
+    private String telPhone;
+
+    /**联系人*/
+    private List<Contacts> contacts;
+}

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

@@ -8,26 +8,33 @@ import java.util.Date;
 
 @Data
 public class VisitPlanVO {
+    private Long id;
     private String planName;
+    private Integer customId;
     private String customName;
+    private String inchargerId;
     /**负责人名称(user表)*/
     private String inchargerName;
     /**拜访目的 字典表id*/
     private Integer visitGoal;
-//    private String visitGoalText;
+    private String visitGoalName;
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone = "GMT+8")
     private Date visitTime;
     /**备注*/
     private String remark;
     /**提醒类型 字典表id,对应字典项name类型名称 ex1时间[秒]*/
     private Integer remindType;
+    private String remindTypeName;
     /**提醒时间[自定义时间时再填充]*/
     @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     private Date remindTime;
     /**是否达到提醒时间 0未到 1已到 2不需要提醒*/
     private Integer remindState;
     /**完成状态 0未完成 1已完成*/
     private Integer finishState;
+
+    /**客户电话*/
+    private String telPhone;
 }

+ 7 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/BusinessOpportunityMapper.java

@@ -1,11 +1,9 @@
 package com.management.platform.mapper;
 
-import com.management.platform.entity.BusinessOpportunity;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.BusinessOpportunity;
 import com.management.platform.entity.User;
-import com.management.platform.util.HttpRespMsg;
 import org.apache.ibatis.annotations.Param;
-import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 import java.util.Map;
@@ -43,4 +41,10 @@ public interface BusinessOpportunityMapper extends BaseMapper<BusinessOpportunit
     List<BusinessOpportunity> getAllList1(User user);
 
     List<BusinessOpportunity> getAllList2(User user);
+
+    List<BusinessOpportunity> selectAllListByPin(BusinessOpportunity bo);
+
+    List<BusinessOpportunity> selectAllList1ByPin(@Param("bo") BusinessOpportunity bo, @Param("userId")String userId);
+
+    List<BusinessOpportunity> selectAllList2ByPin(@Param("bo") BusinessOpportunity bo, @Param("userId")String userId);
 }

+ 7 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/CustomMapper.java

@@ -1,9 +1,7 @@
 package com.management.platform.mapper;
 
-import com.management.platform.entity.Clue;
-import com.management.platform.entity.Custom;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.management.platform.util.HttpRespMsg;
+import com.management.platform.entity.Custom;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
@@ -34,4 +32,10 @@ public interface CustomMapper extends BaseMapper<Custom> {
     Custom getInfo(Integer id);
 
     Map<String, Object> getDataSummary(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
+
+    List<Custom> getListByPin(Custom custom);
+
+    List<Custom> getList1ByPin(Custom custom);
+
+    List<Custom> getList2ByPin(Custom custom);
 }

+ 3 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/VisitPlanMapper.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.management.platform.entity.VisitPlan;
 import com.management.platform.entity.dto.QueryVisitPlanDTO;
+import com.management.platform.entity.vo.VisitPlanDetailVO;
 import com.management.platform.entity.vo.VisitPlanVO;
 import org.apache.ibatis.annotations.Param;
 
@@ -13,4 +14,6 @@ public interface VisitPlanMapper extends BaseMapper<VisitPlan> {
     IPage<VisitPlanVO> getPageVisitPlan(IPage<VisitPlan> page, @Param("query") QueryVisitPlanDTO query);
 
     List<VisitPlanVO> getListVisitPlan(@Param("query")QueryVisitPlanDTO query);
+
+    VisitPlanDetailVO getDetail(@Param("planId") Long planId);
 }

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

@@ -1,9 +1,10 @@
 package com.management.platform.service;
 
-import com.management.platform.entity.*;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.BusinessOpportunity;
+import com.management.platform.entity.UploadFile;
+import com.management.platform.entity.User;
 import com.management.platform.util.HttpRespMsg;
-import org.apache.ibatis.annotations.Param;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
@@ -68,4 +69,7 @@ public interface BusinessOpportunityService extends IService<BusinessOpportunity
 
     void saveStage(BusinessOpportunity bo, User user);
 
+    HttpRespMsg listByPin(BusinessOpportunity bo, HttpServletRequest request);
+
+    HttpRespMsg pinBusinessOpportunity(BusinessOpportunity bo, HttpServletRequest request);
 }

+ 3 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/CustomService.java

@@ -61,4 +61,7 @@ public interface CustomService extends IService<Custom> {
 
     HttpRespMsg importData(MultipartFile multipartFile);
 
+    HttpRespMsg listByPin(Custom custom, HttpServletRequest request);
+
+    HttpRespMsg pinCutom(Custom custom, HttpServletRequest request);
 }

+ 5 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/VisitPlanService.java

@@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
 
 public interface VisitPlanService extends IService<VisitPlan> {
-    HttpRespMsg addVisitPlan(VisitPlan visitPlan, HttpServletRequest request);
+    HttpRespMsg addOrUpdateVisitPlan(VisitPlan visitPlan, HttpServletRequest request);
 
     HttpRespMsg finishVisitPlan(Long planId, HttpServletRequest request);
 
@@ -16,5 +16,8 @@ public interface VisitPlanService extends IService<VisitPlan> {
 
     HttpRespMsg delVisitPlan(Long planId, HttpServletRequest request);
 
-    HttpRespMsg getPageVisitPlan(Integer pageIndex, Integer pageSize, String calenderDate, HttpServletRequest request);
+    HttpRespMsg getVisitPlanList(Integer pageIndex, Integer pageSize, String calenderDate, HttpServletRequest request);
+
+    HttpRespMsg getVisitPlanDetail(Long planId, HttpServletRequest request);
+
 }

+ 51 - 7
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java

@@ -3,15 +3,15 @@ package com.management.platform.service.impl;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.TypeReference;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.*;
 import com.management.platform.service.BusinessOpportunityService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.service.SysFunctionService;
 import com.management.platform.service.WxCorpInfoService;
 import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.FileUtil;
@@ -38,11 +38,7 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.net.URLEncoder;
-import java.text.SimpleDateFormat;
 import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
@@ -91,6 +87,11 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     @Resource
     private StageMapper stageMapper;
 
+    @Resource
+    private SysFunctionService sysFunctionService;
+    @Autowired
+    private BusinessOpportunityMapper businessOpportunityMapper;
+
     @Override
     public List<BusinessOpportunity> getAll(BusinessOpportunity bo) {
         return bOMapper.selectAllList(bo);
@@ -743,6 +744,49 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
         actionLogMapper.insert(al);
     }
 
+    @Override
+    public HttpRespMsg listByPin(BusinessOpportunity bo, HttpServletRequest request) {
+        HashMap<Object, Object> r = new HashMap<>();
+        User user = userMapper.selectById(request.getHeader("Token"));
+        bo.setIsDelete(0);
+        bo.setCompanyId(user.getCompanyId());
+        bo.setUserId(user.getId());
+        bo.setEndTime(bo.getEndTime() + " 23:59:59");
+        bo.setPageIndex((bo.getPageIndex()-1) * bo.getPageFrom());
+        List<BusinessOpportunity> list = new ArrayList<>();
+        boolean isAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部商机");
+        boolean isNotAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看负责部门商机");
+        int i = 0;
+        if (isAll) {
+            list = bOMapper.selectAllListByPin(bo);
+            i = bOMapper.getTotal(bo);
+        } else if (isNotAll) {
+            list = bOMapper.selectAllList1ByPin(bo, user.getId());
+            i = bOMapper.getTotal1(bo, user.getId());
+        } else {
+            list = bOMapper.selectAllList2ByPin(bo, user.getId());
+            i = bOMapper.getTotal2(bo, user.getId());
+        }
+        r.put("data", list);
+        r.put("total",i);
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.setData(r);
+        return msg;
+
+    }
+
+    @Override
+    public HttpRespMsg pinBusinessOpportunity(BusinessOpportunity bo, HttpServletRequest request) {
+        HttpRespMsg respMsg = new HttpRespMsg();
+//        User user = userMapper.selectById(request.getHeader("Token"));
+        businessOpportunityMapper.update(null,new LambdaUpdateWrapper<BusinessOpportunity>()
+                .set(BusinessOpportunity::getNeedPin,1)
+                .set(BusinessOpportunity::getPinTime,new Date())
+                .eq(BusinessOpportunity::getId,bo.getId())
+        );
+        return respMsg;
+    }
+
 
     private BusinessOpportunity setNull(BusinessOpportunity bo) {
         if (bo.getPlate1() == "") {

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

@@ -5,6 +5,7 @@ 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.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.management.platform.entity.*;
 import com.management.platform.entity.vo.DepartmentVO;
@@ -865,6 +866,53 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
         return msg;
     }
 
+    @Override
+    public HttpRespMsg listByPin(Custom custom, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        custom.setCompanyId(user.getCompanyId());
+        custom.setIsDelete(0);
+        custom.setUserId(user.getId());
+        custom.setEndTime(custom.getEndTime() + " 23:59:59");
+        if (custom.getPageIndex()!=null){
+            custom.setPageIndex((custom.getPageIndex() - 1) * custom.getPageFrom());
+        }
+        boolean isAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部客户");
+        boolean isNotAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看负责部门客户");
+        List<Custom> list = new ArrayList<>();
+        int i = 0;
+        if (isAll) {
+            //查看全部线索
+            list = customMapper.getListByPin(custom);
+            i = customMapper.getTotal(custom);
+        } else if (isNotAll) {
+            //查看负责部门线索 找出所处部门下所有的负责人
+            list = customMapper.getList1ByPin(custom);
+            i = customMapper.getTotal1(custom);
+        } else {
+            // 查看负责人为 自己 和 null的数据
+            list = customMapper.getList2ByPin(custom);
+            i = customMapper.getTotal2(custom);
+        }
+        HashMap<Object, Object> map = new HashMap<>();
+        map.put("data", list);
+        map.put("total", i);
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.setData(map);
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg pinCutom(Custom custom, HttpServletRequest request) {
+        HttpRespMsg respMsg = new HttpRespMsg();
+//        User user = userMapper.selectById(request.getHeader("Token"));
+        customMapper.update(null,new LambdaUpdateWrapper<Custom>()
+                .set(Custom::getNeedPin,1)
+                .set(Custom::getPinTime,new Date())
+                .eq(Custom::getId,custom.getId())
+        );
+        return respMsg;
+    }
+
 
     private Custom setNull(Custom clue) {
         if (clue.getPlate1() == "") {

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

@@ -83,6 +83,7 @@ public class UserCommonModuleServiceImpl extends ServiceImpl<UserCommonModuleMap
                 }
                 if(CollectionUtils.isNotEmpty(toAddList)){
                     userCommonModuleMapper.insertBatch(toAddList);
+                    commonModules = userCommonModuleMapper.getCommonModules(user.getId(),user.getCompanyId());
                 }
             }
         }

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

@@ -3,25 +3,23 @@ package com.management.platform.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.management.platform.entity.SysDict;
-import com.management.platform.entity.User;
-import com.management.platform.entity.VisitPlan;
+import com.management.platform.entity.*;
 import com.management.platform.entity.dto.QueryVisitPlanDTO;
+import com.management.platform.entity.vo.VisitPlanDetailVO;
 import com.management.platform.entity.vo.VisitPlanVO;
-import com.management.platform.mapper.SysDictMapper;
-import com.management.platform.mapper.UserMapper;
-import com.management.platform.mapper.VisitPlanMapper;
+import com.management.platform.mapper.*;
 import com.management.platform.service.VisitPlanService;
 import com.management.platform.time.VisitPlanDelayHandler;
 import com.management.platform.time.VisitPlanDelayItem;
 import com.management.platform.util.HttpRespMsg;
 import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
-import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -31,7 +29,8 @@ import java.util.stream.Collectors;
 public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan> implements VisitPlanService {
 
 
-    @Resource
+    @Autowired
+    @Qualifier(value = "VisitPlanThreadPool")
     ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
 
@@ -43,9 +42,14 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
 
     @Resource
     private SysDictMapper sysDictMapper;
+    @Resource
+    private CustomMapper customMapper;
+
+    @Resource
+    private ContactsMapper contactsMapper;
 
     @Override
-    public HttpRespMsg addVisitPlan(VisitPlan visitPlan, HttpServletRequest request) {
+    public HttpRespMsg addOrUpdateVisitPlan(VisitPlan visitPlan, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         Date now = new Date();
         User user= userMapper.selectById(request.getHeader("token"));
@@ -53,7 +57,6 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
             httpRespMsg.setError("用户不存在");
             return httpRespMsg;
         }
-
         if(visitPlan.getVisitTime().before(now)){
             httpRespMsg.setError("拜访时间不能在当前时段之前");
             return httpRespMsg;
@@ -68,54 +71,118 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
             return httpRespMsg;
         }
         Map<Integer, SysDict> timeTypeMap = remindTypeList.stream().collect(Collectors.toMap(SysDict::getId, t->t));
-        SysDict timeTypeDict = timeTypeMap.get(visitPlan.getRemindType());
-        if(null == timeTypeDict){
-            httpRespMsg.setError("提醒类型不存在,请重新填写");
-            return httpRespMsg;
+        SysDict timeTypeDict = null;
+        if(-1 != visitPlan.getRemindType()){ //timeType自定义时间不能作为字典项传输,写死为 -1
+            timeTypeDict = timeTypeMap.get(visitPlan.getRemindType());
+            if(null == timeTypeDict){
+                httpRespMsg.setError("提醒类型不存在,请重新填写");
+                return httpRespMsg;
+            }
         }
 
-        Long delayTime = 0L;
-        Long startTimeMilliSec = 0L;
-        Long endTimeMilliSec = 0L;
-        if(timeTypeDict.getName().equals("不提醒")){
-            //不加入队列,不需要提醒
-            visitPlan.setRemindState(2);
-        } else if (timeTypeDict.getName().equals("自定义时间")) {
-            //重新计算delayTime 自定义时间-当前时间
-            if(visitPlan.getRemindTime().before(now)
-                    || visitPlan.getRemindTime().after(visitPlan.getVisitTime())){
-                httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
+        if(null != visitPlan.getId()){
+            //修改
+            Date visitTime = visitPlanMapper.selectById(visitPlan.getId()).getVisitTime();
+            long milliCosts = visitTime.getTime() - now.getTime();
+            if(milliCosts > 30 * 60 * 1000){
+                httpRespMsg.setError("原拜访时间为近半小时内的数据无法修改");
                 return httpRespMsg;
             }
-            //毫秒
-            startTimeMilliSec = now.getTime();
-            endTimeMilliSec = visitPlan.getRemindTime().getTime();
-            delayTime = endTimeMilliSec - startTimeMilliSec;
+            Long delayTime = 0L;
+            Long startTimeMilliSec = 0L;
+            Long endTimeMilliSec = 0L;
+            boolean timeTypeCheck = false;
+            if(-1 == visitPlan.getRemindType()){
+                //重新计算delayTime 自定义时间-当前时间
+                if(visitPlan.getRemindTime().before(now)
+                        || visitPlan.getRemindTime().after(visitPlan.getVisitTime())){
+                    httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
+                    return httpRespMsg;
+                }
+                //毫秒
+                startTimeMilliSec = now.getTime();
+                endTimeMilliSec = visitPlan.getRemindTime().getTime();
+                delayTime = endTimeMilliSec - startTimeMilliSec;
+                timeTypeCheck = true;
+            } else if (timeTypeDict.getName().equals("不提醒")) {
+                //不加入队列,不需要提醒
+                visitPlan.setRemindState(2);
+            }else{
+                startTimeMilliSec = now.getTime();
+                endTimeMilliSec = visitPlan.getVisitTime().getTime()-Integer.parseInt(timeTypeDict.getExt1()) * 1000L;
+                if(endTimeMilliSec < startTimeMilliSec){
+                    httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
+                    return httpRespMsg;
+                }
+                delayTime = endTimeMilliSec-startTimeMilliSec;
+                timeTypeCheck=true;
+            }
+
+            visitPlanMapper.updateById(visitPlan);
+            VisitPlan newPlan = visitPlanMapper.selectById(visitPlan.getId());//前端某些字段不传,以防万一
+
+            //从队列中取出,并重新计算时间入队列
+            if(timeTypeCheck){
+                VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
+                tool.removeIfExist(visitPlan.getId());
+                VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
+                        ,startTimeMilliSec
+                        ,endTimeMilliSec
+                        ,newPlan.getId(),newPlan);
+                tool.addItem(item);
+            }
+
         }else{
-            startTimeMilliSec = now.getTime();
-            endTimeMilliSec = visitPlan.getVisitTime().getTime()-Integer.parseInt(timeTypeDict.getExt1()) * 1000L;
-            if(endTimeMilliSec < startTimeMilliSec){
-                httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
-                return httpRespMsg;
+            //新增
+            visitPlan.setCompanyId(user.getCompanyId());
+            Long delayTime = 0L;
+            Long startTimeMilliSec = 0L;
+            Long endTimeMilliSec = 0L;
+            boolean timeTypeCheck = false;
+            if(-1 == visitPlan.getRemindType()){
+                //重新计算delayTime 自定义时间-当前时间
+                if(visitPlan.getRemindTime().before(now)
+                        || visitPlan.getRemindTime().after(visitPlan.getVisitTime())){
+                    httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
+                    return httpRespMsg;
+                }
+                //毫秒
+                startTimeMilliSec = now.getTime();
+                endTimeMilliSec = visitPlan.getRemindTime().getTime();
+                delayTime = endTimeMilliSec - startTimeMilliSec;
+                timeTypeCheck = true;
+            } else if (timeTypeDict.getName().equals("不提醒")) {
+                //不加入队列,不需要提醒
+                visitPlan.setRemindState(2);
+            }else{
+                startTimeMilliSec = now.getTime();
+                endTimeMilliSec = visitPlan.getVisitTime().getTime()-Integer.parseInt(timeTypeDict.getExt1()) * 1000L;
+                if(endTimeMilliSec < startTimeMilliSec){
+                    httpRespMsg.setError("提醒时间不能在当前时段之前或拜访时间之后");
+                    return httpRespMsg;
+                }
+                delayTime = endTimeMilliSec-startTimeMilliSec;
+                timeTypeCheck=true;
             }
-            delayTime = endTimeMilliSec-startTimeMilliSec;
-        }
 
-        visitPlan.setCreateBy(user.getId());
-        visitPlanMapper.insert(visitPlan);
+            Custom custom = customMapper.selectById(visitPlan.getCustomId());
+            visitPlan.setCustomName(custom.getCustomName());
+            visitPlan.setCreateBy(user.getId());
+            visitPlanMapper.insert(visitPlan);
 
-        //加入执行队列
-        if(!timeTypeDict.getName().equals("不提醒")){
-            VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
-            VisitPlan taskPlan = visitPlanMapper.selectById(visitPlan.getId());
-            VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
-                    ,startTimeMilliSec
-                    ,endTimeMilliSec
-                    ,taskPlan.getId(),taskPlan);
-            tool.addItem(item);
-            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-            System.out.println("当前时间: "+format.format(new Date()));
-            threadPoolTaskExecutor.execute(tool);
+            //加入执行队列
+            if(timeTypeCheck){
+                VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
+                VisitPlan taskPlan = visitPlanMapper.selectById(visitPlan.getId());
+                VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
+                        ,startTimeMilliSec
+                        ,endTimeMilliSec
+                        ,taskPlan.getId(),taskPlan);
+                tool.addItem(item);
+//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//            System.out.println("当前时间: "+format.format(new Date()));
+                threadPoolTaskExecutor.execute(tool);
+            }
         }
         return httpRespMsg;
     }
@@ -152,19 +219,22 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
             return httpRespMsg;
         }
         VisitPlan taskPlan = visitPlanMapper.selectById(planId);
-        SysDict timeTypeDict = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>()
-                .eq(SysDict::getCode, "RemindType")
-                .eq(SysDict::getId, taskPlan.getRemindType())
-                .eq(SysDict::getCompanyId, user.getCompanyId())
-        );
+        SysDict timeTypeDict = null;
+        if(-1 != taskPlan.getRemindType()){
+            timeTypeDict = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>()
+                    .eq(SysDict::getCode, "RemindType")
+                    .eq(SysDict::getId, taskPlan.getRemindType())
+                    .eq(SysDict::getCompanyId, user.getCompanyId())
+            );
+        }
+
 
         Long delayTime = 0L;
         Long startTimeMilliSec = 0L;
         Long endTimeMilliSec = 0L;
+        boolean timeTypeCheck = false;
         taskPlan.setVisitTime(newVisitTime);
-        if(timeTypeDict.getName().equals("不提醒")){
-            //不加入队列,不修改状态,只修改拜访时间
-        } else if (timeTypeDict.getName().equals("自定义时间")) {
+        if(-1 == taskPlan.getRemindType()){
             //重新计算delayTime 自定义时间-当前时间
             if(taskPlan.getRemindTime().before(now)
                     || taskPlan.getRemindTime().after(newVisitTime)){
@@ -175,6 +245,9 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
             startTimeMilliSec = now.getTime();
             endTimeMilliSec = taskPlan.getRemindTime().getTime();
             delayTime = endTimeMilliSec - startTimeMilliSec;
+            timeTypeCheck=true;
+        }else if (timeTypeDict.getName().equals("不提醒")){
+            //不加入队列,不修改状态,只修改拜访时间
         }else{
             startTimeMilliSec = now.getTime();
             endTimeMilliSec = newVisitTime.getTime()-Integer.parseInt(timeTypeDict.getExt1()) * 1000L;
@@ -183,17 +256,20 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
                 return httpRespMsg;
             }
             delayTime = endTimeMilliSec-startTimeMilliSec;
+            timeTypeCheck=true;
         }
 
         //删除队列
         VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
         tool.removeIfExist(planId);
         //重新添加
-        VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
-                ,startTimeMilliSec
-                ,endTimeMilliSec
-                ,taskPlan.getId(),taskPlan);
-        tool.addItem(item);
+        if(timeTypeCheck){
+            VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
+                    ,startTimeMilliSec
+                    ,endTimeMilliSec
+                    ,taskPlan.getId(),taskPlan);
+            tool.addItem(item);
+        }
 
         visitPlanMapper.update(null,new LambdaUpdateWrapper<VisitPlan>()
                 .set(VisitPlan::getVisitTime,newVisitTime)
@@ -224,7 +300,7 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
     }
 
     @Override
-    public HttpRespMsg getPageVisitPlan(Integer pageIndex, Integer pageSize, String calenderDate, HttpServletRequest request) {
+    public HttpRespMsg getVisitPlanList(Integer pageIndex, Integer pageSize, String calenderDate, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         User user= userMapper.selectById(request.getHeader("token"));
         if(null == user){
@@ -242,4 +318,15 @@ public class VisitPlanServiceImpl extends ServiceImpl<VisitPlanMapper, VisitPlan
         httpRespMsg.setData(resPage);
         return httpRespMsg;
     }
+
+    @Override
+    public HttpRespMsg getVisitPlanDetail(Long planId, HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        VisitPlanDetailVO detailVO = visitPlanMapper.getDetail(planId);
+        List<Contacts> contacts = contactsMapper.selectList(new LambdaQueryWrapper<Contacts>()
+                .eq(Contacts::getCustomId, detailVO.getCustomId()));
+        detailVO.setContacts(contacts);
+        httpRespMsg.setData(detailVO);
+        return httpRespMsg;
+    }
 }

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

@@ -91,6 +91,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 
     public static final int TEXT_CARD_MSG__ASYNC_DWONLOAD = 23;//文件异步下载
 
+    public static final int TEXT_CARD_MSG_VISIT_PLAN = 25;//访客计划
+
     private static Object userLock = new Object();
 
     @Value("${suitId}")
@@ -291,6 +293,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     title = "日报审核通过";
                 } else if (msgType.equals(TEXT_CARD_MSG_TASK)) {
                     title = "客户管家:任务信息提醒";
+                } else if (msgType.equals(TEXT_CARD_MSG_VISIT_PLAN)) {
+                    title = "访客计划";
                 }
             } else {
                 jumpUrl = jumpUrl.replace("STATE", pageRouter);

+ 26 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanDelayHandler.java

@@ -1,11 +1,16 @@
 package com.management.platform.time;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.management.platform.entity.User;
 import com.management.platform.entity.VisitPlan;
+import com.management.platform.entity.WxCorpInfo;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.mapper.WxCorpInfoMapper;
 import com.management.platform.service.VisitPlanService;
+import com.management.platform.service.WxCorpInfoService;
+import com.management.platform.service.impl.WxCorpInfoServiceImpl;
 
-import java.text.SimpleDateFormat;
-import java.util.Date;
 import java.util.concurrent.DelayQueue;
 
 public class VisitPlanDelayHandler implements Runnable{
@@ -16,6 +21,15 @@ public class VisitPlanDelayHandler implements Runnable{
     private VisitPlanService visitPlanService =
             (VisitPlanService) ApplicationContextHelperUtil.getBean(VisitPlanService.class);
 
+    private WxCorpInfoMapper wxCorpInfoMapper =
+            (WxCorpInfoMapper) ApplicationContextHelperUtil.getBean(WxCorpInfoMapper.class);
+
+    private WxCorpInfoService wxCorpInfoService =
+            (WxCorpInfoService) ApplicationContextHelperUtil.getBean(WxCorpInfoService.class);
+
+    private UserMapper userMapper =
+            (UserMapper) ApplicationContextHelperUtil.getBean(UserMapper.class);
+
 
     public void addItem(VisitPlanDelayItem item) {
         VISIT_PLAN_DELAY_QUEUE.offer(item);
@@ -53,15 +67,23 @@ public class VisitPlanDelayHandler implements Runnable{
                 System.out.println("visitPlanService 未初始化");
                 return;
             }
-            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             //到达提醒时间,则修改 是否到达提醒时间为 已到
             visitPlanService.update(new LambdaUpdateWrapper<VisitPlan>()
                             .set(VisitPlan::getRemindState,1)
                     .eq(VisitPlan::getId, visitPlan.getId())
             );
-            System.out.println("修改时间: "+format.format(new Date()));
+//            System.out.println("修改时间: "+format.format(new Date()));
+            //发送企微消息通知
+            String createById = item.getVisitPlan().getCreateBy();
+            User user = userMapper.selectById(createById);
+            WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
+            if (wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                wxCorpInfoService.sendWXCorpMsg(wxCorpInfo,user.getCorpwxUserid(),"访客计划提醒",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_VISIT_PLAN);
+            }
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
+        System.out.println("===执行完成====");
     }
 }

+ 133 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanInit.java

@@ -0,0 +1,133 @@
+package com.management.platform.time;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.management.platform.entity.Company;
+import com.management.platform.entity.SysDict;
+import com.management.platform.entity.VisitPlan;
+import com.management.platform.mapper.CompanyMapper;
+import com.management.platform.mapper.SysDictMapper;
+import com.management.platform.mapper.VisitPlanMapper;
+import org.apache.commons.collections.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Component
+public class VisitPlanInit implements ApplicationRunner {
+
+    @Resource
+    private VisitPlanMapper visitPlanMapper;
+
+    @Resource
+    private SysDictMapper sysDictMapper;
+
+    @Resource
+    private CompanyMapper companyMapper;
+
+    @Autowired
+    @Qualifier(value = "VisitPlanThreadPool")
+    ThreadPoolTaskExecutor threadPoolTaskExecutor;
+
+    //检查表中数据,在项目重启后重新加入队列
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        System.out.println("====开始访客计划初始化=====");
+        Date now = new Date();
+        List<VisitPlan> visitPlans = visitPlanMapper.selectList(new LambdaQueryWrapper<VisitPlan>()
+                .ne(VisitPlan::getRemindState, 2)
+                .eq(VisitPlan::getFinishState, 0)
+                .gt(VisitPlan::getVisitTime, now)
+        );
+        if(CollectionUtils.isEmpty(visitPlans)){
+            return;
+        }
+        List<SysDict> remindTypeList = sysDictMapper.selectList(new LambdaQueryWrapper<SysDict>()
+                .eq(SysDict::getCode, "RemindType"));
+        if(CollectionUtils.isEmpty(remindTypeList)){
+            return;
+        }
+        List<Company> companies = companyMapper.selectList(new LambdaQueryWrapper<Company>()
+                .select(Company::getId)
+                .gt(Company::getExpirationDate, now)
+        );
+        Map<Integer, List<VisitPlan>> companyVisitPlanMap = visitPlans.stream().collect(Collectors.groupingBy(VisitPlan::getCompanyId));
+        Map<Integer, List<SysDict>> companyRemindTypeMap = remindTypeList.stream().collect(Collectors.groupingBy(SysDict::getCompanyId));
+        if(CollectionUtils.isNotEmpty(companies)){
+            for (Company company : companies) {
+                List<SysDict> remindTypes = companyRemindTypeMap.get(company.getId());
+                if(CollectionUtils.isEmpty(remindTypes)){
+                    continue;
+                }
+                List<VisitPlan> visitPlansList = companyVisitPlanMap.get(company.getId());
+                if(CollectionUtils.isEmpty(visitPlansList)){
+                    continue;
+                }
+                Map<Integer, SysDict> timeTypeMap = remindTypes.stream().collect(Collectors.toMap(SysDict::getId, t->t));
+                for (VisitPlan visitPlan : visitPlansList) {
+                    Long delayTime = 0L;
+                    Long startTimeMilliSec = 0L;
+                    Long endTimeMilliSec = 0L;
+                    boolean timeTypeCheck = false;
+                    if(-1 == visitPlan.getRemindType()){
+                        //重新计算delayTime 自定义时间-当前时间
+                        if(visitPlan.getRemindTime().before(now)
+                                || visitPlan.getRemindTime().after(visitPlan.getVisitTime())){
+                            //提醒时间不能在当前时段之前或拜访时间之后
+                            continue;
+                        }
+                        //毫秒
+                        startTimeMilliSec = now.getTime();
+                        endTimeMilliSec = visitPlan.getRemindTime().getTime();
+                        delayTime = endTimeMilliSec - startTimeMilliSec;
+                        timeTypeCheck = true;
+                    }else{
+                        SysDict timeTypeDict = timeTypeMap.get(visitPlan.getRemindType());
+                        if(null == timeTypeDict){
+                            continue;
+                        }
+                        if(visitPlan.getVisitTime().before(now)){
+                            //拜访时间不能在当前时段之前
+                            continue;
+                        }
+
+                        if(timeTypeDict.getName().equals("不提醒")){
+                            //不加入队列,不需要提醒
+                            visitPlan.setRemindState(2);
+                        }else{
+                            startTimeMilliSec = now.getTime();
+                            endTimeMilliSec = visitPlan.getVisitTime().getTime()-Integer.parseInt(timeTypeDict.getExt1()) * 1000L;
+                            if(endTimeMilliSec < startTimeMilliSec){
+                                //提醒时间不能在当前时段之前或拜访时间之后
+                                continue;
+                            }
+                            delayTime = endTimeMilliSec-startTimeMilliSec;
+                            timeTypeCheck= true;
+                        }
+                    }
+                    //加入执行队列
+                    if(timeTypeCheck){
+                        VisitPlanDelayHandler tool = new VisitPlanDelayHandler();
+                        VisitPlan taskPlan = visitPlanMapper.selectById(visitPlan.getId());
+                        VisitPlanDelayItem item = new VisitPlanDelayItem(delayTime
+                                ,startTimeMilliSec
+                                ,endTimeMilliSec
+                                ,taskPlan.getId(),taskPlan);
+                        tool.addItem(item);
+                        threadPoolTaskExecutor.execute(tool);
+                    }
+                }
+
+            }
+        }
+        System.out.println("====结束访客计划初始化=====");
+    }
+}

+ 117 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml

@@ -30,7 +30,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, name, contacts_id, customer_id,  amount_of_money, expected_transaction_date, stage_id, create_time, edit_time, creator_id, incharger_id, remark, is_delete,seq, plate1, plate2, plate3, plate4, plate5
+        id, company_id, name, contacts_id, customer_id,  amount_of_money, expected_transaction_date, stage_id, create_time, edit_time, creator_id, incharger_id, remark, is_delete,seq, plate1, plate2, plate3, plate4, plate5,need_pin ,pin_time
     </sql>
     <select id="selectAllList" resultType="com.management.platform.entity.BusinessOpportunity">
         select
@@ -310,4 +310,120 @@
           and (incharger_id = #{id} or incharger_id is null)
         order by create_time desc
     </select>
+    <select id="selectAllListByPin" resultType="com.management.platform.entity.BusinessOpportunity">
+        select
+        <include refid="Base_Column_List"></include>,
+        (select `name` from  stage where id =  stage_id) stageValue,
+        (select custom_name from custom where id = customer_id) customerName,
+        (select `name` from contacts where id = contacts_id) contactsName,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        (select `name` from `user` where id = creator_id) creatorName
+        from business_opportunity
+        where company_id = #{companyId}
+        and is_delete = #{isDelete}
+        <if test="name != null and name != ''">
+            and `name` LIKE CONCAT('%', #{name}, '%')
+        </if>
+        <if test="inchargerId != null and inchargerId != '' ">
+            and incharger_id = #{inchargerId}
+        </if>
+        <if test="startTime != null and endTime != null ">
+            and create_time BETWEEN  #{startTime} and #{endTime}
+        </if>
+        <if test="contactsName != null and contactsName != ''">
+            and contacts_id in (select id from contacts where `name`LIKE CONCAT('%', #{contactsName}, '%'))
+        </if>
+        <if test="customerName != null and customerName != ''">
+            and customer_id in (select id from custom where `custom_name`LIKE CONCAT('%', #{customerName}, '%'))
+        </if>
+        <if test="stageId != null ">
+            and stage_id = #{stageId}
+        </if>
+        <if test="productId != null ">
+            and id in (select business_id from business_item_product where product_id = #{productId})
+        </if>
+        ORDER BY need_pin desc,pin_time desc
+        <if test="pageIndex!=null and pageFrom !=null">
+            limit #{pageIndex},#{pageFrom}
+        </if>
+    </select>
+
+    <select id="selectAllList1ByPin" resultType="com.management.platform.entity.BusinessOpportunity">
+        select
+        <include refid="Base_Column_List"></include>,
+        (select `name` from  stage where id =  stage_id) stageValue,
+        (select custom_name from custom where id = customer_id) customerName,
+        (select `name` from contacts where id = contacts_id) contactsName,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        (select `name` from `user` where id = creator_id) creatorName
+        from business_opportunity
+        where company_id = #{bo.companyId}
+        and is_delete = #{bo.isDelete}
+        and (incharger_id in
+        (SELECT id from `user` WHERE department_id = (SELECT department_id from `user` WHERe id = #{userId}))
+        or incharger_id is null)
+        <if test="bo.name != null and bo.name != ''">
+            and `name` LIKE CONCAT('%', #{bo.name}, '%')
+        </if>
+        <if test="bo.inchargerId != null ">
+            and incharger_id = #{bo.inchargerId}
+        </if>
+        <if test="bo.startTime != null and bo.endTime != null ">
+            and create_time BETWEEN  #{bo.startTime} and #{bo.endTime}
+        </if>
+        <if test="bo.contactsName != null and bo.contactsName != ''">
+            and contacts_id in (select id from contacts where `name`LIKE CONCAT('%', #{bo.contactsName}, '%'))
+        </if>
+        <if test="bo.customerName != null and bo.customerName != ''">
+            and customer_id in (select id from custom where `name`LIKE CONCAT('%', #{bo.customerName}, '%'))
+        </if>
+        <if test="bo.stageId != null ">
+            and stage_id = #{bo.stageId}
+        </if>
+        <if test="bo.productId != null ">
+            and id in (select business_id from business_item_product where product_id = #{bo.productId})
+        </if>
+        ORDER BY need_pin desc,pin_time desc
+        <if test="bo.pageIndex!=null and bo.pageFrom !=null">
+            limit #{bo.pageIndex},#{bo.pageFrom}
+        </if>
+    </select>
+    <select id="selectAllList2ByPin" resultType="com.management.platform.entity.BusinessOpportunity">
+        select
+        <include refid="Base_Column_List"></include>,
+        (select `name` from  stage where id =  stage_id) stageValue,
+        (select custom_name from custom where id = customer_id) customerName,
+        (select `name` from contacts where id = contacts_id) contactsName,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        (select `name` from `user` where id = creator_id) creatorName
+        from business_opportunity
+        where company_id = #{bo.companyId}
+        and is_delete = #{bo.isDelete}
+        and (incharger_id = #{userId} or incharger_id is null)
+        <if test="bo.name != null and bo.name != ''">
+            and `name` LIKE CONCAT('%', #{bo.name}, '%')
+        </if>
+        <if test="bo.inchargerId != null ">
+            and incharger_id = #{bo.inchargerId}
+        </if>
+        <if test="bo.startTime != null and bo.endTime != null ">
+            and create_time BETWEEN  #{bo.startTime} and #{bo.endTime}
+        </if>
+        <if test="bo.contactsName != null and bo.contactsName != ''">
+            and contacts_id in (select id from contacts where `name`LIKE CONCAT('%', #{bo.contactsName}, '%'))
+        </if>
+        <if test="bo.customerName != null and bo.customerName != ''">
+            and customer_id in (select id from custom where `name`LIKE CONCAT('%', #{bo.customerName}, '%'))
+        </if>
+        <if test="bo.stageId != null ">
+            and stage_id = #{bo.stageId}
+        </if>
+        <if test="bo.productId != null ">
+            and id in (select business_id from business_item_product where product_id =#{bo.productId})
+        </if>
+        ORDER BY need_pin desc,pin_time desc
+        <if test="bo.pageIndex!=null and bo.pageFrom !=null">
+            limit #{bo.pageIndex},#{bo.pageFrom}
+        </if>
+    </select>
 </mapper>

+ 170 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CustomMapper.xml

@@ -384,5 +384,175 @@
             </foreach>
         </if>
     </select>
+    <select id="getListByPin" resultType="com.management.platform.entity.Custom">
+        select
+        c.id,
+        c.contacts_name,
+        c.custom_name,
+        c.email,
+        c.tel_phone,
+        c.company_phone,
+        c.custom_desc,
+        c.incharger_id,
+        c.address,
+        c.create_time,
+        c.customer_level_id,
+        (select name from sys_dict where customer_level_id = id and code = 'CustomLevel') customerLevelValue,
+        c.customer_industry_id,
+        (select name from sys_dict where customer_industry_id = id and code = 'CustomIndustry') customerIndustryValue,
+        c.custom_source_id,
+        (select name from sys_dict where custom_source_id = id) customSourceValue,
+        c.incharger_id,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        c.creator_id,
+        c.create_time,
+        (select `name` from `user` where id = creator_id) creatorName
+        from custom c
+        left join sys_dict sd on c.customer_level_id = sd.id
+        where c.company_id = #{companyId}
+        and c.is_delete =#{isDelete}
+        <if test="inchargerId != null and inchargerId != ''  ">
+            and c.incharger_id =#{inchargerId}
+        </if>
+        <if test="startTime != null and endTime != null ">
+            and c.create_time BETWEEN #{startTime} and #{endTime}
+        </if>
+        <if test="customName != null and customName != '' ">
+            and c.custom_name LIKE CONCAT('%', #{customName}, '%')
+        </if>
+        <if test="email != null and email != '' ">
+            and c.email LIKE CONCAT('%', #{email}, '%')
+        </if>
+        <if test="companyPhone != null and companyPhone != '' ">
+            and c.company_phone LIKE CONCAT('%', #{companyPhone}, '%')
+        </if>
+        <if test="customerLevelId != null ">
+            and c.customer_level_id = #{customerLevelId}
+        </if>
+        <if test="customSourceId != null ">
+            and c.custom_source_id = #{customSourceId}
+        </if>
+        <if test="customerIndustryId != null ">
+            and c.customer_industry_id = #{customerIndustryId}
+        </if>
+        ORDER BY c.need_pin desc,c.pin_time desc
+        <if test="pageFrom != null ">
+            Limit #{pageIndex},#{pageFrom}
+        </if>
+    </select>
+    <select id="getList1ByPin" resultType="com.management.platform.entity.Custom">
+        select c.id,
+        c.custom_name,
+        c.email,
+        c.tel_phone,
+        c.contacts_name,
+        c.company_phone,
+        c.custom_desc,
+        c.address,
+        c.create_time,
+        c.customer_level_id,
+        (select name from sys_dict where customer_level_id = id and code = 'CustomLevel') customerLevelValue,
+        c.customer_industry_id,
+        (select name from sys_dict where customer_industry_id = id and code = 'CustomIndustry') customerIndustryValue,
+        c.custom_source_id,
+        (select name from sys_dict where custom_source_id = id ) customSourceValue,
+        c.incharger_id,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        c.creator_id,
+        c.create_time,
+        (select `name` from `user` where id = creator_id) creatorName
+        from custom c
+        left join sys_dict sd on c.customer_level_id = sd.id
+        where
+        (c.incharger_id in
+        (SELECT id from `user` WHERE department_id = (SELECT department_id from `user` WHERE id = #{userId}))
+        or c.incharger_id is null)
+        and c.company_id = #{companyId}
+        and c.is_delete =#{isDelete}
+        <if test="inchargerId != null and inchargerId != ''  ">
+            and c.incharger_id =#{inchargerId}
+        </if>
+        <if test="startTime != null and endTime != null ">
+            and c.create_time BETWEEN #{startTime} and #{endTime}
+        </if>
+        <if test="customName != null and customName != '' ">
+            and c.custom_name LIKE CONCAT('%', #{customName}, '%')
+        </if>
+        <if test="email != null and email != '' ">
+            and c.email LIKE CONCAT('%', #{email}, '%')
+        </if>
+        <if test="companyPhone != null ">
+            and c.company_phone LIKE CONCAT('%', #{companyPhone}, '%')
+        </if>
+        <if test="customerLevelId != null ">
+            and c.customer_level_id = #{customerLevelId}
+        </if>
+        <if test="customSourceId != null ">
+            and c.custom_source_id = #{customSourceId}
+        </if>
+        <if test="customerIndustryId != null ">
+            and c.customer_industry_id = #{customerIndustryId}
+        </if>
+        ORDER BY c.need_pin desc,c.pin_time desc
+        <if test="pageFrom != null ">
+            Limit #{pageIndex},#{pageFrom}
+        </if>
+    </select>
+    <select id="getList2ByPin" resultType="com.management.platform.entity.Custom">
+        select id,
+        custom_name,
+        email,
+        contacts_name
+        tel_phone,contacts_name,
+        company_phone,
+        custom_desc,
+        contacts_name
+        incharger_id,
+        address,
+        create_time,
+        customer_level_id,
+        (select name from sys_dict where customer_level_id = id and code = 'CustomLevel') customerLevelValue,
+        customer_industry_id,
+        (select name from sys_dict where customer_industry_id = id and code = 'CustomIndustry') customerIndustryValue,
+        custom_source_id,
+        (select name from sys_dict where custom_source_id = id ) customSourceValue,
+        incharger_id,
+        (select `name` from `user` where id = incharger_id) inchargerName,
+        creator_id,
+        (select `name` from `user` where id = creator_id) creatorName
+        from custom
+        where company_id = #{companyId}
+        and is_delete =#{isDelete}
+        and (incharger_id = #{userId}
+        or incharger_id is null)
+        <if test="inchargerId != null and inchargerId != ''  ">
+            and incharger_id =#{inchargerId}
+        </if>
+        <if test="startTime != null and endTime != null ">
+            and create_time BETWEEN #{startTime} and #{endTime}
+        </if>
+        <if test="customName != null and customName != '' ">
+            and custom_name LIKE CONCAT('%', #{customName}, '%')
+        </if>
+        <if test="email != null and email != '' ">
+            and email LIKE CONCAT('%', #{email}, '%')
+        </if>
+        <if test="companyPhone != null ">
+            and company_phone LIKE CONCAT('%', #{companyPhone}, '%')
+        </if>
+        <if test="customerLevelId != null ">
+            and customer_level_id = #{customerLevelId}
+        </if>
+        <if test="customSourceId != null ">
+            and custom_source_id = #{customSourceId}
+        </if>
+        <if test="customerIndustryId != null ">
+            and customer_industry_id = #{customerIndustryId}
+        </if>
+        ORDER BY c.need_pin desc,c.pin_time desc
+        <if test="pageFrom != null ">
+            Limit #{pageIndex},#{pageFrom}
+        </if>
+    </select>
 
 </mapper>

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

@@ -10,7 +10,7 @@
         </foreach>
     </insert>
     <select id="getCommonModules" resultType="com.management.platform.entity.vo.UserCommonModuleVO">
-        select ucm.module_id,sm.name as moduleName
+        select ucm.module_id,sm.name as moduleName,sm.path
         from user_common_module ucm
                  left join sys_module sm on ucm.module_id = sm.id
         where user_id = #{userId} and company_id = #{companyId}

+ 21 - 5
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/VisitPlanMapper.xml

@@ -6,9 +6,9 @@
     <select id="getPageVisitPlan" resultType="com.management.platform.entity.vo.VisitPlanVO">
         select vs.id, vs.plan_name, vs.custom_id, vs.custom_name
              , vs.incharger_id, u.name as inchargerName
-             , vs.visit_goal,sd1.name as visitGoalText
+             , vs.visit_goal,sd1.name as visitGoalName
              , vs.visit_time, vs.remark
-             , vs.remind_type, sd2.name as remindTypeText
+             , vs.remind_type, case remind_type when -1 then '自定义时间' else sd2.name end as remindTypeName
              ,vs.remind_time, vs.remind_state, vs.finish_state
              , vs.create_by
         from visit_plan vs
@@ -20,15 +20,31 @@
     <select id="getListVisitPlan" resultType="com.management.platform.entity.vo.VisitPlanVO">
         select vs.id, vs.plan_name, vs.custom_id, vs.custom_name
              , vs.incharger_id, u.name as inchargerName
-             , vs.visit_goal,sd1.name as visitGoalText
+             , vs.visit_goal,sd1.name as visitGoalName
              , vs.visit_time, vs.remark
-             , vs.remind_type, sd2.name as remindTypeText
+             , vs.remind_type, case remind_type when -1 then '自定义时间' else sd2.name end as remindTypeName
              ,vs.remind_time, vs.remind_state, vs.finish_state
-             , vs.create_by
+             , vs.create_by,c.tel_phone
         from visit_plan vs
                  left join user u on vs.incharger_id = u.id
                  left join (select * from sys_dict where code = 'VisitGoal') sd1 on vs.visit_goal = sd1.id
                  left join (select * from sys_dict where code = 'RemindType') sd2 on vs.remind_type = sd2.id
+                 left join custom c on vs.custom_id = c.id
         where vs.create_by = #{query.userId} and date_format(vs.visit_time,'%Y-%m-%d') = #{query.calenderDate}
     </select>
+    <select id="getDetail" resultType="com.management.platform.entity.vo.VisitPlanDetailVO">
+        select vs.id, vs.plan_name, vs.custom_id, vs.custom_name
+             , vs.incharger_id, u.name as inchargerName
+             , vs.visit_goal,sd1.name as visitGoalName
+             , vs.visit_time, vs.remark
+             , vs.remind_type, case remind_type when -1 then '自定义时间' else sd2.name end as remindTypeName
+             ,vs.remind_time, vs.remind_state, vs.finish_state
+             , vs.create_by,c.tel_phone
+        from visit_plan vs
+                 left join user u on vs.incharger_id = u.id
+                 left join (select * from sys_dict where code = 'VisitGoal') sd1 on vs.visit_goal = sd1.id
+                 left join (select * from sys_dict where code = 'RemindType') sd2 on vs.remind_type = sd2.id
+                 left join custom c on vs.custom_id = c.id
+        where vs.id = #{planId}
+    </select>
 </mapper>

+ 38 - 32
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -902,9 +902,10 @@ public class ReportController {
             }
         }
         try {
+            User reportor = targetUidList == null ? user : userService.getById(targetUidList.get(0));
             List<AuditWorkflowTimeSetting> auditWorkflowList
                     = auditWorkflowTimeSettingMapper.selectList(
-                    new QueryWrapper<AuditWorkflowTimeSetting>().eq("dept_id", user.getDepartmentId()).orderByAsc("seq"));
+                    new QueryWrapper<AuditWorkflowTimeSetting>().eq("dept_id", reportor.getDepartmentId()).orderByAsc("seq"));
             List<Department> allDeptList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", user.getCompanyId()));
 
             //准备全局的校验数据
@@ -913,7 +914,7 @@ public class ReportController {
             if (comTimeType.getType() == 2) {
                 excludeTimeList = timeAutoExcludeMapper.selectList(new QueryWrapper<TimeAutoExclude>().eq("company_id", user.getCompanyId()));
                 //按当前填报人所属部门或者全公司来匹配
-                excludeTimeList = excludeTimeList.stream().filter(ex->ex.getDId() == null || ex.getDId().equals(user.getDepartmentId())).collect(Collectors.toList());
+                excludeTimeList = excludeTimeList.stream().filter(ex->ex.getDId() == null || ex.getDId().equals(reportor.getDepartmentId())).collect(Collectors.toList());
             }
 
             for (int i = 0; i < id.length; i++) {
@@ -1142,6 +1143,7 @@ public class ReportController {
                     } else {
                         //批量代填的情况
                         for (User subsUser : targetUserList) {
+                            System.out.println("这是代填=========");
                             Report report = new Report()
                                     .setId(id[i] == -1 ? null : id[i])
                                     .setProjectId(projectId[i])
@@ -1154,7 +1156,7 @@ public class ReportController {
                                     .setCustomData(customData[i])
                                     .setStage(stage != null && stage.length>0?stage[i]:"-")
 //                                    .setState(auditWorkflowList.size() == 0?1:0)//代填的如果没有自定义审核流程就直接审核通过了
-                                    .setCompanyId(user.getCompanyId())
+                                    .setCompanyId(subsUser.getCompanyId())
                                     .setPicAdd(pics!=null?pics[i]:null)
                                     .setPicStr(picStr!=null?picStr[i]:null)
                                     .setCreateDate(LocalDate.parse(createDate[i], DateTimeFormatter.ofPattern("yyyy-MM-dd")))
@@ -1162,7 +1164,7 @@ public class ReportController {
                                     .setDeptId(subsUser.getDepartmentId())
                                     .setProjectAuditorId(projectAuditorId[i])
                                     .setFillUserid(token);
-                            report.setProjectAuditState(1);
+                            report.setProjectAuditState(0);
                             report.setTaskFinish(taskFinish[i]);
                             report.setCustomText(customText[i]);
                             report.setBasecostId(basecostId[i]);
@@ -1179,34 +1181,37 @@ public class ReportController {
                                 report.setState(draft==0?0:3);
                             } else {
                                 //并非并行审核模式下的代填,需要设置审核状态
-                                if (auditWorkflowList.size() == 0) {
-                                    //没有自定义审核流,直接代填的,还是走正常的审核流程
-                                    report.setIsDeptAudit(0);
-                                    report.setIsFinalAudit(1);
-                                    report.setState(0);
-                                } else {
-                                    //有审核流程的,取项目经理后面的流程节点
-                                    int projectLeaderNodeIndex = 0;
-                                    for (int t=0;t<auditWorkflowList.size(); t++) {
-                                        if (auditWorkflowList.get(t).getIsDeptAudit() == 0) {
-                                            projectLeaderNodeIndex = t;
-                                            break;
-                                        }
-                                    }
-                                    if (projectLeaderNodeIndex == auditWorkflowList.size() -1) {
-                                        //最后一个节点就是项目经理,那就不用审核了,直接通过
-                                        report.setState(1);
-                                    } else {
-                                        //否则取下一个节点,待审核
-                                        report.setState(0);
-                                        int nextIndex = projectLeaderNodeIndex + 1;
-                                        AuditWorkflowTimeSetting nextNode = auditWorkflowList.get(nextIndex);
-                                        report.setIsFinalAudit((nextIndex == auditWorkflowList.size()-1)?1:0);
-                                        report.setIsDeptAudit(nextNode.getIsDeptAudit());
-                                        report.setAuditDeptid(nextNode.getAuditDeptId());
-                                        report.setAuditDeptManagerid(nextNode.getAuditDeptId() != null?allDeptList.stream().filter(d->d.getDepartmentId().equals(nextNode.getAuditDeptId())).findFirst().get().getManagerId(): null);
-                                    }
-                                }
+                                report.setState(0);
+                                setReportWorkflowAuditor(auditWorkflowList, allDeptList, report,comTimeType);
+//                                if (auditWorkflowList.size() == 0) {
+//                                    //没有自定义审核流,直接代填的,还是走正常的审核流程
+//                                    report.setIsDeptAudit(0);
+//                                    report.setIsFinalAudit(1);
+//                                    report.setState(0);
+//                                } else {
+//                                    System.out.println("代填的情况下,有自定义审核流程");
+
+//                                    int projectLeaderNodeIndex = 0;
+//                                    for (int t=0;t<auditWorkflowList.size(); t++) {
+//                                        if (auditWorkflowList.get(t).getIsDeptAudit() == 0) {
+//                                            projectLeaderNodeIndex = t;
+//                                            break;
+//                                        }
+//                                    }
+//                                    if (projectLeaderNodeIndex == auditWorkflowList.size() -1) {
+//                                        //最后一个节点就是项目经理,那就不用审核了,直接通过
+//                                        report.setState(1);
+//                                    } else {
+//                                        //否则取下一个节点,待审核
+//                                        report.setState(0);
+//                                        int nextIndex = projectLeaderNodeIndex + 1;
+//                                        AuditWorkflowTimeSetting nextNode = auditWorkflowList.get(nextIndex);
+//                                        report.setIsFinalAudit((nextIndex == auditWorkflowList.size()-1)?1:0);
+//                                        report.setIsDeptAudit(nextNode.getIsDeptAudit());
+//                                        report.setAuditDeptid(nextNode.getAuditDeptId());
+//                                        report.setAuditDeptManagerid(nextNode.getAuditDeptId() != null?allDeptList.stream().filter(d->d.getDepartmentId().equals(nextNode.getAuditDeptId())).findFirst().get().getManagerId(): null);
+//                                    }
+//                                }
                             }
 
 
@@ -2163,6 +2168,7 @@ public class ReportController {
             report.setIsFinalAudit(auditWorkflowList.size() > 1?0:1);
             report.setIsDeptAudit(firstNode.getIsDeptAudit());
             report.setAuditDeptid(firstNode.getAuditDeptId());
+            System.out.println("firstNode.getIsDeptAudit() = " + firstNode.getIsDeptAudit());
             if (firstNode.getIsDeptAudit() == 1) {
                 Department curDept = allDeptList.stream()
                         .filter(ad->ad.getDepartmentId().equals(firstNode.getAuditDeptId())).findFirst().get();