Bladeren bron

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

seyason 4 maanden geleden
bovenliggende
commit
34e664c551
100 gewijzigde bestanden met toevoegingen van 3449 en 299 verwijderingen
  1. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/index.html
  2. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/package.json
  3. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/chongqi.png
  4. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/kehuhz.png
  5. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/lianxir.png
  6. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contract.png
  7. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/min_contract.png
  8. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitdingdan.png
  9. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitjer.png
  10. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitkehu.png
  11. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlianxir.png
  12. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlishi.png
  13. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitshangji.png
  14. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitxianshuo.png
  15. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/shangjihz.png
  16. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/wanc.png
  17. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/xiansuohz.png
  18. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/yanqi.png
  19. 5 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/formForm/formItem.vue
  20. 10 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/formForm/formView.vue
  21. 26 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/pullDownSelector.vue
  22. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/translationComponent.vue
  23. 64 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/dataView/reRcharts.vue
  24. 8 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js
  25. 30 24
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue
  26. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/addEditor.vue
  27. 68 14
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue
  28. 238 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessOpportunityStage.vue
  29. 23 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/detail.vue
  30. 9 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/relatedBusinessOpportunities.vue
  31. 49 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/contactsInfo.vue
  32. 15 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/detail.vue
  33. 51 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/customerInfo.vue
  34. 15 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/detail.vue
  35. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/addEditor.vue
  36. 6 7
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/newAndModifiedRelatedProducts.vue
  37. 4 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/addEditor.vue
  38. 26 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/detail.vue
  39. 15 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/detail.vue
  40. 50 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/threadInfo.vue
  41. 146 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/businessEcharts.vue
  42. 111 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue
  43. 165 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataSummary.vue
  44. 176 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/filterItem.vue
  45. 184 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/salesBriefings.vue
  46. 26 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue
  47. 4 8
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue
  48. 9 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/addEditorVisitor.vue
  49. 101 17
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/visitorDetails.vue
  50. 18 4
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js
  51. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js
  52. 20 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/yarn.lock
  53. 21 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java
  54. 34 5
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ContactsController.java
  55. 41 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CustomController.java
  56. 5 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/TaskController.java
  57. 6 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/UserController.java
  58. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/VisitPlanController.java
  59. 6 6
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Task.java
  60. 4 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/VisitPlanVO.java
  61. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/CustomService.java
  62. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/UserService.java
  63. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java
  64. 55 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/CustomServiceImpl.java
  65. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java
  66. 66 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  67. 1 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanTask.java
  68. 3 3
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml
  69. 3 3
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/ClueMapper.xml
  70. 4 4
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CustomMapper.xml
  71. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/VisitPlanMapper.xml
  72. 215 8
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalController.java
  73. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  74. 165 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java
  75. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskExecutorController.java
  76. 217 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskFilesController.java
  77. 21 21
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java
  78. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Project.java
  79. 16 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApproval.java
  80. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApprovalCheck.java
  81. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApprovalLog.java
  82. 95 24
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Task.java
  83. 12 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TaskExecutor.java
  84. 17 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TaskFiles.java
  85. 21 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  86. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/User.java
  87. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/QueryTaskChargePage.java
  88. 77 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/excel/SpecialContractMonthSalary.java
  89. 100 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/ContractBonusDetailVO.java
  90. 26 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/TaskChargePageVO.java
  91. 4 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ContractBonusDetailMapper.java
  92. 3 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/TaskFilesMapper.java
  93. 15 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/TaskMapper.java
  94. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectApprovalService.java
  95. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java
  96. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/TaskFilesService.java
  97. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/TaskService.java
  98. 77 28
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ContractBonusDetailServiceImpl.java
  99. 366 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java
  100. 0 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/PermissionServiceImpl.java

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/index.html

@@ -7,7 +7,7 @@
     <title>客户管家</title>
     <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js" referrerpolicy="origin"></script>
     <script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js" referrerpolicy="origin"></script>
-    <script src="https://s.url.cn/qqun/qun/qqweb/m/qun/confession/js/vconsole.min.js "></script>
+    <!-- <script src="https://s.url.cn/qqun/qun/qqweb/m/qun/confession/js/vconsole.min.js "></script> -->
   </head>
   <body>
     <div id="app"></div>

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/package.json

@@ -13,6 +13,7 @@
   "dependencies": {
     "axios": "^1.6.7",
     "dayjs": "^1.11.13",
+    "echarts": "^5.5.1",
     "pinia": "^2.1.7",
     "pinia-plugin-persist": "^1.0.0",
     "vant": "^4.5.0",

BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/chongqi.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/kehuhz.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/lianxir.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contract.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/min_contract.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitdingdan.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitjer.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitkehu.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlianxir.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitlishi.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitshangji.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/salesKitxianshuo.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/shangjihz.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/wanc.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/xiansuohz.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/yanqi.png


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

@@ -1,7 +1,7 @@
 <template>
   <template v-if="element.type === 'input'">
     <van-field
-      v-model="element.options.defaultValue"
+      v-model.trim="element.options.defaultValue"
       :label="element.label"
       :name="element.model"
       :maxlength="element.options?.maxlength"
@@ -18,7 +18,7 @@
   </template>
   <template v-if="element.type === 'number'">
     <van-field
-      v-model="element.options.defaultValue"
+      v-model.trim="element.options.defaultValue"
       type="number"
       :label="element.label"
       :name="element.model"
@@ -50,7 +50,7 @@
       is-link
       :class="element.options?.rules?.required ? '' : 'resetStyles'"
     >
-      <template #input v-if="element.options.defaultValue || element.options.defaultValue == 0">
+      <template #input v-if="element.options.defaultValue || element.options.defaultValue === '0' || element.options.defaultValue === 0">
         {{ selectedLabel }}
       </template>
     </van-field>
@@ -95,7 +95,7 @@
   </template>
   <template v-if="element.type === 'textarea'">
     <van-field
-      v-model="element.options.defaultValue"
+      v-model.trim="element.options.defaultValue"
       type="textarea"
       :label="element.label"
       :name="element.model"
@@ -212,6 +212,7 @@ function selectChange(value, label) {
   showSelect.value = false;
   value && errorMessageForResettingTheCurrentOption();
   const cascade = relatedField[props.element.model];
+  console.log(cascade, '<=== cascade')
   if (cascade) {
     emit("cascadeProcessing", cascade);
   }

+ 10 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/common/formForm/formView.vue

@@ -10,7 +10,7 @@
 import { ref, reactive, toRefs, watch, nextTick, onMounted } from "vue";
 import requests from "@common/requests";
 import FormItem from "./formItem.vue";
-import { resetListData, itemFormSetValue } from "./formCorrespondenceProcessing"
+import { resetListData, itemFormSetValue, relatedField } from "./formCorrespondenceProcessing"
 
 const list = ref([]);
 const formRef = ref(null);
@@ -32,6 +32,15 @@ watch(() => props.formJson, (newValue) => {
 
 watch(() => props.formValue, (newValue) => {
   list.value = itemFormSetValue(props.formJson.list, newValue);
+  const fileds = Object.keys(newValue);
+  setTimeout(() => {
+    fileds.forEach(item => {
+      const cascade = relatedField[item];
+      if (cascade) {
+        cascadeProcessing(cascade)
+      }
+    });
+  }, 800)
 });
 
 function getJsonData() {

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

@@ -1,8 +1,8 @@
 <template>
   <div class="w-full h-full flex flex-col">
-    <div class="w-full pt-2">
+    <div class="w-full pt-2 searchKeywords">
       <van-search v-model.trim="searchForValue" shape="round" placeholder="请输入搜索关键词"
-        @update:model-value="debouncedSearchOptions" />
+        @update:model-value="debouncedSearchOptions" input-align="left" />
     </div>
     <div class="flex-1 my-2 overflow-y-auto">
       <template v-if="renderingOptions.length == 1 && renderingOptions[0] && !renderingOptions[0].label">
@@ -57,7 +57,7 @@
 </template>
 
 <script setup>
-import { ref, onBeforeUpdate, reactive, watch, onMounted } from "vue";
+import { ref, onBeforeUpdate, reactive, watch, onMounted, onActivated } from "vue";
 import { manualCopying, useDebounce } from "@hooks/useCommon";
 import requests from "@common/requests";
 import { GET_ALL_PERSONNEL } from "@hooks/useApi";
@@ -68,6 +68,10 @@ const props = defineProps({
     type: Array,
     default: () => [],
   },
+  showElement: {
+    type: Boolean,
+    default: () => false,
+  },
   value: {
     type: [String, Array],
     default: () => [],
@@ -106,7 +110,21 @@ watch(() => props.options, (newValue) => {
 })
 
 watch(() => props.value, (newValue) => {
-  selectChecked.value = newValue
+  if(!newValue) {
+    selectChecked.value = []
+  }
+  selectChecked.value = Array.isArray(newValue) ? newValue : [newValue]
+})
+
+watch(() => props.showElement, (newValue) => {
+  if(!newValue) {
+    setTimeout(() => {
+      searchForValue.value = ''
+      searchOptions('')
+      const val = (props.value && Array.isArray(props.value) && props.value.length > 0)
+      selectChecked.value = val ? selectChecked.value : []
+    }, 500)
+  }
 })
 
 function searchOptions(val) {
@@ -120,7 +138,7 @@ function searchOptions(val) {
       return;
     }
     const list = manualCopying(allOptions.value);
-    renderingOptions.value = list.filter((item) => item.label.indexOf(val) > -1);
+    renderingOptions.value = list.filter((item) => (item.label || '').indexOf(val) > -1);
     return
   }
 
@@ -187,3 +205,6 @@ onMounted(() => {
   }
 });
 </script>
+
+<style lang="scss" scoped>
+</style>

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

@@ -1,5 +1,5 @@
 <template>
-  <template v-if="userInfo.userInfo.userNameNeedTranslate == 1">
+  <!-- <template v-if="userInfo.userInfo.userNameNeedTranslate == 1">
     <template v-if="Array.isArray(openId)">
       <template v-for="(item, index) in openId">
         <ww-open-data :type='type' :openid='item'></ww-open-data>
@@ -8,9 +8,9 @@
     </template>
     <ww-open-data :type='type' :openid='openId' v-else></ww-open-data>
   </template>
-  <template v-else>
+  <template v-else> -->
     {{ Array.isArray(openId) ? openId.join(',') : openId }}
-  </template>
+  <!-- </template> -->
 </template>
 
 <script setup>

+ 64 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/components/dataView/reRcharts.vue

@@ -0,0 +1,64 @@
+<script setup lang="ts">
+import { ECharts, EChartsOption, init } from 'echarts';
+import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
+
+const props = defineProps({
+  width: {
+    type: String,
+    default: '100%'
+  },
+  height: {
+    type: String,
+    default: '100%'
+  },
+  option: {
+    type: Object,
+    default: () => ({})
+  }
+});
+
+const myChartsRef = ref();
+let myChart;
+let timer;
+
+// 初始化echarts
+const initChart = () => {
+  if (myChart !== undefined) {
+    myChart.dispose();
+  }
+  myChart = init(myChartsRef.value);
+  // 拿到option配置项,渲染echarts
+  myChart?.setOption(props.option, true);
+  resizeChart()
+};
+// 重新渲染echarts
+const resizeChart = () => {
+  timer = setTimeout(() => {
+    if (myChart) {
+      myChart.resize();
+    }
+  }, 500);
+};
+
+onMounted(() => {
+  initChart();
+  window.addEventListener('resize', resizeChart);
+});
+
+onBeforeUnmount(() => {
+  window.removeEventListener('resize', resizeChart);
+  clearTimeout(timer);
+  timer = null;
+});
+
+watch(() => props.option, () => {
+  initChart();
+}, {
+  deep: true
+}
+);
+</script>
+
+<template>
+  <div ref="myChartsRef" :style="{ height: height, width: width }" :option="option"></div>
+</template>

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

@@ -99,4 +99,11 @@ export const TOP_DATA_OF_CONTRACT_LIST = `/contract/pageContractByPin` // 合同
 
 export const SALES_ORDER_TOP_PLACEMENT = `/order/pinOrder` // 销售订单顶置
 export const CANCEL_THE_TOP_PLACEMENT_OF_THE_SALES_ORDER = `/order/undoPin` // 取消顶置
-export const TOP_DATA_OF_SALES_ORDER_LIST = `/order/pageOrderByPin` // 销售订单列表顶置数据
+export const TOP_DATA_OF_SALES_ORDER_LIST = `/order/pageOrderByPin` // 销售订单列表顶置数据
+
+export const GET_SALES_BRIEFINGS = `/order/salesKit` // 获取销售简报
+export const OBTAIN_DATA_SUMMARY = `/order/dataSummary` // 获取数据汇总
+export const STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES = `/order/businessOpportunityStage` // 获取商机阶段
+export const ACQUISITION_STAGE = `/business-opportunity/getStage` // 获取阶段
+export const PROMOTION_STAGE = `/business-opportunity/saveStageId` // 推进阶段
+export const STAGE_NOTES = `/business-opportunity/saveReason` // 阶段备注

+ 30 - 24
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue

@@ -87,7 +87,7 @@
                           <div>
                             <div class="flex items-center flex-row">
                               <div class="flex-1 truncate mr-8 titles relative">{{ item.taskName }}</div>
-                              <div class="text-[#B9B9B9]">
+                              <div :style="`color: ${fixedFieldTaskStatus.find(subItem => subItem.value == item.status)?.color}`">
                                 {{ fixedFieldTaskStatus.find(subItem => subItem.value == item.status)?.label || '' }}
                               </div>
                             </div>
@@ -176,16 +176,12 @@
                             <img src="/src/assets/image/noTopMounted.png" class="w-full h-full">
                           </div>
                         </template>
-                        <template v-permission="[queryParameters?.jurisdiction?.edit]">
-                          <div class="buttonCircle rounded-full" @click="edit(item)" >
-                            <img src="/src/assets/image/edit.png" class="w-full h-full">
-                          </div>
-                        </template>
-                        <template v-permission="[queryParameters?.jurisdiction?.delete]">
-                          <div class="buttonCircle rounded-full" @click="deleteRow(item)" >
-                            <img src="/src/assets/image/delete.png" class="w-full h-full">
-                          </div>
-                        </template>
+                        <div class="buttonCircle rounded-full" @click="edit(item)" v-permission="[queryParameters?.jurisdiction?.edit]">
+                          <img src="/src/assets/image/edit.png" class="w-full h-full">
+                        </div>
+                        <div class="buttonCircle rounded-full" @click="deleteRow(item)" v-permission="[queryParameters?.jurisdiction?.delete]">
+                          <img src="/src/assets/image/delete.png" class="w-full h-full">
+                        </div>
                       </div>
                     </template>
                   </van-swipe-cell>
@@ -194,7 +190,12 @@
             </van-pull-refresh>
           </template>
           <template v-else>
-            <van-skeleton title :row="20" class="w-full h-full" />
+            <template v-if="!isLoading && listData?.records && !listData.records.length">
+              <van-empty description="暂无数据" />
+            </template>
+            <template v-else>
+              <van-skeleton title :row="20" class="w-full h-full" />
+            </template>
           </template>
         </div>
       </div>
@@ -219,7 +220,7 @@
 
       <!-- select 选择器 -->
       <van-popup v-model:show="showSelect" destroy-on-close position="bottom" :style="{ height: '80%' }">
-        <PullDownSelector @change="selectChange" />
+        <PullDownSelector :showElement="showSelect" @change="selectChange" />
       </van-popup>
     </template>
   </Page>
@@ -319,7 +320,13 @@ function confirmTransfer() {
   }
   const { id } = excessiveData.value
   const { value } = dialogSelection.value
-  requests.post(queryParameters?.value.transferInterface, { ids: id, inchargerId: value }).then((res) => {
+  let formVal = {}
+  if(queryParameters?.value.key == 'contacts') {
+    formVal = { id, inchargerId: value, ownerId: value }
+  } else {
+    formVal = { ids: id, inchargerId: value, }
+  }
+  requests.post(queryParameters?.value.transferInterface, { ...formVal }).then((res) => {
     toastSuccess('转移成功')
     onRefresh(true)
     showDialog.value = false
@@ -344,11 +351,12 @@ function claimAndClaim(item) {
 // 删除事件
 function deleteRow(row) {
   const { name = '', searchFiled = {}, deteleFiled = '' } = queryParameters.value
+  const foemVal = { [queryParameters.value.key == 'tasks' ? 'taskIds' : 'ids']: row.id }
   showConfirmDialog({
     title: `删除${name}`,
     message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
   }).then(() => {
-    requests.post(deteleFiled, { ids: row.id }).then((res) => {
+    requests.post(deteleFiled, { ...foemVal }).then((res) => {
       toastSuccess('删除成功')
       onRefresh(true)
     }).catch((err) => {
@@ -359,23 +367,20 @@ function deleteRow(row) {
 
 // 顶置事件
 function topMounted(row) {
-  requests.post(queryParameters?.value.topMountedInterface, { ids: row.id }).then((res) => {
+  requests.post(queryParameters?.value.topMountedInterface, { id: row.id }).then((res) => {
     toastSuccess('顶置成功')
     onRefresh(true)
   })
 }
 
 function noTopMounted(row) {
-  requests.post(queryParameters?.value.cancelTheTopMountedInterface, { ids: row.id }).then((res) => {
+  requests.post(queryParameters?.value.cancelTheTopMountedInterface, { id: row.id }).then((res) => {
     toastSuccess('取消顶置成功')
     onRefresh(true)
   })
 }
 
 function toDetail(item) {
-  if (queryParameters.value?.key == 'tasks') {
-    return
-  }
   router.navigateTo({
     pathName: 'details',
     success: () => {
@@ -463,6 +468,7 @@ function reloadListData(data) {
   refreshing.value = false
   isLoading.value = false
   finished.value = false
+  searchVal.value = ''
   listData.value = { records: [], pageIndex: 0, pageSize: 20, total: 0, totalPage: 0 }
   popUpWindowArray.value = popUpWindowArray.value.filter(item => !item.removeModule.includes(queryParameters.value?.key))
   if (!fixedData.formJson[queryParameters.value.key]) {
@@ -503,13 +509,13 @@ useLifecycle({
     router.on('moduleListDetailParameter', (data) => {
       reloadListData(data)
     })
+
+    router.eventOn('moduleListRefreshData', (data) => {
+      onRefresh(true)
+    })
   }
 });
 
-router.eventOn('moduleListRefreshData', (data) => {
-  onRefresh(true)
-})
-
 </script>
 
 <style lang='scss' scoped>

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

@@ -96,7 +96,9 @@ function judgmentaAmounteEqual(mob, arr) {
   }
   let flag = false;
   const amounte = +mob.amountOfMoney || 0;
-  const totalAmounte = arr.reduce((pre, cur) => pre + (cur.totalPrice || 0), 0);
+  const totalAmounte = arr.reduce((pre, cur) => pre + (+cur.totalPrice || 0), 0);
+
+  console.log(amounte, totalAmounte)
 
   if (amounte != totalAmounte) {
     toastText(`商机金额${amounte > totalAmounte ? '大于' : '小于'}产品总金额,${amounte > totalAmounte ? '保存中...' : '请修改'}`)
@@ -135,12 +137,10 @@ useLifecycle({
   load: () => {
     formVal.value = props.formValue
     getBusinessOpportunityDetails(formVal.value)
-    console.log(formVal.value, '<==== formVal.value')
   },
   init: () => {
     formVal.value = props.formValue
     getBusinessOpportunityDetails(formVal.value)
-    console.log(formVal.value, '<==== formVal.value')
   }
 });
 

+ 68 - 14
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessInfo.vue

@@ -1,30 +1,32 @@
 <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="商机名称" :value="infoData.name" />
+      <van-cell title="客户名称" :value="infoData.customerName" />
+      <van-cell title="联系人姓名" :value="infoData.contactsName" />
       <van-cell title="商机金额">
         <template #default>
-          <span class="text-[#FF8B32]" v-if="info.amountOfMoney">¥ {{ info.amountOfMoney }}</span>
+          <span class="text-[#FF8B32]" v-if="infoData.amountOfMoney">¥ {{ infoData.amountOfMoney }}</span>
         </template>
       </van-cell>
-      <van-cell title="预计成交" :value="info.expectedTransactionDate" />
-      <van-cell title="商机阶段" :value="info.stageValue" />
+      <van-cell title="预计成交" :value="infoData.expectedTransactionDate" />
+      <van-cell title="商机阶段" :value="infoData.stageValue" />
       <van-cell title="负责人">
         <template #default>
-          <TranslationComponent :openId="info.inchargerName" />
+          <TranslationComponent :openId="infoData.inchargerName" />
         </template>
       </van-cell>
-      <van-cell title="备注" :value="info.remark" />
+      <van-cell title="备注" :value="infoData.remark" />
     </div>
     <div class="bottomButton">
-      <van-button type="primary" class="w-full block" v-if="!info.contactsName"
+      <van-button type="primary" class="w-full block" v-if="!infoData.contactsName"
         @click="shoContactDialag()">关联联系人</van-button>
-      <van-button type="warning" class="w-full block" v-if="info.inchargerName"
+      <van-button type="warning" class="w-full block" v-if="infoData.inchargerName"
         @click="showDialogCli()">转移商机</van-button>
-      <van-button type="primary" class="w-full block" v-if="!info.inchargerName"
+      <van-button type="primary" class="w-full block" v-if="!infoData.inchargerName"
         @click="claimAndClaim()">认领商机</van-button>
+      <van-button type="default" class="w-full block" v-permission="[routingInformation.jurisdiction.edit]" @click="jumpEdit()">编辑商机</van-button>
+      <van-button type="danger" class="w-full block" v-permission="[routingInformation.jurisdiction.delete]" @click="deleteBusinessOpportunity()">删除商机</van-button>
     </div>
 
     <!-- 转移弹窗 -->
@@ -59,15 +61,20 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
+import { showConfirmDialog } from 'vant';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { BUSINESS_OPPORTUNITY_TRANSFER, GET_CONTACTS_WITH_MORE_I_DS, CONTACT_PERSON_ASSOCIATED_WITH_BUSINESS_OPPORTUNITY } from '@hooks/useApi'
+import { resetListData, getListFieldKey } from '@components/common/formForm/formCorrespondenceProcessing'
 import requests from "@common/requests";
 import useShowToast from '@hooks/useToast'
 import useInfoStore from '@store/useInfoStore'
+import useFixedData from "@store/useFixedData.js"
 import useRouterStore from "@store/useRouterStore.js";
-const router = useRouterStore()
+import { routingInfos } from "@utility/generalVariables"
 
+const router = useRouterStore()
+const fixedData = useFixedData()
 const userInfo = useInfoStore()
 const { toastSuccess, toastFail, toastText } = useShowToast()
 const props = defineProps({
@@ -77,6 +84,13 @@ const props = defineProps({
     default: () => ({})
   }
 })
+const infoData = ref(props.info)
+const routingInformation = routingInfos['business']
+
+watch(() => props.info, (newValue) => {
+  infoData.value = newValue
+  getAllContactsList()
+})
 
 const showDialog = ref(false);
 const showSelect = ref(false);
@@ -85,6 +99,46 @@ const showContactDialog = ref(false)
 const dialogSelection = ref({});
 const allContactsList = ref([]);
 
+function deleteBusinessOpportunity() {
+  const { name = '', searchFiled = {}, deteleFiled = '' } = routingInformation
+  const row = infoData.value
+  const foemVal = { [routingInformation.key == 'tasks' ? 'taskIds' : 'ids']: row.id }
+  showConfirmDialog({
+    title: `删除${name}`,
+    message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
+  }).then(() => {
+    requests.post(deteleFiled, { ...foemVal }).then((res) => {
+      toastSuccess('删除成功')
+      router.navigateBack({
+        success: () => {
+          router.emit('moduleListDetailParameter', {
+            row: JSON.stringify(routingInformation)
+          })
+        }
+      })
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
+}
+
+function jumpEdit() {
+  const formJson = fixedData.formJson[routingInformation.key] || []
+  const formList = resetListData(formJson?.list)
+  const filedObj = getListFieldKey(formList, infoData.value)
+  const formVal = { ...filedObj, id: infoData.value.id }
+
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(routingInformation),
+        filedValue: JSON.stringify(formVal)
+      })
+    }
+  })
+}
+
 function shoContactDialag() {
   dialogSelection.value = {}
   showContactDialog.value = true
@@ -180,7 +234,7 @@ function getAllContactsList() {
 
 useLifecycle({
   load: () => {
-    getAllContactsList()
+    
   },
   init: () => {
     getAllContactsList()

+ 238 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessOpportunityStage.vue

@@ -0,0 +1,238 @@
+<template>
+  <div class="w-full h-full flex flex-col">
+    <div class="flex-1 overflow-y-auto my-3 px-6">
+      <van-steps direction="vertical" :active="stageIndex">
+        <template v-for="(item, index) in stageList">
+          <van-step>
+            <div class="flex flex-row items-center bg-white rounded">
+              <div :class="`verticalArrangement px-2 py-3 ${showStageClass(index)}`">
+                {{ showStageText(index) }}
+              </div>
+              <div class="flex-1 pl-3">{{ item.name }} {{ item.plan }}</div>
+            </div>
+          </van-step>
+        </template>
+      </van-steps>
+    </div>
+
+    <div class="px-6 flex flex-col pb-4 mt-2" v-if="stageIndex <= stageList.length && displayButtonOrNot">
+      <van-button type="primary" class="btn-3" v-if="stageIndex < stageList.length - 1"
+        @click="promotionStage()">推荐下阶段({{
+          stageList[+stageIndex + 1]?.name
+        }})</van-button>
+      <van-button type="success" class="btn-3" @click="completionStage(1)">赢单</van-button>
+      <van-button type="warning" class="btn-3" @click="completionStage(2)">输单</van-button>
+      <van-button type="danger" class="btn-3" @click="completionStage(3)">无效</van-button>
+    </div>
+
+    <van-dialog v-model:show="showDisold" :title="`${transitionalData.title}原因`" show-cancel-button @confirm="stageCompletion()">
+      <van-field v-model="transitionalData.reason" rows="4" autosize type="textarea" :placeholder="`请输入${transitionalData.title}原因`"
+        show-word-limit />
+    </van-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { ACQUISITION_STAGE, GET_BUSINESS_OPPORTUNITY_DETAILS, PROMOTION_STAGE, STAGE_NOTES } from '@hooks/useApi'
+import requests from "@common/requests";
+import useShowToast from '@hooks/useToast'
+import useInfoStore from '@store/useInfoStore'
+import useRouterStore from "@store/useRouterStore.js";
+
+const { toastSuccess, toastFail, toastText, toastLoading } = useShowToast()
+
+const emit = defineEmits();
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+const infoData = ref({})
+const stageList = ref([])
+const stageIndex = ref(0)
+const stageSetting = ref([])
+const transitionalData = ref({})
+const showDisold = ref(false)
+const displayButtonOrNot = ref(true)
+
+watch(() => props.info, (val) => {
+  getBusinessOpportunityDetails(val.id)
+})
+
+function showStageText(index) {
+  const row = stageList.value[index]
+  const idx = stageIndex.value
+  const flag = displayButtonOrNot.value
+  const idList = stageSetting.value.map(item => item.id)
+
+  if(!flag && index == idx) {
+    return row.name
+  }
+
+  return idx > index ? '已完成' : idx == index ? '进行中' : '未开始'
+}
+
+function showStageClass (index) {
+  const idx = stageIndex.value
+  const flag = displayButtonOrNot.value
+  const row = stageList.value[index]
+  const idList = stageSetting.value.map(item => item.id)
+  if(idx > index) {
+    return 'ended'
+  }
+  if(idx == index && flag) {
+    return 'haveInHand'
+  }
+
+  if(flag && idx < index) {
+    return 'notStartedYet'
+  }
+  
+  if(!flag && idList.includes(row.id)) {
+    return row.name == '赢单' ? 'winningOrders' : row.name == '输单' ? 'singleLoss'  : 'invalid'
+  }
+
+  return ''
+}
+
+function completionStage(type) {
+  const typeObj = { 1: '赢单', 2: '输单', 3: '无效' }
+  const row = stageSetting.value.find(item => item.name === typeObj[type])
+  transitionalData.value = {
+    id: infoData.value.id,
+    stageId: row.id,
+    stageValue: row.name,
+    title: `${typeObj[type]}`,
+    reason: ''
+  }
+
+  if (type != 1) {
+    showDisold.value = true
+  } else {
+    stageCompletion(false)
+  }
+}
+
+function enterReason() {
+  const { id, reason } = transitionalData.value
+  requests.post(PROMOTION_STAGE, {
+    id, reason
+  }).then((res) => {
+    getBusinessOpportunityDetails(id)
+  }).catch((err) => {
+    toastFail(err.msg)
+  })
+}
+
+function stageCompletion(flag = true) {
+  const { id, stageId, stageValue } = transitionalData.value
+  requests.post(PROMOTION_STAGE, {
+    id, stageId, stageValue
+  }).then((res) => {
+    toastSuccess('操作成功')
+    if(flag) {
+      enterReason()
+    } else {
+      emit('chnage');
+      getBusinessOpportunityDetails(id)
+    }
+  }).catch((err) => {
+    toastFail(err.msg)
+  })
+}
+
+function promotionStage() {
+  const item = stageList.value[+stageIndex.value + 1]
+  toastLoading('推进中...', 0)
+  requests.post(PROMOTION_STAGE, {
+    id: infoData.value.id,
+    stageId: item.id,
+    stageValue: item.name
+  }).then((res) => {
+    toastSuccess('推进成功')
+    emit('chnage');
+    getBusinessOpportunityDetails(infoData.value.id)
+  }).catch((err) => {
+    toastFail(err.msg)
+  })
+}
+
+function acquisitionStage(row) {
+  requests.post(ACQUISITION_STAGE, {}).then((res) => {
+    stageIndex.value = (res.data || []).findIndex(item => item.id === row.stageId)
+    stageList.value = (res.data || []).filter(item => !(['赢单', '输单', '无效'].includes(item.name)))
+    stageSetting.value = (res.data || []).filter(item => (['赢单', '输单', '无效'].includes(item.name)))
+    const val = (stageSetting.value || []).filter(item => item.id === row.stageId)
+    displayButtonOrNot.value = true
+    if(val.length > 0) {
+      stageList.value.push(val[0])
+      stageIndex.value = stageList.value.length - 1
+      displayButtonOrNot.value = false
+    }
+  })
+}
+
+function getBusinessOpportunityDetails(id) {
+  requests.post(GET_BUSINESS_OPPORTUNITY_DETAILS, { id }).then((res) => {
+    infoData.value = res.data || {}
+    acquisitionStage(res.data)
+  })
+}
+
+function initializeData() {
+  getBusinessOpportunityDetails(props.info.id)
+}
+
+useLifecycle({
+  init: () => {
+    initializeData()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.verticalArrangement {
+  writing-mode: vertical-rl; 
+  border-radius: 6px;
+  background: #D9D9D9;
+  font-size: 12px;
+  line-height: 1.2;
+}
+
+.ended {
+  background: #D9D9D9;
+  color: #858585;
+}
+.haveInHand {
+  background: #075985;
+  color: #fff;
+}
+.notStartedYet {
+  background: #E6E6E6;
+  color: #fff;
+}
+.winningOrders {
+  background: #07C160;
+  color: #fff;
+}
+.singleLoss {
+  background: #FF976A;
+  color: #fff;
+}
+.invalid {
+  background: #EE0A24;
+  color: #fff;
+}
+.btn-3 {
+  margin-bottom: 10px;
+}
+
+::v-deep(.van-steps) {
+  background: none;
+}
+</style>

+ 23 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/detail.vue

@@ -1,9 +1,11 @@
 <template>
   <div class="w-full h-full">
     <van-tabs v-model:active="tabActive">
-      <van-tab title="商机阶段" name="商机阶段">商机阶段 1</van-tab>
+      <van-tab title="商机阶段" name="商机阶段">
+        <BusinessOpportunityStage :info="infoData" @chnage="refreshData()" />
+      </van-tab>
       <van-tab title="商机信息" name="商机信息">
-        <BusinessInfo :info="info" />
+        <BusinessInfo :info="infoData" />
       </van-tab>
       <van-tab title="相关产品" name="相关产品">
         <RelatedProducts :infoList="relatedProductsList" />
@@ -23,6 +25,7 @@ import requests from "@common/requests";
 import RelatedProducts from '../product/relatedProducts.vue';
 import BusinessInfo from './businessInfo.vue';
 import RelatedTasks from '../tasks/relatedTasks.vue';
+import BusinessOpportunityStage from './businessOpportunityStage.vue';
 
 const props = defineProps({
   info: {
@@ -34,27 +37,44 @@ const props = defineProps({
 const tabActive = ref('商机信息');
 const relatedProductsList = ref([]);
 const relatedTasksList = ref([]);
+const infoData = ref(props.info);
+const timeout = ref(null);
 
 watch(() => props.info, (newValue) => {
   tabActive.value = '商机信息';
   processingData(newValue.id)
 })
 
+function refreshData() {
+  processingData(infoData.value.id)
+}
+
 function getBusinessOpportunityDetails(id) {
   requests.post(GET_BUSINESS_OPPORTUNITY_DETAILS, { id }).then(({ data }) => {
+    infoData.value = data
     relatedProductsList.value = data.businessItemProducts || []
     relatedTasksList.value = data.taskList || []
   })
 }
 
 function processingData(id) {
-  getBusinessOpportunityDetails(id)
+  clearTimeout(timeout.value);
+  timeout.value = setTimeout(() => {
+    getBusinessOpportunityDetails(id)
+  }, 100);
 }
 
 useLifecycle({
   init: () => {
     tabActive.value = '商机信息';
     processingData(props.info.id)
+  },
+  load: () => {
+    tabActive.value = '商机信息';
+    processingData(props.info.id)
+  },
+  unLoad: () => {
+    clearTimeout(timeout.value);
   }
 });
 </script>

+ 9 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/relatedBusinessOpportunities.vue

@@ -1,7 +1,7 @@
 <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">
+    <div class="info h-full cellnormall" v-if="infoListData.length">
+      <div v-for="(item, index) in infoListData">
         <FoldingPanel :title="`相关商机(${item.name})`">
           <template #foldContainer>
             <div class="p-5 bg-white ">
@@ -32,7 +32,7 @@
 </template>
 
 <script setup>
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { fixedFieldPriority, fixedFieldTaskStatus } from "@utility/defaultData.js"
 import FoldingPanel from '@components/common/foldingPanel.vue';
@@ -45,6 +45,12 @@ const props = defineProps({
   }
 })
 
+const infoListData = ref(props.infoList);
+
+watch(() => props.infoList, (newValue) => {
+  infoListData.value = newValue;
+})
+
 useLifecycle({
   load: () => {
     // 添加加载逻辑

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

@@ -16,6 +16,8 @@
     </div>
     <div class="bottomButton">
       <van-button type="warning" class="w-full block" @click="showDialogCli()">转移联系人</van-button>
+      <van-button type="default" class="w-full block" v-permission="[routingInformation.jurisdiction.edit]" @click="jumpEdit()">编辑联系人</van-button>
+      <van-button type="danger" class="w-full block" v-permission="[routingInformation.jurisdiction.delete]" @click="deleteRow()">删除联系人</van-button>
     </div>
 
     <!-- 转移弹窗 -->
@@ -38,13 +40,18 @@
 
 <script setup>
 import { ref } from 'vue';
+import { showConfirmDialog } from 'vant';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { TRANSFER_CONTACT_PERSON } from '@hooks/useApi'
 import requests from "@common/requests";
 import useShowToast from '@hooks/useToast'
 import useInfoStore from '@store/useInfoStore'
 import useRouterStore from "@store/useRouterStore.js";
+import { routingInfos } from "@utility/generalVariables"
+import { resetListData, getListFieldKey } from '@components/common/formForm/formCorrespondenceProcessing'
+import useFixedData from "@store/useFixedData.js"
 
+const fixedData = useFixedData()
 const router = useRouterStore()
 const userInfo = useInfoStore()
 const { toastSuccess, toastFail, toastText } = useShowToast()
@@ -56,10 +63,52 @@ const props = defineProps({
   }
 })
 
+const routingInformation = routingInfos['contacts']
 const showDialog = ref(false);
 const showSelect = ref(false);
 const dialogSelection = ref({});
 
+function deleteRow() {
+  const { name = '', searchFiled = {}, deteleFiled = '' } = routingInformation
+  const row = props.info
+  const foemVal = { [routingInformation.key == 'tasks' ? 'taskIds' : 'ids']: row.id }
+  showConfirmDialog({
+    title: `删除${name}`,
+    message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
+  }).then(() => {
+    requests.post(deteleFiled, { ...foemVal }).then((res) => {
+      toastSuccess('删除成功')
+      router.navigateBack({
+        success: () => {
+          router.emit('moduleListDetailParameter', {
+            row: JSON.stringify(routingInformation)
+          })
+        }
+      })
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
+}
+
+function jumpEdit() {
+  const formJson = fixedData.formJson[routingInformation.key] || []
+  const formList = resetListData(formJson?.list)
+  const filedObj = getListFieldKey(formList, props.info)
+  const formVal = { ...filedObj, id: props.info.id }
+
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(routingInformation),
+        filedValue: JSON.stringify(formVal)
+      })
+    }
+  })
+}
+
+
 function confirmTransfer() {
   if (!dialogSelection.value.label) {
     return toastText('请选择要转移的人员')

+ 15 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/contacts/detail.vue

@@ -2,7 +2,7 @@
   <div class="w-full h-full">
     <van-tabs v-model:active="tabActive">
       <van-tab title="联系人信息" name="联系人信息">
-        <ContactsInfo :info="info" />
+        <ContactsInfo :info="infoData" />
       </van-tab>
       <van-tab title="相关任务" name="相关任务">
         <RelatedTasks :infoList="relatedTasksList" />
@@ -33,6 +33,8 @@ const props = defineProps({
 const tabActive = ref('联系人信息');
 const relatedBusinessOpportunitiesList = ref([]);
 const relatedTasksList = ref([]);
+const infoData = ref(props.info);
+const timeout = ref(null);
 
 watch(() => props.info, (newValue) => {
   tabActive.value = '联系人信息';
@@ -41,19 +43,30 @@ watch(() => props.info, (newValue) => {
 
 function getDetailedData(id) {
   requests.post(GET_CONTACT_DETAILS, { id }).then(({ data }) => {
+    infoData.value = data || {}
     relatedBusinessOpportunitiesList.value = data.businessOpportunityList || []
     relatedTasksList.value = data.taskList || []
   })
 }
 
 function processingData(id) {
-  getDetailedData(id)
+  clearTimeout(timeout.value);
+  timeout.value = setTimeout(() => {
+    getDetailedData(id)
+  }, 100);
 }
 
 useLifecycle({
   init: () => {
     tabActive.value = '联系人信息';
     processingData(props.info.id || props.info.customId)
+  },
+  load: () => {
+    tabActive.value = '线索信息';
+    processingData(props.info.id || props.info.customId)
+  },
+  unload: () => {
+    clearTimeout(timeout.value)
   }
 });
 </script>

+ 51 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/customerInfo.vue

@@ -3,7 +3,7 @@
     <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.companyPhone" />
       <van-cell title="邮箱" :value="info.email" />
       <van-cell title="客户行业" :value="info.customerIndustryValue" />
       <van-cell title="客户级别" :value="info.customerLevelValue" />
@@ -20,6 +20,8 @@
         @click="showDialogCli()">转移客户</van-button>
       <van-button type="primary" class="w-full block" v-if="!info.inchargerName"
         @click="claimAndClaim()">认领客户</van-button>
+      <van-button type="default" class="w-full block" v-permission="[routingInformation.jurisdiction.edit]" @click="jumpEdit()">编辑客户</van-button>
+      <van-button type="danger" class="w-full block" v-permission="[routingInformation.jurisdiction.delete]" @click="deleteRow()">删除客户</van-button>
     </div>
 
     <!-- 转移弹窗 -->
@@ -44,11 +46,16 @@
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { TRANSFER_CUSTOMERS } from '@hooks/useApi'
+import { showConfirmDialog } from 'vant';
 import requests from "@common/requests";
 import useShowToast from '@hooks/useToast'
 import useInfoStore from '@store/useInfoStore'
 import useRouterStore from "@store/useRouterStore.js";
+import { routingInfos } from "@utility/generalVariables"
+import { resetListData, getListFieldKey } from '@components/common/formForm/formCorrespondenceProcessing'
+import useFixedData from "@store/useFixedData.js"
 
+const fixedData = useFixedData()
 const router = useRouterStore()
 const userInfo = useInfoStore()
 const { toastSuccess, toastFail, toastText } = useShowToast()
@@ -63,6 +70,47 @@ const props = defineProps({
 const showDialog = ref(false);
 const showSelect = ref(false);
 const dialogSelection = ref({});
+const routingInformation = routingInfos['customer']
+
+function deleteRow() {
+  const { name = '', searchFiled = {}, deteleFiled = '' } = routingInformation
+  const row = props.info
+  const foemVal = { [routingInformation.key == 'tasks' ? 'taskIds' : 'ids']: row.id }
+  showConfirmDialog({
+    title: `删除${name}`,
+    message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
+  }).then(() => {
+    requests.post(deteleFiled, { ...foemVal }).then((res) => {
+      toastSuccess('删除成功')
+      router.navigateBack({
+        success: () => {
+          router.emit('moduleListDetailParameter', {
+            row: JSON.stringify(routingInformation)
+          })
+        }
+      })
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
+}
+
+function jumpEdit() {
+  const formJson = fixedData.formJson[routingInformation.key] || []
+  const formList = resetListData(formJson?.list)
+  const filedObj = getListFieldKey(formList, props.info)
+  const formVal = { ...filedObj, id: props.info.id }
+
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(routingInformation),
+        filedValue: JSON.stringify(formVal)
+      })
+    }
+  })
+}
 
 function confirmTransfer() {
   if (!dialogSelection.value.label) {
@@ -81,12 +129,12 @@ function confirmTransfer() {
 function claimAndClaim() {
   showConfirmDialog({
     title: '认领客户',
-    message: `确定认领【${props.info.name}】客户吗?`,
+    message: `确定认领【${props.info.customName}】客户吗?`,
   }).then(() => {
     requests.post(TRANSFER_CUSTOMERS, { ids: props.info.id, inchargerId: userInfo.userInfo.id }).then((res) => {
       toastSuccess('认领成功')
       listReloadData()
-      props.info.inchargerName = userInfo.userInfo.name
+      props.info.inchargerName = userInfo.userInfo.customName
       showDialog.value = false
     })
   })

+ 15 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/customer/detail.vue

@@ -2,7 +2,7 @@
   <div class="w-full h-full">
     <van-tabs v-model:active="tabActive" swipe-threshold="3">
       <van-tab title="客户信息" name="客户信息">
-        <CustomerInfo :info="info" />
+        <CustomerInfo :info="infoData" />
       </van-tab>
       <van-tab title="相关任务" name="相关任务">
         <RelatedTasks :infoList="relatedTasksList" />
@@ -43,6 +43,8 @@ const relatedTasksList = ref([]);
 const relatedContactsList = ref([]);
 const relatedBusinessOpportunitiesList = ref([]);
 const relatedSalesOrdersList = ref([]);
+const infoData = ref(props.info)
+const timeout = ref(null);
 
 watch(() => props.info, (newValue) => {
   tabActive.value = '客户信息';
@@ -51,6 +53,7 @@ watch(() => props.info, (newValue) => {
 
 function getDetailedData(id) {
   requests.post(OBTAIN_CUSTOMER_DETAILS, { id }).then(({ data }) => {
+    infoData.value = data || {}
     relatedTasksList.value = data.tasks || []
     relatedContactsList.value = data.contacts || []
     relatedBusinessOpportunitiesList.value = data.businessOpportunitys || []
@@ -59,13 +62,23 @@ function getDetailedData(id) {
 }
 
 function processingData(id) {
-  getDetailedData(id)
+  clearTimeout(timeout.value);
+  timeout.value = setTimeout(() => {
+    getDetailedData(id)
+  }, 100);
 }
 
 useLifecycle({
   init: () => {
     tabActive.value = '客户信息';
     processingData(props.info.id)
+  },
+  load: () => {
+    tabActive.value = '客户信息';
+    processingData(props.info.id)
+  },
+  unload: () => {
+    clearTimeout(timeout.value)
   }
 });
 </script>

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

@@ -46,8 +46,8 @@ function onSubmit() {
           }
         })
       }, 2000)
-    }).catch(() => {
-      toastFail('保存失败')
+    }).catch((err) => {
+      toastFail(err.msg)
     })
   })
 }

+ 6 - 7
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/product/newAndModifiedRelatedProducts.vue

@@ -11,7 +11,6 @@
       v-model="form.productId"
       is-link
       readonly
-      required
       name="productId"
       label="产品名称"
       placeholder="请选择"
@@ -26,7 +25,7 @@
       readonly
       name="productType"
       label="产品类型"
-      placeholder="请先选择产品"
+      :placeholder="!form.productId ? '请先选择产品' : '该产品未设置类型'"
     ></van-field>
     <van-field
       class="resetStyles"
@@ -34,7 +33,7 @@
       readonly
       name="unit"
       label="单位"
-      placeholder="请先选择产品"
+      :placeholder="!form.productId ? '请先选择产品' : '该产品未设置单位'"
     ></van-field>
     <van-field
       class="resetStyles"
@@ -43,7 +42,7 @@
       readonly
       name="price"
       label="标准价格"
-      placeholder="请先选择产品"
+      :placeholder="!form.productId ? '请先选择产品' : '该产品未设置标准价格'"
     ></van-field>
     <van-field
       class="resetStyles"
@@ -52,7 +51,7 @@
       readonly
       name="inventory"
       label="库存"
-      placeholder="请先选择产品"
+      :placeholder="!form.productId ? '请先选择产品' : '该产品未设置标准库存'"
     ></van-field>
     <van-field
       class="resetStyles"
@@ -164,8 +163,8 @@ function selectChange(value, label) {
 
 function inputNumberChange(field) {
     const { sellingPrice, quantity, discount, price } = props.form
-    props.form.discount = field == 'sellingPrice' ? Math.round(sellingPrice / price * 100) : props.form.discount
-    props.form.sellingPrice = field == 'discount' ? Math.round(price * discount / 100) : props.form.sellingPrice
+    // props.form.discount = field == 'sellingPrice' ? Math.round(sellingPrice / price * 100) : props.form.discount
+    // props.form.sellingPrice = field == 'discount' ? Math.round(price * discount / 100) : props.form.sellingPrice
     if (sellingPrice && quantity && price && discount) {
         props.form.totalPrice = Math.round(Math.round(price * discount / 100) * quantity)
     }

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

@@ -129,7 +129,7 @@
                     <div class="flex items-center justify-end">
                       <van-stepper v-model="item.value" :min="0" button-size="1.2rem" theme="round" integer
                         class="mr-2" />
-                      
+                      
                       <van-icon name="delete-o" class="ml-2 text-[red]" size="1.3rem"
                         @click="deleteCustomeDateItem(index)" />
                     </div>
@@ -167,11 +167,11 @@
     <div>
       <!-- 下拉框选择 -->
       <van-popup v-model:show="showSelectionFlag" destroy-on-close position="bottom" :style="{ height: '80%' }">
-        <PullDownSelector :options="showSelectionArray" :doYouNeedTranslation="false" @change="selectChange" />
+        <PullDownSelector :show-element="showSelectionFlag" :options="showSelectionArray" :doYouNeedTranslation="false" @change="selectChange" />
       </van-popup>
 
       <van-popup v-model:show="showSelectionToFlag" destroy-on-close position="bottom" :style="{ height: '80%' }">
-        <PullDownSelector :value="showSelectionToValue" @change="selectChange" :multiple-choice="true" />
+        <PullDownSelector :show-element="showSelectionToFlag" :value="showSelectionToValue" @change="selectChange" :multiple-choice="true" />
       </van-popup>
 
       <!-- 选择日期 -->
@@ -440,7 +440,7 @@ function initializeData() {
     businessOpportunityIdName: businessOpportunityName,
     priorityName: fixedFieldPriority.find(item => item.value == priority)?.label || '',
     taskTypeName: fixedFieldTaskType.find(item => item.value == taskType)?.label || '',
-    repeatTypenName: fixedFieldRepetitiveType.find(item => item.value == repeatType)?.label || ''
+    repeatTypeName: fixedFieldRepetitiveType.find(item => item.value == repeatType)?.label || ''
   }
 
   const list = repeatDesignDay && repeatDesignDay.split(',') || []

+ 26 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/tasks/detail.vue

@@ -1,6 +1,9 @@
 <template>
-  <div class="w-full h-full">
-    任务详情
+  <div class="w-full h-full flex-col">
+    <div class="bg-white info flex-1 overflow-y-auto">
+      <van-cell title="任务名称" :value="''" />
+      <van-cell title="优先级" :value="''" />
+    </div>
   </div>
 </template>
 
@@ -8,13 +11,32 @@
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
 
+const props = defineProps({
+  info: {
+    type: Object,
+    required: true,
+    default: () => ({})
+  }
+})
+
+function getTaskDetails() {
+  
+}
+
+function initializeData() {
+
+}
+
 useLifecycle({
   load: () => {
-    // 添加加载逻辑
+    initializeData()
+  },
+  init: () => {
+    initializeData()
   }
 });
 </script>
 
 <style lang='scss' scoped>
-  /* 样式代码 */
+
 </style>

+ 15 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/detail.vue

@@ -2,7 +2,7 @@
   <div class="w-full h-full">
     <van-tabs v-model:active="tabActive">
       <van-tab title="线索信息">
-        <ThreadInfo :info="info" />
+        <ThreadInfo :info="infoData" />
       </van-tab>
       <van-tab title="相关任务" name="相关任务">
         <RelatedTasks :infoList="relatedTasksList" :key="componentKey" />
@@ -29,6 +29,8 @@ const props = defineProps({
 const tabActive = ref('线索信息');
 const componentKey = ref(1);
 const relatedTasksList = ref([]);
+const infoData = ref(props.info);
+const timeout = ref(null);
 
 watch(() => props.info, (newValue) => {
   tabActive.value = '线索信息';
@@ -37,6 +39,7 @@ watch(() => props.info, (newValue) => {
 
 function getDetails(id) {
   requests.post(GET_CLUE_DETAILS, { id }).then(({ data }) => {
+    infoData.value = data || {}
     relatedTasksList.value = data.taskList || []
   }).finally(() => {
     setTimeout(() => {
@@ -46,13 +49,23 @@ function getDetails(id) {
 }
 
 function processingData(id) {
-  getDetails(id)
+  clearTimeout(timeout.value);
+  timeout.value = setTimeout(() => {
+    getDetails(id)
+  }, 100);
 }
 
 useLifecycle({
   init: () => {
     tabActive.value = '线索信息';
     processingData(props.info.id)
+  },
+  load: () => {
+    tabActive.value = '线索信息';
+    processingData(props.info.id)
+  },
+  unload: () => {
+    clearTimeout(timeout.value)
   }
 });
 </script>

+ 50 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/thread/threadInfo.vue

@@ -18,6 +18,8 @@
     <div class="bottomButton">
       <van-button type="warning" class="w-full block" v-if="info.inchargerName"  @click="showDialogCli()">转移线索</van-button>
       <van-button type="primary" class="w-full block" v-if="!info.inchargerName" @click="claimAndClaim()">认领线索</van-button>
+      <van-button type="default" class="w-full block" v-permission="[routingInformation.jurisdiction.edit]" @click="jumpEdit()">编辑线索</van-button>
+      <van-button type="danger" class="w-full block" v-permission="[routingInformation.jurisdiction.delete]" @click="deleteRow()">删除线索</van-button>
     </div>
 
     <!-- 转移弹窗 -->
@@ -40,13 +42,18 @@
 
 <script setup>
 import { ref } from 'vue';
+import { showConfirmDialog } from 'vant';
 import { useLifecycle } from '@hooks/useCommon.js';
 import { TRANSFER_CLUES } from '@hooks/useApi'
 import requests from "@common/requests";
 import useShowToast from '@hooks/useToast'
 import useInfoStore from '@store/useInfoStore'
 import useRouterStore from "@store/useRouterStore.js";
+import { routingInfos } from "@utility/generalVariables"
+import { resetListData, getListFieldKey } from '@components/common/formForm/formCorrespondenceProcessing'
+import useFixedData from "@store/useFixedData.js"
 
+const fixedData = useFixedData()
 const router = useRouterStore()
 const userInfo = useInfoStore()
 const { toastSuccess, toastFail, toastText } = useShowToast()
@@ -58,10 +65,51 @@ const props = defineProps({
   }
 })
 
+const routingInformation = routingInfos['thread']
 const showDialog = ref(false);
 const showSelect = ref(false);
 const dialogSelection = ref({});
 
+function deleteRow() {
+  const { name = '', searchFiled = {}, deteleFiled = '' } = routingInformation
+  const row = props.info
+  const foemVal = { [routingInformation.key == 'tasks' ? 'taskIds' : 'ids']: row.id }
+  showConfirmDialog({
+    title: `删除${name}`,
+    message: `确定删除【${row[searchFiled?.search]}】${name}吗?`,
+  }).then(() => {
+    requests.post(deteleFiled, { ...foemVal }).then((res) => {
+      toastSuccess('删除成功')
+      router.navigateBack({
+        success: () => {
+          router.emit('moduleListDetailParameter', {
+            row: JSON.stringify(routingInformation)
+          })
+        }
+      })
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
+}
+
+function jumpEdit() {
+  const formJson = fixedData.formJson[routingInformation.key] || []
+  const formList = resetListData(formJson?.list)
+  const filedObj = getListFieldKey(formList, props.info)
+  const formVal = { ...filedObj, id: props.info.id }
+
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(routingInformation),
+        filedValue: JSON.stringify(formVal)
+      })
+    }
+  })
+}
+
 function listReloadData() {
   router.navigateBack({
     success: () => {
@@ -86,12 +134,12 @@ function confirmTransfer() {
 function claimAndClaim() {
   showConfirmDialog({
     title: '认领线索',
-    message: `确定认领【${props.info.name}】线索吗?`,
+    message: `确定认领【${props.info.clueName}】线索吗?`,
   }).then(() => {
     requests.post(TRANSFER_CLUES, { ids: props.info.id, inchargerId: userInfo.userInfo.id }).then((res) => {
       toastSuccess('认领成功')
       listReloadData()
-      props.info.inchargerName = userInfo.userInfo.name
+      props.info.inchargerName = userInfo.userInfo.clueName
       showDialog.value = false
     })
   })

+ 146 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/businessEcharts.vue

@@ -0,0 +1,146 @@
+<template>
+  <div class="businessOpportunityStage box mt-4">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">商机阶段</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content w-full overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(echartsHeight) : '0'}`">
+      <ReRcharts :option="echartsOption" />
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF" :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import ReRcharts from '@components/dataView/reRcharts.vue';
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+const expandAndCollapse = ref(true);
+const echartsHeight = ref(140)
+const echartsOption = ref({})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  getEchartsData()
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function processChartData(yAxisData = [], seriesData = []) {
+  const dataLength = yAxisData.length
+  if(dataLength > 3) {
+    echartsHeight.value = dataLength * 38
+  }
+  echartsOption.value = {
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'shadow'
+      }
+    },
+    legend: {
+      show: false
+    },
+    grid: {
+      top: usePxToVwView(60),
+      left: usePxToVwView(250),
+      right: usePxToVwView(60),
+      bottom: usePxToVwView(100),
+    },
+    xAxis: {
+      type: 'value',
+      axisLabel: {
+        fontSize: usePxToVwView(12),
+      }
+    },
+    yAxis: {
+      type: 'category',
+      data: yAxisData,
+      axisLabel: {
+        fontSize: usePxToVwView(14),
+      }
+    },
+    series: [
+      {
+        name: '2011',
+        type: 'bar',
+        data: seriesData,
+        barWidth: usePxToVwView(80),
+      },
+    ]
+  }
+}
+
+function getEchartsData() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(STAGE_OF_OBTAINING_BUSINESS_OPPORTUNITIES, {
+    ...formVal
+  }).then((res) => {
+    const yAxisData = (res.data?.dataMap || []).map(item => item.stageName);
+    const seriesData = (res.data?.dataMap || []).map(item => item.num);
+    processChartData(yAxisData, seriesData)
+  })
+}
+
+useLifecycle({
+  load: () => {
+    getEchartsData()
+  },
+  init: () => {
+    getEchartsData()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

+ 111 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue

@@ -1,18 +1,126 @@
 <template>
-  <div>数据统计</div>
+  <div class="dataAnalysis">
+    <!-- 销售简报 -->
+    <SalesBriefings />
+
+    <!-- 数据汇总 -->
+    <DataSummary />
+
+    <!-- 商机阶段 -->
+    <BusinessEcharts />
+  </div>
 </template>
 
 <script setup>
 import { ref } from 'vue';
 import { useLifecycle } from '@hooks/useCommon.js';
+import requests from "@common/requests"
+
+import SalesBriefings from './salesBriefings.vue';
+import DataSummary from './dataSummary.vue';
+import BusinessEcharts from './businessEcharts.vue'
 
 useLifecycle({
   load: () => {
     // 添加加载逻辑
+  },
+  init: () => {
   }
 });
 </script>
 
-<style lang='scss' scoped>
-  /* 样式代码 */
+<style lang='scss'>
+/* 样式代码 */
+.dataAnalysis {
+  margin: 20px 0 16px 0;
+  padding: 0 16px;
+
+  .transitionEffect {
+    transition: all .5s ease-in-out;
+  }
+
+  .salesKit {
+    .content {
+      width: 100%;
+
+      .item {
+        width: 50%;
+        display: flex;
+        margin-top: 20px;
+
+        .item-img {
+          width: 54px;
+          height: 54px;
+          margin-right: 6px;
+        }
+      }
+    }
+  }
+
+  .dataSummary {
+    .data-item {
+      padding: 15px;
+      border-radius: 6px;
+      margin-top: 16px;
+
+      .kehu-img {
+        width: 21px;
+        height: 22px;
+        margin-right: 12.5px;
+      }
+
+      .businessOpportunity-img {
+        width: 23px;
+        height: 23px;
+        margin-right: 12px;
+      }
+
+      .xiansuo-img {
+        width: 23px;
+        height: 23px;
+        margin-right: 12px;
+      }
+    }
+  }
+
+  .businessOpportunityStage {}
+
+  .box {
+    background: #fff;
+    border-radius: 10px;
+    padding: 6px 16px 6px 16px;
+
+    .box-title {
+      height: 40px;
+      border-bottom: 1px solid #E5E4EE;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      position: relative;
+
+      .box-title-left {
+        padding-left: 10px;
+        color: #0E1012;
+
+        &::after {
+          content: '';
+          width: 2px;
+          border-radius: 10px;
+          background: #075985;
+          height: 18px;
+          position: absolute;
+          left: 0;
+          top: 50%;
+          transform: translateY(-50%);
+        }
+      }
+
+      .box-title-right {
+        color: #747474;
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
+}
 </style>

+ 165 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataSummary.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="dataSummary box mt-4">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">数据汇总</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content w-full overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(313) : '0'}`">
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/kehuhz.png" class="kehu-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">客户汇总</div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%">
+              新增客户 <span class="ml-1 text-[#FFBD27]">
+                {{ dataSummary?.customDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%">
+              转成交客户 <span class="ml-1 text-[#FFBD27]">
+                {{ dataSummary?.closeDealNum?.newNum || 0 }}
+              </span> 人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/shangjihz.png" class="businessOpportunity-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">商机汇总</div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%;">
+              新增商机 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%;">
+              商机赢单 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.losting || 0 }}
+              </span> 个
+            </div>
+          </div>
+          <div class="flex mt-2">
+            <div class="text-size-small text-[#999]" style="width: 40%;">
+              输单商机 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.winning || 0 }}
+              </span> 人
+            </div>
+            <div class="text-size-small text-[#999]" style="width: 60%;">
+              商机总金额 <span class="ml-1 text-[#51C2FF]">
+                {{ dataSummary?.businessOpportunityDataSummary?.allAmountOfMoney || 0 }}
+              </span> 元
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="data-item flex flex-row bg-[#F8F8FA]">
+        <img src="/src/assets/image/xiansuohz.png" class="xiansuo-img">
+        <div class="flex-1">
+          <div class="font-bold text-[#000]">线索汇总</div>
+          <div class="flex mt-2">
+            <div class="w-1/2 text-size-small text-[#999]">
+              新增线索 <span class="ml-1 text-[#28C67E]">
+                {{ dataSummary?.clueDataSummary?.newNum || 0 }}
+              </span> 人
+            </div>
+            <div class="w-1/2 text-size-small text-[#999]">
+              线索转商机 <span class="ml-1 text-[#28C67E]">
+                {{ dataSummary?.clueDataSummary?.changeNum || 0 }}
+              </span> 人
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF"
+        :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { OBTAIN_DATA_SUMMARY } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const expandAndCollapse = ref(true);
+const dataSummary = ref({});
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  obtainDataSummary()
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function obtainDataSummary() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(OBTAIN_DATA_SUMMARY, {
+    ...formVal
+  }).then((res) => {
+    dataSummary.value = res.data
+  })
+}
+
+useLifecycle({
+  load: () => {
+    obtainDataSummary()
+  },
+  init: () => {
+    obtainDataSummary()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

+ 176 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/filterItem.vue

@@ -0,0 +1,176 @@
+<template>
+  <div class="filterCriteria">
+    <div class="flex-1 overflow-x-auto">
+      <div class="filterCriteria-title">范围选择</div>
+      <div class="flex flex-wrap">
+        <template v-for="(item, index) in fixedFieldPermissionOptions">
+          <div :class="`${dataFilter.scopeSelection == item.value && 'selected'} item item-special`"
+            @click="scopeSelectionCli(item)">{{ item.label }}</div>
+        </template>
+      </div>
+      <div class="filterCriteria-title">时间选择</div>
+      <div class="flex flex-wrap">
+        <template v-for="(item, index) in fixedFieldDateOptions">
+          <div :class="`${dataFilter.dateSelection == item.value && 'selected'} item`" @click="timeSelectionCli(item)">
+            {{ item.label }}</div>
+        </template>
+      </div>
+
+      <template v-if="dataFilter.dateSelection == -1">
+        <div class="filterCriteria-title">日期选择</div>
+        <div class="dateSelection flex justify-between flex-row items-center mb-4">
+          <div class="mr-6">开始日期</div>
+          <div class="dateItem flex-1" @click="showDate(0)">
+            <div>{{ dataFilter.customTime[0] ? dataFilter.customTime[0] : '请选择' }}</div>
+            <van-icon name="arrow" />
+          </div>
+        </div>
+        <div class="dateSelection flex justify-between flex-row items-center">
+          <div class="mr-6">结束日期</div>
+          <div class="dateItem flex-1" @click="showDate(1)">
+            <div>{{ dataFilter.customTime[1] ? dataFilter.customTime[1] : '请选择' }}</div>
+            <van-icon name="arrow" />
+          </div>
+        </div>
+      </template>
+    </div>
+
+    <van-button type="primary" class=" mt-6 mb-12 w-full" @click="submitSave">确定</van-button>
+
+    <van-popup v-model:show="showPicker" destroy-on-close position="bottom" :style="{ height: '50%' }">
+      <van-date-picker v-model="current" :min-date="minDate" :max-date="maxDate" @confirm="showPickerConfirm"
+        @cancel="showPicker = false" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue';
+import { defineProps, defineEmits } from 'vue'
+import { useLifecycle } from '@hooks/useCommon.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+import dayjs from 'dayjs';
+import useShowToast from "@hooks/useToast";
+
+const { toastText } = useShowToast();
+const emit = defineEmits()
+const props = defineProps({
+  dataFilters: {
+    typeof: Object,
+    default: () => ({})
+  }
+})
+
+const minDate = ref(new Date(dayjs().add(-2, 'year')))
+const maxDate = ref(new Date(dayjs().add(2, 'year')))
+const showPicker = ref(false)
+const current = ref([])
+const dateFiledIndex = ref(0)
+const dataFilter = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+watch(() => props.dataFilters, (newValue) => {
+  dataFilter.value = newValue
+})
+
+function submitSave() {
+  const { dateSelection, customTime = [] } = dataFilter.value
+  if(dateSelection == -1 && customTime.length == 0) {
+    toastText('请选择日期', 1000)
+    return
+  }
+  emit('submit', dataFilter.value)
+}
+function showPickerConfirm({ selectedValues }) {
+  dataFilter.value.customTime[dateFiledIndex.value] = selectedValues.join('-')
+  showPicker.value = false
+}
+
+function showDate(index) {
+  if (index == 0) {
+    maxDate.value = dataFilter.value.customTime[1] ? new Date(dataFilter.value.customTime[1]) : new Date(dayjs().add(2, 'year'))
+  }
+
+  if (index == 1) {
+    minDate.value = dataFilter.value.customTime[0] ? new Date(dataFilter.value.customTime[0]) : new Date(dayjs().add(-2, 'year'))
+  }
+  const dateVal = dataFilter.value.customTime[index] || '';
+  const todaySDate = dayjs().format("YYYY-MM-DD");
+  current.value = dateVal ? dateVal.split("-") : todaySDate.split("-");
+  dateFiledIndex.value = index
+  showPicker.value = true;
+}
+
+function scopeSelectionCli(item) {
+  dataFilter.value.scopeSelection = item.value
+}
+
+function timeSelectionCli(item) {
+  dataFilter.value.dateSelection = item.value
+  if(item.value == -1) {
+    dataFilter.value.customTime = []
+  }
+}
+
+useLifecycle({
+  load: () => {
+    // 添加加载逻辑
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+.filterCriteria {
+  padding: 14px 0;
+  margin: 0 20px;
+  position: relative;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .filterCriteria-title {
+    font-size: 18px;
+    color: #474A56;
+    margin-bottom: 20px;
+  }
+
+  .item {
+    width: 98px;
+    text-align: center;
+    padding: 10px 0;
+    color: #000000;
+    border: 1px solid #F8F8FA;
+    border-radius: 6px;
+    background: #F8F8FA;
+    margin-right: 20px;
+    margin-bottom: 20px;
+
+    &:nth-child(3n) {
+      margin-right: 0;
+    }
+  }
+
+  .item-special:last-child {
+    width: 160px;
+  }
+
+  .selected {
+    border-color: $themeColor;
+    color: $themeColor;
+  }
+
+  .dateSelection {
+    .dateItem {
+      border-radius: 6px;
+      background: #F8F8FA;
+      padding: 10px 14px;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+  }
+}
+</style>

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

@@ -0,0 +1,184 @@
+<template>
+  <div class="salesKit box">
+    <div class="box-title">
+      <div class="box-title-left text-size-in">销售简报</div>
+      <div class="box-title-right text-size-small" @click="showPopUpLayer()">
+        {{ scopeText }}/{{ dateText }}
+        <van-icon name="play" class="ml-2 rotate-90" color="#747474" />
+      </div>
+    </div>
+
+    <div class="content overflow-hidden transitionEffect"
+      :style="`height: ${expandAndCollapse ? usePxToVwView(298) : '0'}`">
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitkehu.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增客户</div>
+            <div class="flex items-end">
+              <span class="text-[#3C84F3] text-size-in font-bold mr-1">
+                {{ salesBriefings?.custom?.customCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitlianxir.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增联系人</div>
+            <div class="flex items-end">
+              <span class="text-[#42DC9E] text-size-in font-bold mr-1">
+                {{ salesBriefings?.contacts?.contactsCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitlishi.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增商机</div>
+            <div class="flex items-end">
+              <span class="text-[#F3893C] text-size-in font-bold mr-1">
+                {{ salesBriefings?.businessOpportunity?.businessOpportunityCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitdingdan.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增销售订单</div>
+            <div class="flex items-end">
+              <span class="text-[#A66AFF] text-size-in font-bold mr-1">
+                {{ salesBriefings?.salesOrder?.salesOrderCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitjer.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>销售订单金额</div>
+            <div class="flex items-end">
+              <span class="text-[#67DF2A] text-size-in font-bold mr-1">
+                {{ salesBriefings?.salesOrdersPrice?.salesOrdersPriceCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+        <div class="item">
+          <img src="/src/assets/image/salesKitshangji.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>商机金额</div>
+            <div class="flex items-end">
+              <span class="text-[#EED116] text-size-in font-bold mr-1">
+                {{ salesBriefings?.businessOpportunityPrice?.businessOpportunityPriceCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <div class="flex items-center w-full">
+        <div class="item">
+          <img src="/src/assets/image/salesKitxianshuo.png" class="item-img" />
+          <div class="flex flex-col justify-center text=[#999999]">
+            <div>新增线索</div>
+            <div class="flex items-end">
+              <span class="text-[#F44873] text-size-in font-bold mr-1">
+                {{ salesBriefings?.clue?.clueCount || 0 }}
+              </span>人
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="w-full flex justify-center mt-2" @click="expandAndCollapseSwitch()">
+      <van-icon name="arrow-up" color="#AFAFAF" :class="`${!expandAndCollapse && 'rotate-180'} transitionEffect`" />
+    </div>
+    <van-popup v-model:show="filterCriteriaPopUpLayerFlag" position="bottom" :style="{ height: '100%' }" closeable>
+      <FilterItem :dataFilters="filterCriteria" @submit="FilterItemSubmit" />
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { GET_SALES_BRIEFINGS } from '@hooks/useApi.js';
+import { fixedFieldPermissionOptions, fixedFieldDateOptions } from '@utility/defaultData.js';
+
+import requests from "@common/requests"
+import usePxToVwView from "@hooks/usePxTransform.js";
+import FilterItem from './filterItem.vue';
+
+const filterCriteriaPopUpLayerFlag = ref(false);
+const expandAndCollapse = ref(true);
+const salesBriefings = ref({});
+const filterCriteria = ref({
+  scopeSelection: 0,
+  dateSelection: 0,
+  customTime: []
+})
+
+const scopeText = computed(() => {
+  return fixedFieldPermissionOptions[filterCriteria.value.scopeSelection].label
+})
+
+const dateText = computed(() => {
+  if(filterCriteria.value.dateSelection == -1) {
+    const { customTime = [] } = filterCriteria.value
+    return ` ${customTime[0]} 至 ${customTime[1]}`
+  }
+  
+  return fixedFieldDateOptions[filterCriteria.value.dateSelection].label
+})
+
+function FilterItemSubmit(val) {
+  filterCriteria.value = val
+
+  filterCriteriaPopUpLayerFlag.value = false;
+  getData()
+}
+
+function expandAndCollapseSwitch() {
+  expandAndCollapse.value = !expandAndCollapse.value;
+}
+
+function showPopUpLayer() {
+  filterCriteriaPopUpLayerFlag.value = true;
+}
+
+function getData() {
+  const { scopeSelection = 0, dateSelection = 0, customTime = [] } = filterCriteria.value
+  const formVal = {
+    queryType: scopeSelection,
+    ...(dateSelection !== -1 ? { dateType: dateSelection } : { startDate: customTime[0], endDate: customTime[1] })
+  }
+  requests.post(GET_SALES_BRIEFINGS, {
+    ...formVal
+  }).then((res) => {
+    salesBriefings.value = res.data
+  })
+}
+
+useLifecycle({
+  load: () => {
+    getData()
+  },
+  init: () => {
+    getData()
+  }
+});
+</script>
+
+<style lang='scss' scoped>
+/* 样式代码 */
+</style>

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

@@ -6,7 +6,7 @@
         :show-title="false" :show-mark="false" :poppable="false" :show-confirm="false" :row-height="'3rem'"
         :min-date="minDate"
         :style="{ borderRadius: '0.3rem', height: expandAndCollapse ? (calendarHeight ? calendarHeight + 'px' : 'auto') : usePxToVwView(140) }"
-        :formatter="formatter" @select="calendarSelect">
+        :formatter="formatter" @select="calendarSelect" @panel-change="calendarPanelChangeSet">
         <template #month-title></template>
         <template #bottom-info="day">
           <div class="doT" v-if="day?.bottomInfo"></div>
@@ -42,7 +42,8 @@
                   <div class="w-2/3 text-[#505050]" style="width: 62%;">拜访时间: {{ item.visitTime }}</div>
                   <div class="w-1/3 text-[#505050] flex items-center" style="width: 38%;">
                     <van-icon name="phone-o" class="text-size-in mr-2" />
-                    {{ item.telPhone }}
+                    <!-- {{ item.telPhone }} -->
+                    {{ item?.companyPhone }}
                   </div>
                 </div>
               </div>
@@ -78,7 +79,7 @@
       <div class="p16 pt-0 pb-0 flex justify-between overflow-x-auto">
         <div class="flex">
           <template v-for="item in commonModulesList">
-            <div class="w80 bg-[#FFA359] h-28 rounded-md flex flex-col items-center justify-center">
+            <div class="w80 bg-[#FFA359] h-28 rounded-md flex flex-col items-center justify-center" @click="jumpToAdd(item)">
               <div class="formImage">
                 <img class="w-full h-full" src="/src/assets/image/form.png">
               </div>
@@ -185,6 +186,23 @@ const commonExpressionsHaveBeenNodded = ref([])
 const areYouRequesting = ref(false)
 const displayFrequentlyUsedContacts = ref(false)
 
+function calendarPanelChangeSet(data) {
+  dateConditions.value = dayjs(data.date).format('YYYY-MM-DD')
+  getVisitorPlan()
+}
+
+function jumpToAdd(rows) {
+  const jumpTo = routingInfos[rows.path.replace('/', '')]
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(jumpTo)
+      })
+    }
+  })
+}
+
 function toContactDetails(item) {
   router.navigateTo({
     pathName: 'details',
@@ -266,7 +284,8 @@ function jumpToAddNewVisitors(row) {
     pathName: 'addEditorVisitor',
     success: () => {
       router.emit('addEditorVisitorParameter', {
-        row: JSON.stringify(row || {})
+        row: JSON.stringify(row || {}),
+        date: JSON.stringify(dateConditions.value || {})
       })
     }
   })
@@ -297,7 +316,9 @@ function processForms() {
 }
 
 function getVisitorPlan() {
-  requests.post(GET_VISITOR_PLAN, { calenderDate: dateConditions.value }).then((res) => {
+  requests.post(GET_VISITOR_PLAN, { 
+    calenderDate: dayjs(dateConditions.value).format('YYYY-MM-DD') 
+  }).then((res) => {
     visitorProgramList.value = res.data || []
   })
 }

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

@@ -61,6 +61,7 @@ import { routingInfos } from "@utility/generalVariables.js";
 import Footer from "@components/page/footer.vue";
 import Workbench from "./component/workbench.vue";
 import DataAnalysis from "./component/dataAnalysis.vue";
+import requests from "@common/requests"
 
 
 const userInfo = useInfoStore()
@@ -88,10 +89,9 @@ function returnImageAddress(rows) {
 
 function obtainEnterpriseWeChatParameters(data = {}) {
   const token = data.id
-  const curUrl = window.location.href.split('#')[0]
-  console.log(wx, '开始调用接口')
+  // const curUrl = window.location.href.split('home')[0]
+  const curUrl = window.location.href
   requests.post('/wxcorp/getCorpWXConfig', { url: curUrl, token }).then((res) => {
-    console.log(res, '<====== 返回的参数 /wxcorp/getCorpWXConfig')
     wx.config({
       beta: true,
       debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
@@ -114,19 +114,16 @@ function obtainEnterpriseWeChatParameters(data = {}) {
           signature: res.data.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
           jsApiList: ['selectExternalContact', 'openThirdAppServiceChat', 'openAppManage'], //必填,传入需要使用的接口名称
           success: function (result) {
-            console.log(result, '《+========== 成功1')
             //  wx.agentConfig成功回调后,WWOpenData 才会注入到 window 对象上面
             window.WWOpenData.bind(document.querySelector('ww-open-data'))
           },
           fail: function (res) {
-            console.log(res, '<===== 失败1')
             if (res.errMsg.indexOf('function not exist') > -1) {
               alert('版本过低请升级')
             }
           },
         })
       }).catch(err => {
-        console.log(err, '<===== 失败2')
         if (err.errMsg.indexOf('function not exist') > -1) {
           alert('版本过低请升级')
         }
@@ -136,7 +133,7 @@ function obtainEnterpriseWeChatParameters(data = {}) {
       // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
       // alert('wxConfig发生异常:'+JSON.stringify(res));
       // 企业第一次授权安装进入后会报not in reliable domain的错误,刷新后正常
-      location.reload();
+      // location.reload();
     });
   }).catch(err => {
     alert(err);
@@ -148,7 +145,6 @@ useLifecycle({
 
   },
   init: () => {
-    console.log('执行一次')
     const currentEnvironment = navigator.userAgent.toLowerCase();
     const isCorpWX = currentEnvironment.indexOf("wxwork") > 0 ? true : false
     if(isCorpWX) {

+ 9 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/addEditorVisitor.vue

@@ -2,6 +2,9 @@
   <Page :title="`${addOrEdit ? '编辑' : '新增'}访客计划`">
     <template v-slot:body>
       <div class="w-full h-full flex flex-col">
+        <div class="bg-[#EAEAEA] py-4 px-5" v-if="headDate">
+          {{ headDate }}
+        </div>
         <van-form
           ref="addEditFormRef"
           show-error
@@ -98,7 +101,6 @@
             name="remindTime"
             label="提醒时间"
             placeholder="请选择"
-            :disabled="addOrEdit"
             :rules="[{ required: true, message: '' }]"
             @click="showPickerClick('remindTime')"
           >
@@ -155,6 +157,7 @@
         :style="{ height: '80%' }"
       >
         <PullDownSelector
+          :showElement="customerShowPicker"
           :options="customerAllList"
           :doYouNeedTranslation="false"
           @change="customerSelectChange"
@@ -167,7 +170,7 @@
         position="bottom"
         :style="{ height: '80%' }"
       >
-        <PullDownSelector @change="personnelSelectChange" />
+        <PullDownSelector :showElement="personnelShowPicker" @change="personnelSelectChange" />
       </van-popup>
       <!-- 拜访目的 -->
       <van-popup
@@ -230,6 +233,7 @@ const currentTime = ref(dayjs(new Date()).format("HH:mm").split(":"));
 const dateSelectionFiled = ref("");
 const addOrEdit = ref(true); // true 编辑,false 新增
 const addOrEditRow = ref({})
+const headDate = ref('')
 
 function onSubmit() {
   addEditFormRef.value.validate().then((res) => {
@@ -291,7 +295,7 @@ function timeConfirm({ selectedValues }) {
 }
 
 function showPickerClick(filed) {
-  const dateTimeVal = form.value[filed] ? form.value[filed] : new Date()
+  const dateTimeVal = form.value[filed] ? form.value[filed] : headDate.value ? new Date(headDate.value) : new Date()
   dateSelectionValue.value = dayjs(dateTimeVal).format("YYYY-MM-DD").split("-")
   currentTime.value = dayjs(dateTimeVal).format("HH:mm").split(":")
   dateSelectionFiled.value = filed;
@@ -301,8 +305,10 @@ function showPickerClick(filed) {
 function processingDataSource(data) {
   const row = JSON.parse(data.row);
   addOrEdit.value = Object.keys(row).length > 0 ? true : false;
+  headDate.value = JSON.parse(data.date)
   addOrEditRow.value = row
   form.value = row
+  addEditFormRef.value?.resetValidation()
   obtainThePurposeOfTheVisit();
   getReminderTime();
   getAllCustomers();

+ 101 - 17
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/visitorDetails.vue

@@ -1,5 +1,8 @@
 <template>
   <Page title="访客计划详情">
+    <template v-slot:headerRight>
+      <div class="themeTextColor" v-if="detailedData.finishState == 0" @click="visitorEditor">编辑</div>
+    </template>
     <template v-slot:body>
       <!-- 头部 -->
       <div class="planDetailsHead">
@@ -14,7 +17,8 @@
         <div class="w-full flex items-center">
           <van-icon name="user-circle-o" class="text-size-in mr-2" />
           客户:
-          <div class="ml-2 themeTextColor decoration-auto underline">{{ detailedData.customName }}</div>
+          <div class="ml-2 themeTextColor decoration-auto underline" @click="jumpToCustomerDetails()">{{
+            detailedData.customName }}</div>
         </div>
       </div>
 
@@ -51,7 +55,7 @@
 
       <van-popup v-model:show="showDelay" closeable position="bottom" round>
         <div class="newModuleAdded relative">
-          <div class="text-size-large text-[#474A56] absolute topTitle">延期时间</div>
+          <div class="text-size-large text-[#474A56] absolute topTitle">{{ delayRestartText }}时间</div>
           <div class="flex flex-col overflow-y-auto h-52">
             <div class="flex-1">
               <div class="px-6 py-4 bg-[#F8F8FA] flex justify-between items-center mt-2" @click="showDatePicker = true">
@@ -71,33 +75,51 @@
         <van-date-picker v-model="showDatePickerVal" @confirm="showPickerConfirm" @cancel="showDatePicker = false" />
       </van-popup>
 
-      <van-popup
-        v-model:show="timeShowPicker"
-        destroy-on-close
-        position="bottom"
-        :style="{ height: '50%' }"
-      >
+      <van-popup v-model:show="timeShowPicker" destroy-on-close position="bottom" :style="{ height: '50%' }">
         <van-time-picker v-model="currentTime" title="选择时间" @confirm="timeConfirm" />
       </van-popup>
     </template>
 
     <template v-slot:footer v-if="detailedData.finishState == 0">
       <div class="w-full flex justify-between layout">
-        <div class="footerBtnLeft layouts" @click="showContacts = true">
-          <div class="imgClss"></div>
+        <div class="footerBtnLeft layouts" @click="showContactsCli()">
+          <div class="imgClss">
+            <img src="/src/assets/image/lianxir.png" alt="">
+          </div>
           联系人
         </div>
-        <div class="footerBtnRight layouts" @click="showDelay = true, showDelayData = ''">
-          <div class="imgClss"></div>
+        <div class="footerBtnRight layouts" @click="delayRestart('DELAY')">
+          <div class="imgClss">
+            <img src="/src/assets/image/yanqi.png" alt="">
+          </div>
           延期
         </div>
         <div class="coverWithWhite"></div>
         <div class="bigCircle" @click="completeThePlan()">
-          <div class="imgClss"></div>
+          <div class="imgClss">
+            <img src="/src/assets/image/wanc.png" alt="">
+          </div>
           完成
         </div>
       </div>
     </template>
+
+    <template v-slot:footer v-if="detailedData.finishState != 0">
+      <div class="w-full flex justify-between layout">
+        <div class="footerBtnTwoLeft layoutsTwo" @click="showContactsCli()">
+          <div class="imgClss">
+            <img src="/src/assets/image/lianxir.png" alt="">
+          </div>
+          联系人
+        </div>
+        <div class="footerBtnTwoLeft layoutsTwo" @click="delayRestart('RESTART')">
+          <div class="imgClss">
+            <img src="/src/assets/image/chongqi.png" alt="">
+          </div>
+          重启
+        </div>
+      </div>
+    </template>
   </Page>
 </template>
 
@@ -105,11 +127,12 @@
 import { ref } from "vue";
 import { showConfirmDialog } from 'vant';
 import { useLifecycle } from "@hooks/useCommon.js";
-import { OBTAIN_DETAILS_OF_THE_VISIT_PLAN, POSTPONE_THE_VISIT_PLAN, COMPLETE_THE_VISIT_PLAN } from "@hooks/useApi"
+import { OBTAIN_DETAILS_OF_THE_VISIT_PLAN, POSTPONE_THE_VISIT_PLAN, COMPLETE_THE_VISIT_PLAN, OBTAIN_CUSTOMER_DETAILS } from "@hooks/useApi"
 import useToast from "@hooks/useToast"
 import requests from "@common/requests";
 import useRouterStore from "@store/useRouterStore.js";
 import dayjs from "dayjs";
+import { routingInfos } from "@utility/generalVariables.js";
 import useFixedData from "@store/useFixedData.js";
 import PullDownSelector from "@components/common/pullDownSelector.vue";
 
@@ -124,6 +147,49 @@ const showDatePickerVal = ref(dayjs().format("YYYY-MM-DD").split('-'))
 const showDelayData = ref('')
 const currentTime = ref('')
 const contactList = ref([]);
+const delayRestartText = ref('')
+
+function delayRestart(type = 'DELAY') {
+  const textType = { 'DELAY': '延期', 'RESTART': '重启' }
+  delayRestartText.value = textType[type]
+  showDelay.value = true
+  showDelayData.value = ''
+}
+
+function visitorEditor() {
+  router.navigateTo({
+    pathName: 'addEditorVisitor',
+    success: () => {
+      router.emit('addEditorVisitorParameter', {
+        row: JSON.stringify(detailedData.value || {}),
+        date: JSON.stringify(detailedData.value.visitTime || {})
+      })
+    }
+  })
+}
+
+function jumpToCustomerDetails() {
+  const routerInfo = routingInfos['customer']
+  requests.post(OBTAIN_CUSTOMER_DETAILS, { id: detailedData.value.customId }).then(res => {
+    router.navigateTo({
+      pathName: 'details',
+      success: () => {
+        router.emit('detailParameter', {
+          routerInfo: JSON.stringify(routerInfo || {}),
+          parameter: JSON.stringify(res.data || {})
+        })
+      }
+    })
+  })
+}
+
+function showContactsCli() {
+  if (!((contactList.value || []).length)) {
+    toastText('该联系人无号码')
+    return
+  }
+  showContacts.value = true
+}
 
 function completeThePlan() {
   showConfirmDialog({
@@ -141,6 +207,7 @@ function delayTimeEvent() {
   requests.post(POSTPONE_THE_VISIT_PLAN, { planId: detailedData.value.id, visitTime: showDelayData.value }).then(() => {
     toastSuccess(`操作成功`)
     getDetailData(detailedData.value)
+    showDelay.value = false
   })
 }
 
@@ -230,13 +297,32 @@ useLifecycle({
     justify-content: center;
   }
 
+  .layoutsTwo {
+    width: 40%;
+    height: 100%;
+    background: #fff;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+  }
+
   .footerBtnLeft {
     border-top-right-radius: 25px;
 
     .imgClss {
       width: 30px;
       height: 30px;
-      background-color: #000;
+      margin-bottom: 6px;
+    }
+  }
+
+  .footerBtnTwoLeft {
+    width: 50%;
+
+    .imgClss {
+      width: 30px;
+      height: 30px;
       margin-bottom: 6px;
     }
   }
@@ -247,7 +333,6 @@ useLifecycle({
     .imgClss {
       width: 26px;
       height: 24px;
-      background-color: #000;
       margin-bottom: 9px;
     }
   }
@@ -285,7 +370,6 @@ useLifecycle({
       width: 25.5px;
       height: 25.5px;
       margin-bottom: 4px;
-      background: #000;
     }
   }
 }

+ 18 - 4
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/defaultData.js

@@ -30,10 +30,10 @@ export const fixedFieldTaskType = [
 
 //任务状态
 export const fixedFieldTaskStatus = [
-  { label: "未开始", value: "0", type: "info" },
-  { label: "进行中", value: "1", type: "primary" },
-  { label: "已完成", value: "2", type: "success" },
-  { label: "已超时", value: "3", type: "danger" },
+  { label: "未开始", value: "0", type: "info", color: '#B9B9B9' },
+  { label: "进行中", value: "1", type: "primary", color: '#075985' },
+  { label: "已完成", value: "2", type: "success", color: '#07C160' },
+  { label: "已超时", value: "3", type: "danger", color: '#EE0A24' },
 ];
 
 // 合同状态
@@ -57,4 +57,18 @@ export const fixedFieldPaymentStatus = [
   { value: 0, label: '未回款' },
   { value: 1, label: '已回款' },
   { value: 2, label: '已完全回款' },
+]
+
+export const fixedFieldPermissionOptions = [
+  { value: 0, label: '仅本人' },
+  { value: 1, label: '本人及下属' },
+  { value: 2, label: '仅本部门' },
+  { value: 3, label: '本部门及下属部门' },
+]
+
+export const fixedFieldDateOptions = [
+  { value: 0, label: '本月' },
+  { value: 1, label: '本周' },
+  { value: 2, label: '本年' },
+  { value: -1, label: '自定义' },
 ]

File diff suppressed because it is too large
+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js


+ 20 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/yarn.lock

@@ -658,6 +658,14 @@ eastasianwidth@^0.2.0:
   resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
   integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
 
+echarts@^5.5.1:
+  version "5.5.1"
+  resolved "https://registry.npmmirror.com/echarts/-/echarts-5.5.1.tgz#8dc9c68d0c548934bedcb5f633db07ed1dd2101c"
+  integrity sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==
+  dependencies:
+    tslib "2.3.0"
+    zrender "5.6.0"
+
 electron-to-chromium@^1.5.41:
   version "1.5.71"
   resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz"
@@ -1317,6 +1325,11 @@ ts-interface-checker@^0.1.9:
   resolved "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
   integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
 
+tslib@2.3.0:
+  version "2.3.0"
+  resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
+  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+
 unplugin-vue-components@^0.25.1:
   version "0.25.2"
   resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-0.25.2.tgz"
@@ -1445,3 +1458,10 @@ yaml@^2.3.4:
   version "2.6.1"
   resolved "https://registry.npmmirror.com/yaml/-/yaml-2.6.1.tgz"
   integrity sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==
+
+zrender@5.6.0:
+  version "5.6.0"
+  resolved "https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz#01325b0bb38332dd5e87a8dbee7336cafc0f4a5b"
+  integrity sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==
+  dependencies:
+    tslib "2.3.0"

+ 21 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java

@@ -15,6 +15,7 @@ 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.PostMapping;
@@ -67,6 +68,8 @@ public class BusinessOpportunityController {
     private ActionLogMapper actionLogMapper;
     @Resource
     private BusinessOpportunityService businessOpportunityService;
+    @Autowired
+    private TaskMapper taskMapper;
 
     @RequestMapping("getAll")
     public Object getAll(HttpServletRequest request) {
@@ -193,6 +196,15 @@ public class BusinessOpportunityController {
             for (String id : ids1.split(",")) {
                 ids.add(Integer.parseInt(id));
             }
+            Integer taskCount = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
+                    .ne(Task::getStatus, 2)
+                    .eq(Task::getIsDelete, 0)
+                    .in(Task::getBusinessOpportunityId, ids)
+            );
+            if(null!=taskCount&&taskCount>0){
+                msg.setError("选中商机仍有关联任务,无法删除");
+                return msg;
+            }
             bOservice.isDelete(ids);
             msg.setMsg("操作成功");
         } else {
@@ -250,6 +262,15 @@ public class BusinessOpportunityController {
             bo.setUserId(user.getId());
             bo.setCreateTime(new Date());
             bo.setIsDelete(0);
+            if(StringUtils.isNotBlank(bo.getBusinessItemProductList())){
+                List<BusinessItemProduct> businessItemProducts = JSONArray.parseArray(bo.getBusinessItemProductList(), BusinessItemProduct.class);
+                for (BusinessItemProduct businessItemProduct : businessItemProducts) {
+                    if(businessItemProduct.getQuantity() > businessItemProduct.getInventory()){
+                        msg.setError("["+businessItemProduct.getProductName()+"]产品数量不能大于库存");
+                        return msg;
+                    }
+                }
+            }
             QueryWrapper<BusinessOpportunity> queryWrapper = new QueryWrapper<>();
             queryWrapper.eq("stage_id", bo.getStageId()).eq("is_delete",0).orderByDesc("seq").last("limit 1");
             List<BusinessOpportunity> list = bOservice.list(queryWrapper);

+ 34 - 5
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ContactsController.java

@@ -2,17 +2,21 @@ package com.management.platform.controller;
 
 
 import com.alibaba.fastjson.JSONArray;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.Contacts;
-import com.management.platform.entity.Custom;
-import com.management.platform.entity.User;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.BusinessOpportunityMapper;
 import com.management.platform.mapper.ContactsMapper;
+import com.management.platform.mapper.TaskMapper;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.ContactsService;
 import com.management.platform.util.HttpRespMsg;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+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.multipart.MultipartFile;
 
 import javax.annotation.Resource;
@@ -43,6 +47,12 @@ public class ContactsController {
     @Autowired
     private ContactsMapper contactsMapper;
 
+    @Resource
+    private BusinessOpportunityMapper businessOpportunityMapper;
+
+    @Resource
+    private TaskMapper taskMapper;
+
 
     /***
      * 置顶联系人
@@ -138,12 +148,31 @@ public class ContactsController {
      */
     @RequestMapping("deleteContacts")
     public HttpRespMsg deleteContacts( String ids){
+        HttpRespMsg msg = new HttpRespMsg();
         if (!StringUtils.isEmpty(ids)) {
             ids="["+ids+"]";
             List<Integer> array = JSONArray.parseArray(ids, Integer.class);
+            Integer bCount = businessOpportunityMapper.selectCount(new LambdaQueryWrapper<BusinessOpportunity>()
+                    .eq(BusinessOpportunity::getIsDelete, 0)
+                    .in(BusinessOpportunity::getContactsId, array)
+            );
+            if(bCount>0){
+                msg.setError("选中联系人中存在相关绑定商机,无法删除");
+                return msg;
+            }
+
+            Integer taskCount = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
+                    .eq(Task::getIsDelete, 0)
+                    .in(Task::getContactsId, array)
+            );
+            if(taskCount>0){
+                msg.setError("选中联系人中存在相关绑定任务,无法删除");
+                return msg;
+            }
+
             return contactsService.deleteContacts(array,request);
         }
-        HttpRespMsg msg = new HttpRespMsg();
+
         msg.setError("参数不能为空");
         return msg;
     }

+ 41 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/CustomController.java

@@ -6,10 +6,7 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.management.platform.entity.*;
-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.mapper.*;
 import com.management.platform.service.ContactsService;
 import com.management.platform.service.CustomService;
 import com.management.platform.service.WxCorpInfoService;
@@ -60,6 +57,12 @@ public class CustomController {
     private WxCorpInfoService wxCorpInfoService;
     @Autowired
     private ContactsService contactsService;
+    @Autowired
+    private TaskMapper taskMapper;
+    @Autowired
+    private BusinessOpportunityMapper businessOpportunityMapper;
+    @Autowired
+    private SalesOrderMapper salesOrderMapper;
 
 
     @RequestMapping("list")
@@ -82,6 +85,11 @@ public class CustomController {
         return customService.pinCutom(custom,request);
     }
 
+    @RequestMapping("getPrivilegedCustom")
+    public HttpRespMsg getPrivilegedCustom(HttpServletRequest request) {
+        return customService.getPrivilegedCustom(request);
+    }
+
     @RequestMapping("getAllCustom")
     public HttpRespMsg getAllCustom(HttpServletRequest request) {
         return customService.getAllCustom(request);
@@ -203,6 +211,35 @@ public class CustomController {
             for (String id : ids1.split(",")) {
                 ids.add(Integer.parseInt(id));
             }
+            //任务
+            Integer taskCount = taskMapper.selectCount(new LambdaQueryWrapper<Task>()
+                            .ne(Task::getStatus,2)
+                            .eq(Task::getIsDelete,0)
+                    .in(Task::getCustomId, ids));
+            if(null!=taskCount&&taskCount>0){
+                msg.setError("选中客户仍有任务,无法删除");
+                return msg;
+            }
+            //商机
+            Integer boCount = businessOpportunityMapper.selectCount(new LambdaQueryWrapper<BusinessOpportunity>()
+                            .eq(BusinessOpportunity::getIsDelete,0)
+                    .in(BusinessOpportunity::getCustomerId, ids)
+            );
+            if(null!=boCount&&boCount>0){
+                msg.setError("选中客户仍有商机,无法删除");
+                return msg;
+            }
+
+            //销售订单
+            Integer salesCount = salesOrderMapper.selectCount(new LambdaQueryWrapper<SalesOrder>()
+                            .eq(SalesOrder::getIsDelete,0)
+                    .in(SalesOrder::getCustomId, ids));
+            if(null!=salesCount&&salesCount>0){
+                msg.setError("选中客户仍有销售订单,无法删除");
+                return msg;
+            }
+
+
             customService.isDelete(ids);
             msg.setMsg("操作成功");
         } else {

+ 5 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/TaskController.java

@@ -24,6 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
@@ -180,7 +181,7 @@ public class TaskController {
                         saveInformationListWithCount(task, taskInformation, between, executorIdList);
                     }else if (task.getRepeatEndDate()!=null){
                         TaskInformation taskInformation = selectTaskInformation(task);
-                        if (LocalDateTime.now().isAfter(task.getRepeatEndDate())){
+                        if (LocalDate.now().isAfter(task.getRepeatEndDate())){
                             continue;
                         }
                         int between = (int) ChronoUnit.DAYS.between(LocalDateTime.now(), task.getRepeatEndDate());
@@ -200,7 +201,7 @@ public class TaskController {
                             saveInformationListWithCount(task, taskInformation, between, executorIdList);
                         }else if (task.getRepeatEndDate()!=null){
                             TaskInformation taskInformation = selectTaskInformation(task);
-                            if (LocalDateTime.now().isAfter(task.getRepeatEndDate())){
+                            if (LocalDate.now().isAfter(task.getRepeatEndDate())){
                                 continue;
                             }
                             int between = (int) ChronoUnit.DAYS.between(LocalDateTime.now(), task.getRepeatEndDate());
@@ -224,7 +225,7 @@ public class TaskController {
                             saveInformationListWithCount(task, taskInformation, between, executorIdList);
                         }else if (task.getRepeatEndDate()!=null){
                             TaskInformation taskInformation = selectTaskInformation(task);
-                            if (LocalDateTime.now().isAfter(task.getRepeatEndDate())){
+                            if (LocalDate.now().isAfter(task.getRepeatEndDate())){
                                 continue;
                             }
                             int between = (int) ChronoUnit.DAYS.between(LocalDateTime.now(), task.getRepeatEndDate());
@@ -249,7 +250,7 @@ public class TaskController {
                             saveInformationListWithCount(task, taskInformation, between, executorIdList);
                         }else if (task.getRepeatEndDate()!=null){
                             TaskInformation taskInformation = selectTaskInformation(task);
-                            if (LocalDateTime.now().isAfter(task.getRepeatEndDate())){
+                            if (LocalDate.now().isAfter(task.getRepeatEndDate())){
                                 continue;
                             }
                             int between = (int) ChronoUnit.DAYS.between(LocalDateTime.now(), task.getRepeatEndDate());

+ 6 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/UserController.java

@@ -112,6 +112,12 @@ public class UserController {
         return userService.loginByUserId(userId, request);
     }
 
+    @RequestMapping("/uploadImage")
+    public HttpRespMsg uploadImage(MultipartFile multipartFile) {
+        return userService.uploadImage(multipartFile, request);
+    }
+
+
     /**
      * 根据id获取用户信息
      * userId 用户id

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

@@ -44,7 +44,7 @@ public class VisitPlanController {
             @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,
+            @RequestParam(value = "remindTime",required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")Date remindTime,
             HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         VisitPlan visitPlan = new VisitPlan();

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

@@ -135,10 +135,10 @@ public class Task extends Model<Task> {
     /**
      * 重复结束 在  ? 日期YYYY-MM-DD之后
      */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
     @TableField("repeat_end_date")
-    private LocalDateTime repeatEndDate;
+    private LocalDate repeatEndDate;
 
     /**
      * 自定义日期: 每 ? 天一次,保存为 x1,x2,x3,x4
@@ -197,10 +197,10 @@ public class Task extends Model<Task> {
     /**
      * 完成日期
      */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
     @TableField("finish_date")
-    private LocalDateTime finishDate;
+    private LocalDate finishDate;
 
 
     /**

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

@@ -35,6 +35,9 @@ public class VisitPlanVO {
     /**完成状态 0未完成 1已完成*/
     private Integer finishState;
 
-    /**客户电话*/
+    /**客户电话[废弃]*/
     private String telPhone;
+
+    /**公司电话*/
+    private String companyPhone;
 }

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

@@ -66,4 +66,6 @@ public interface CustomService extends IService<Custom> {
     HttpRespMsg pinCutom(Custom custom, HttpServletRequest request);
 
     HttpRespMsg undoPin(Custom custom, HttpServletRequest request);
+
+    HttpRespMsg getPrivilegedCustom(HttpServletRequest request);
 }

+ 2 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/UserService.java

@@ -109,4 +109,6 @@ public interface UserService extends IService<User> {
     HttpRespMsg deleteUserSalaryById(String id);
 
     HttpRespMsg getSimpleActiveUserListNew(Integer departmentId, HttpServletRequest request, String keyword, String cursor) throws Exception;
+
+    HttpRespMsg uploadImage(MultipartFile multipartFile, HttpServletRequest request);
 }

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

@@ -156,9 +156,11 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
                 // 折后价格
                 discountedPrice = discountedPrice.add(price.multiply(divide));
             }
+            if(finalPrice.compareTo(new BigDecimal(0))>0){
             BigDecimal divide = discountedPrice.divide(finalPrice, 2, BigDecimal.ROUND_UP);
             // 整单折扣率
             businessOpportunity.setFinalPrice(divide.multiply(new BigDecimal(100)));
+            }
         }
         businessOpportunity.setBusinessItemProducts(businessItemProducts);
 

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

@@ -544,10 +544,34 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
 
     @Override
     public HttpRespMsg getAllCustom(HttpServletRequest request) {
-        User user = userMapper.selectById(request.getHeader("token"));
-        Integer companyId = user.getCompanyId();
+//        User user = userMapper.selectById(request.getHeader("token"));
+//        Integer companyId = user.getCompanyId();
+//        HttpRespMsg msg = new HttpRespMsg();
+//        msg.setData(customMapper.selectList(new QueryWrapper<Custom>().eq("company_id", companyId)));
+//        return msg;
         HttpRespMsg msg = new HttpRespMsg();
-        msg.setData(customMapper.selectList(new QueryWrapper<Custom>().eq("company_id", companyId)));
+        User user = userMapper.selectById(request.getHeader("Token"));
+        boolean isAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部客户");
+        boolean isNotAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看负责部门客户");
+        List<Custom> list = new ArrayList<>();
+        Custom custom = new Custom();
+        custom.setCompanyId(user.getCompanyId());
+        custom.setIsDelete(0);
+        custom.setUserId(user.getId());
+        if (isAll) {
+            //查看全部客户
+            list = customMapper.getList(custom);
+//            i = customMapper.getTotal(custom);
+        } else if (isNotAll) {
+            //查看负责部门线索 找出所处部门下所有的负责人
+            list = customMapper.getList1(custom);
+//            i = customMapper.getTotal1(custom);
+        } else {
+            // 查看负责人为 自己 和 null的数据
+            list = customMapper.getList2(custom);
+//            i = customMapper.getTotal2(custom);
+        }
+        msg.data = list;
         return msg;
     }
 
@@ -931,6 +955,34 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
         return respMsg;
     }
 
+    @Override
+    public HttpRespMsg getPrivilegedCustom(HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("Token"));
+        boolean isAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部客户");
+        boolean isNotAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看负责部门客户");
+        List<Custom> list = new ArrayList<>();
+        Custom custom = new Custom();
+        custom.setCompanyId(user.getCompanyId());
+        custom.setIsDelete(0);
+        custom.setUserId(user.getId());
+        if (isAll) {
+            //查看全部客户
+            list = customMapper.getList(custom);
+//            i = customMapper.getTotal(custom);
+        } else if (isNotAll) {
+            //查看负责部门线索 找出所处部门下所有的负责人
+            list = customMapper.getList1(custom);
+//            i = customMapper.getTotal1(custom);
+        } else {
+            // 查看负责人为 自己 和 null的数据
+            list = customMapper.getList2(custom);
+//            i = customMapper.getTotal2(custom);
+        }
+        msg.data = list;
+        return msg;
+    }
+
 
     private Custom setNull(Custom clue) {
         if (clue.getPlate1() == "") {

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

@@ -1250,6 +1250,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                     List<Integer> deptIds = departmentList.stream().map(Department::getDepartmentId).distinct().collect(Collectors.toList());
                     deptIds.add(-1);
                     List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getDepartmentId, deptIds));
+                    userList.add(user);
                     List<String> userIds = userList.stream().map(User::getId).distinct().collect(Collectors.toList());
                     targetUserIds=userIds;
                     break;
@@ -1257,6 +1258,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                     //本部门
                     Integer targetDeptId1 = user.getDepartmentId();
                     List<User> users1 = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getDepartmentId, targetDeptId1));
+                    users1.add(user);
                     List<String> targetUserIds1 = users1.stream().map(User::getId).distinct().collect(Collectors.toList());
                     targetUserIds=targetUserIds1;
                     break;
@@ -1264,6 +1266,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                     Integer targetDeptId2 = user.getDepartmentId();
                     List<Integer> branchDepartment = getBranchDepartment(targetDeptId2, allDeptList);
                     List<User> users2 = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getDepartmentId, branchDepartment));
+                    users2.add(user);
                     List<String> targetUserIds2 = users2.stream().map(User::getId).distinct().collect(Collectors.toList());
                     targetUserIds=targetUserIds2;
             }

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

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.constant.Constant;
@@ -14,6 +15,7 @@ import com.management.platform.entity.vo.SysRichFunction;
 import com.management.platform.entity.vo.UserVO;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
+import com.management.platform.task.SFTPAsyncUploader;
 import com.management.platform.util.*;
 import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
@@ -22,6 +24,8 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
 import org.apache.commons.collections4.Put;
 import org.apache.commons.io.FileUtils;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
 import org.apache.poi.hssf.usermodel.*;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
@@ -101,6 +105,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
     @Value("${configEnv.isPrivateDeploy}")
     private boolean isPrivateDeploy;
 
+    Logger logger = LogManager.getLogger(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
+    @Autowired
+    public SFTPAsyncUploader sftpAsyncUploader;
+
     public static final String GET_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
     public static final String GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=accessToken&openid=openId&lang=zh_CN";
     public static final String[] MATCHING_FILED = {"corpwx_userid","phone","job_number"};
@@ -1967,6 +1975,64 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
             return msg;
         }
     }
+
+    @Override
+    public HttpRespMsg uploadImage(MultipartFile multipartFile, HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        // 处理文件
+        String fileName = multipartFile.getOriginalFilename();
+        String[] split = fileName.split("\\.");
+        String fileExtension = split.length > 1 ? split[split.length - 1].toLowerCase() : ""; // 获取文件扩展名
+        String[] allowedExtensions = {"jpg", "jpeg", "png", "gif"}; // 允许的图片扩展名
+
+        // 检查扩展名是否有效
+        boolean isValidImage = Arrays.stream(allowedExtensions).anyMatch(fileExtension::equals);
+        if (!isValidImage) {
+            msg.setError("只允许上传图片格式的文件!");
+            return msg; // 返回错误信息
+        }
+
+        String serverName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileExtension;
+
+        // 检查目录
+        File dir = new File(path);
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+        File file = new File(dir, serverName);
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            inputStream = multipartFile.getInputStream();
+            outputStream = new FileOutputStream(file);
+            byte[] buffer = new byte[4096];
+            int temp;
+            while ((temp = inputStream.read(buffer)) != -1) {
+                outputStream.write(buffer, 0, temp);
+            }
+
+            // 关闭流
+            inputStream.close();
+            outputStream.close();
+
+            msg.data = serverName;
+
+            // 更新用户头像 URL
+            userMapper.update(null, new UpdateWrapper<User>()
+                    .eq("id", request.getHeader("token"))
+                    .set("portrait_url", serverName));
+
+            // 上传到 SFTP 服务器
+            sftpAsyncUploader.uploadFileAsync(file);
+
+        } catch (Exception exception) {
+            exception.printStackTrace();
+            logger.error(exception.getMessage());
+            msg.data = "文件上传失败!";
+        }
+
+        return msg;
+    }
 //
 //    private String getAccessToken(String code) {
 //        String accessToken = "";

+ 1 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/time/VisitPlanTask.java

@@ -37,6 +37,7 @@ public class VisitPlanTask {
     private UserMapper userMapper ;
 
     @Scheduled(cron = "0 0/1 * * * *")
+    //TODO 需还原
     public void checkRemindMessage() {
         Date now = new Date();
         System.out.println("===开始检查该分钟需提醒的访客计划==="+now);

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

@@ -318,7 +318,7 @@
         (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
+        (select `name` from `user` where id = creator_id) creatorName,need_pin,pin_time
         from business_opportunity
         where company_id = #{companyId}
         and is_delete = #{isDelete}
@@ -357,7 +357,7 @@
         (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
+        (select `name` from `user` where id = creator_id) creatorName,need_pin,pin_time
         from business_opportunity
         where company_id = #{bo.companyId}
         and is_delete = #{bo.isDelete}
@@ -398,7 +398,7 @@
         (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
+        (select `name` from `user` where id = creator_id) creatorName,need_pin,pin_time
         from business_opportunity
         where company_id = #{bo.companyId}
         and is_delete = #{bo.isDelete}

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

@@ -407,7 +407,7 @@
         c.plate4,
         c.plate5,
         c.create_time,
-        c.create_id,
+        c.create_id,c.need_pin,c.pin_time,
         (select company_name from company where c.company_id = id) companyName,
         (select name from `user` where id = c.incharger_id) inchargerName,
         (select name from `user` where id = c.create_id) createName,
@@ -479,7 +479,7 @@
         c.plate4,
         c.plate5,
         c.create_time,
-        c.create_id,
+        c.create_id,c.need_pin,c.pin_time,
         (select name from `user` where id = c.incharger_id) inchargerName,
         (select name from `user` where id = c.create_id) createName,
         (select name from sys_dict where c.clue_source_id = id and code = 'ClueSources') clueSourceValue,
@@ -553,7 +553,7 @@
         c.plate4,
         c.plate5,
         c.create_time,
-        c.create_id,
+        c.create_id,c.need_pin,c.pin_time,
         (select name from `user` where id = c.incharger_id) inchargerName,
         (select name from `user` where id = c.create_id) createName,
         (select name from sys_dict where c.clue_source_id = id and code = 'ClueSources') clueSourceValue,

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

@@ -406,7 +406,7 @@
         (select `name` from `user` where id = incharger_id) inchargerName,
         c.creator_id,
         c.create_time,
-        (select `name` from `user` where id = creator_id) creatorName
+        (select `name` from `user` where id = creator_id) creatorName,c.need_pin,c.pin_time
         from custom c
         left join sys_dict sd on c.customer_level_id = sd.id
         where c.company_id = #{companyId}
@@ -474,7 +474,7 @@
         (select `name` from `user` where id = incharger_id) inchargerName,
         c.creator_id,
         c.create_time,
-        (select `name` from `user` where id = creator_id) creatorName
+        (select `name` from `user` where id = creator_id) creatorName,c.need_pin,c.pin_time
         from custom c
         left join sys_dict sd on c.customer_level_id = sd.id
         where
@@ -547,7 +547,7 @@
         incharger_id,
         (select `name` from `user` where id = incharger_id) inchargerName,
         creator_id,
-        (select `name` from `user` where id = creator_id) creatorName
+        (select `name` from `user` where id = creator_id) creatorName,c.need_pin,c.pin_time
         from custom
         where company_id = #{companyId}
         and is_delete =#{isDelete}
@@ -577,7 +577,7 @@
         <if test="customerIndustryId != null ">
             and customer_industry_id = #{customerIndustryId}
         </if>
-        ORDER BY need_pin desc,pin_time desc.id desc
+        ORDER BY need_pin desc,pin_time desc,id desc
         <if test="pageFrom != null ">
             Limit #{pageIndex},#{pageFrom}
         </if>

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

@@ -29,7 +29,7 @@
              , vs.visit_time, vs.remark
              , vs.remind_type, case remind_type when -1 then '自定义时间' else sd2.name end as remindTypeName
              ,vs.remind_time, vs.finish_state
-             , vs.create_by,c.tel_phone
+             , vs.create_by,c.tel_phone,c.company_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

+ 215 - 8
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalController.java

@@ -90,6 +90,7 @@ public class ProjectApprovalController {
         List<ProjectCategory> categoryList = projectCategoryService.list(new LambdaQueryWrapper<ProjectCategory>().eq(ProjectCategory::getCompanyId, companyId));
         //编辑重新提交修改状态为待审核
         projectApproval.setStatus(0);
+        projectApproval.setRejectInfo("");
         if(projectApproval.getId()==null){
             projectApproval.setCompanyId(companyId);
             projectApproval.setCreateDate(LocalDate.now());
@@ -115,12 +116,16 @@ public class ProjectApprovalController {
             projectApproval.setInchargerName(id.getName());
         }
         if (projectApproval.getId() != null) {
-            //编辑
-            Integer count=projectMapper.selectCount(new LambdaQueryWrapper<Project>().eq(Project::getCompanyId, companyId).eq(Project::getProjectCode, projectApproval.getProjectCode()));
-            Integer count1=projectApprovalService.count(new LambdaQueryWrapper<ProjectApproval>().eq(ProjectApproval::getCompanyId, companyId).eq(ProjectApproval::getProjectCode, projectApproval.getProjectCode()).ne(ProjectApproval::getId,projectApproval.getId()));
-            if(count>0||count1>0){
-                msg.setError("已存在项目编号为["+projectApproval.getProjectCode()+"]的项目/立项");
-                return msg;
+            if (org.apache.commons.lang3.StringUtils.isNotEmpty(projectApproval.getProjectCode())){
+                //编辑
+                Integer count=projectMapper.selectCount(new LambdaQueryWrapper<Project>().eq(Project::getCompanyId, companyId)
+                        .eq( Project::getProjectCode, projectApproval.getProjectCode())
+                        .ne(Project::getApproveId,projectApproval.getId()));
+                Integer count1=projectApprovalService.count(new LambdaQueryWrapper<ProjectApproval>().eq(ProjectApproval::getCompanyId, companyId).eq(ProjectApproval::getProjectCode, projectApproval.getProjectCode()).ne(ProjectApproval::getId,projectApproval.getId()));
+                if(count>0||count1>0){
+                    msg.setError("已存在项目编号为["+projectApproval.getProjectCode()+"]的项目/立项");
+                    return msg;
+                }
             }
         } else {
             //新增
@@ -181,6 +186,7 @@ public class ProjectApprovalController {
             List<ProjectApprovalCheck> projectApprovalCheckList = projectApproval.getProjectApprovalCheckList();
             projectApprovalCheckList.forEach(p->{
                 p.setProjectApprovalId(projectApproval.getId());
+                p.setStatus(0);
             });
             projectApprovalCheckList=projectApprovalCheckList.stream().filter(p-> !StringUtils.isEmpty(p.getUserId())).collect(Collectors.toList());
             for (int i = 0; i < projectApprovalCheckList.size(); i++) {
@@ -275,6 +281,207 @@ public class ProjectApprovalController {
         return msg;
     }
 
+    @RequestMapping("/tempSave")
+    @Transactional(rollbackFor = Exception.class)
+    public HttpRespMsg tempSave(@RequestBody ProjectApproval projectApproval) throws Exception{
+        HttpRespMsg msg=new HttpRespMsg();
+        String[] arr=new String[]{"正常","紧急","重要","重要且紧急","低风险","中风险","高风险"};
+        User user = userMapper.selectById(request.getHeader("token"));
+        Integer companyId = user.getCompanyId();
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
+        List<ProjectCategory> categoryList = projectCategoryService.list(new LambdaQueryWrapper<ProjectCategory>().eq(ProjectCategory::getCompanyId, companyId));
+        //编辑重新提交修改状态为待提交
+        projectApproval.setStatus(-1);
+        if(projectApproval.getId()==null){
+            projectApproval.setCompanyId(companyId);
+            projectApproval.setCreateDate(LocalDate.now());
+            projectApproval.setCreatorId(user.getId());
+        }
+        //新增操作记录数据
+        ProjectApprovalLog projectApprovalLog=new ProjectApprovalLog();
+        projectApprovalLog.setCreateTime(LocalDateTime.now());
+        projectApprovalLog.setUserName(user.getName());
+        if(projectApproval.getId()==null){
+            //暂存
+            projectApprovalLog.setType(-1);
+        }else {
+            //提交
+            projectApprovalLog.setType(0);
+        }
+        Optional<ProjectCategory> category = categoryList.stream().filter(c -> c.getId().equals(projectApproval.getCategory())).findFirst();
+        if(category.isPresent()){
+            projectApproval.setCategoryName(category.get().getName());
+        }
+        if(projectApproval.getInchargerId()!=null){
+            User id = userMapper.selectById(projectApproval.getInchargerId());
+            projectApproval.setInchargerName(id.getName());
+        }
+        if (projectApproval.getId() != null) {
+            if (org.apache.commons.lang3.StringUtils.isNotEmpty(projectApproval.getProjectCode())){
+                //编辑
+                Integer count=projectMapper.selectCount(new LambdaQueryWrapper<Project>().eq(Project::getCompanyId, companyId)
+                        .eq( Project::getProjectCode, projectApproval.getProjectCode())
+                        .ne(Project::getApproveId,projectApproval.getId()));
+                Integer count1=projectApprovalService.count(new LambdaQueryWrapper<ProjectApproval>().eq(ProjectApproval::getCompanyId, companyId).eq(ProjectApproval::getProjectCode, projectApproval.getProjectCode()).ne(ProjectApproval::getId,projectApproval.getId()));
+                if(count>0||count1>0){
+                    msg.setError("已存在项目编号为["+projectApproval.getProjectCode()+"]的项目/立项");
+                    return msg;
+                }
+            }
+        } else {
+            //新增
+            if(!StringUtils.isEmpty(projectApproval.getProjectCode())){
+                Integer count=projectMapper.selectCount(new LambdaQueryWrapper<Project>().eq(Project::getCompanyId, companyId).eq(Project::getProjectCode, projectApproval.getProjectCode()));
+                Integer count1=projectApprovalService.count(new LambdaQueryWrapper<ProjectApproval>().eq(ProjectApproval::getCompanyId, companyId).eq(ProjectApproval::getProjectCode, projectApproval.getProjectCode()));
+                if(count>0||count1>0){
+                    msg.setError("已存在项目编号为["+projectApproval.getProjectCode()+"]的项目/立项");
+                    return msg;
+                }
+            }
+        }
+
+        if(!projectApprovalService.saveOrUpdate(projectApproval)){
+            msg.setError("验证失败");
+            return msg;
+        }
+        projectApprovalLog.setProjectApprovalId(projectApproval.getId());
+        projectApprovalLogService.save(projectApprovalLog);
+        participationApprovalService.remove(new LambdaQueryWrapper<ParticipationApproval>().eq(ParticipationApproval::getProjectApprovalId,projectApproval.getId()));
+        projectApprovalBasecostService.remove(new LambdaQueryWrapper<ProjectApprovalBasecost>().eq(ProjectApprovalBasecost::getProjectApprovalId,projectApproval.getId()));
+        projectApprovalAuditorService.remove(new LambdaQueryWrapper<ProjectApprovalAuditor>().eq(ProjectApprovalAuditor::getProjectApprovalId,projectApproval.getId()));
+        projectApprovalCheckService.remove(new LambdaQueryWrapper<ProjectApprovalCheck>().eq(ProjectApprovalCheck::getProjectApprovalId,projectApproval.getId()).eq(ProjectApprovalCheck::getStatus,0));
+        if(projectApproval.getParticipationApprovalList()!=null&&projectApproval.getParticipationApprovalList().size()>0){
+            List<ParticipationApproval> participationApprovalList = projectApproval.getParticipationApprovalList();
+            participationApprovalList.forEach(p->{
+                p.setProjectApprovalId(projectApproval.getId());
+                Optional<User> first = userList.stream().filter(u -> u.getId().equals(p.getUserId())).findFirst();
+                if(first.isPresent()){
+                    p.setUserName(first.get().getName());
+                }
+            });
+            if(participationApprovalList.size()>0){
+                participationApprovalService.saveBatch(participationApprovalList);
+            }
+        }
+        if(projectApproval.getProjectApprovalBasecostList()!=null&&projectApproval.getProjectApprovalBasecostList().size()>0){
+            List<ProjectApprovalBasecost> projectApprovalBasecostList = projectApproval.getProjectApprovalBasecostList();
+            projectApprovalBasecostList.forEach(p->{
+                p.setProjectApprovalId(projectApproval.getId());
+            });
+            projectApprovalBasecostService.saveBatch(projectApprovalBasecostList);
+        }
+        if(projectApproval.getProjectApprovalAuditorList()!=null&&projectApproval.getProjectApprovalAuditorList().size()>0){
+            List<ProjectApprovalAuditor> projectApprovalAuditorList = projectApproval.getProjectApprovalAuditorList();
+            projectApprovalAuditorList.forEach(p->{
+                p.setProjectApprovalId(projectApproval.getId());
+                Optional<User> first = userList.stream().filter(u -> u.getId().equals(p.getAuditorId())).findFirst();
+                if(first.isPresent()){
+                    p.setAuditorName(first.get().getName());
+                }
+            });
+            if(projectApprovalAuditorList.size()>0){
+                projectApprovalAuditorService.saveBatch(projectApprovalAuditorList);
+            }
+        }
+        if(projectApproval.getProjectApprovalCheckList()!=null&&projectApproval.getProjectApprovalCheckList().size()>0){
+            List<ProjectApprovalCheck> projectApprovalCheckList = projectApproval.getProjectApprovalCheckList();
+            projectApprovalCheckList.forEach(p->{
+                p.setProjectApprovalId(projectApproval.getId());
+            });
+            projectApprovalCheckList=projectApprovalCheckList.stream().filter(p-> !StringUtils.isEmpty(p.getUserId())).collect(Collectors.toList());
+            for (int i = 0; i < projectApprovalCheckList.size(); i++) {
+                projectApprovalCheckList.get(i).setSeq(i+1);
+            }
+            if(projectApprovalCheckList.size()>0){
+                projectApprovalCheckService.saveOrUpdateBatch(projectApprovalCheckList);
+            }
+        }
+
+        if(projectApproval.getStatus()==-1){
+            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                //更新审核人 发送到企业微信完成审核动作
+                String detail = wxCorpInfoService.getTemplateDetail(companyId);
+                if(detail!=null&&!StringUtils.isEmpty(detail)){
+                    DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+                    JSONObject resultOb = JSONObject.parseObject(detail);
+                    JSONObject template_content = resultOb.getJSONObject("template_content");
+                    JSONArray controls = template_content.getJSONArray("controls");
+                    Map<String,Object> templateMap=new HashMap<>();
+                    WxCorpTemplate template = wxCorpTemplateService.getById(companyId);
+                    JSONObject requestData=new JSONObject();
+                    requestData.put("template_id",template.getTemplateId());
+                    requestData.put("creator_userid",user.getCorpwxUserid());
+                    //审核模式  0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用);1-使用此模板在管理后台设置的审批流程(需要保证审批流程中没有“申请人自选”节点),支持条件审批
+                    requestData.put("use_template_approver",1);
+                    JSONObject apply_data=new JSONObject();
+                    JSONArray contents=new JSONArray();
+                    for (int i = 0; i < controls.size(); i++) {
+                        JSONObject item = controls.getJSONObject(i);
+                        JSONObject property = item.getJSONObject("property");
+                        //控件id指定到指定控件
+                        String templateId = property.getString("id");
+                        //控件名称 这里为了统一工时管家格式 限制企业需要用到我们提供的名称(项目编号 项目名称 项目分类 项目类型 项目经理 级别 合同金额 计划开始日期 计划结束日期)
+                        JSONArray titleArray = property.getJSONArray("title");
+                        //控件类型 在这里统一用Text  Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间;Selector-单选/多选;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细;Attendance-假勤控件;Vacation-请假控件;Location-位置;RelatedApproval-关联审批单;Formula-公式;DateRange-时长;BankAccount-收款账户
+                        String control = property.getString("control");
+                        //是否必填
+                        String require = property.getString("require");
+                        //是否打印
+                        String un_print = property.getString("un_print");
+                        JSONObject content=new JSONObject();
+                        content.put("control",control);
+                        content.put("id",templateId);
+                        JSONObject value=new JSONObject();
+                        JSONObject titleOb = titleArray.getJSONObject(0);
+                        switch (titleOb.getString("text")){
+                            case "项目编号":
+                                value.put("text",projectApproval.getProjectCode()==null?"":projectApproval.getProjectCode());
+                                break;
+                            case "项目名称":
+                                value.put("text",projectApproval.getProjectName());
+                                break;
+                            case "项目分类":
+                                value.put("text",projectApproval.getCategoryName()==null?"":projectApproval.getCategoryName());
+                                break;
+                            case "项目类型":
+                                value.put("text",projectApproval.getIsPublic()==0?"正式项目":"非项目");
+                                break;
+                            case "项目经理":
+                                value.put("text",projectApproval.getInchargerName()==null?"":("$userName="+projectApproval.getInchargerName()+"$"));
+                                break;
+                            case "级别":
+                                value.put("text",arr[projectApproval.getLevel()-1]);
+                                break;
+                            case "合同金额":
+                                value.put("text",projectApproval.getContractAmount()==null?String.valueOf(0):String.valueOf(projectApproval.getContractAmount()));
+                                break;
+                            case "计划开始日期":
+                                value.put("text",projectApproval.getPlanStartDate()==null?"":df.format(projectApproval.getPlanStartDate()));
+                                break;
+                            case "计划结束日期":
+                                value.put("text",projectApproval.getPlanEndDate()==null?"":df.format(projectApproval.getPlanEndDate()));
+                                break;
+                        }
+                        content.put("value",value);
+                        contents.add(content);
+                    }
+                    apply_data.put("contents",contents);
+                    requestData.put("apply_data",apply_data);
+                    requestData.put("approver",new JSONArray());
+                    requestData.put("summary_list",new JSONArray());
+                    System.out.println("===============>发送到企业微信的数据"+requestData);
+                    //发送到企业微信
+                    String sp_no = wxCorpInfoService.applyEvent(request, requestData);
+                    projectApproval.setWxCorpSpNo(sp_no);
+                    projectApprovalService.updateById(projectApproval);
+                }else {
+                    throw  new Exception("企业暂未同步服务商审批模板,推送企业微信审批失败,请联系服务商!");
+                }
+            }
+        }
+        return msg;
+    }
 
     @RequestMapping("/getDetail")
     public HttpRespMsg getDetail(Integer id){
@@ -319,8 +526,8 @@ public class ProjectApprovalController {
 
     @RequestMapping("/check")
     @Transactional(rollbackFor = Exception.class)
-    public HttpRespMsg check(Integer id,Integer checkType,Integer revokeType){
-        return projectApprovalService.check(id,checkType,revokeType);
+    public HttpRespMsg check(Integer id,Integer checkType,Integer revokeType,String rejectInfo){
+        return projectApprovalService.check(id,checkType,revokeType,rejectInfo);
     }
 
     @RequestMapping("/importData")

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java

@@ -465,6 +465,17 @@ public class ProjectController {
         return projectService.getProjectTask(pageIndex, pageSize, projectId,groupId, request,taskType);
     }
 
+
+    @RequestMapping("/getProjectTaskPlanAndRealCost")
+    public HttpRespMsg getProjectTaskPlanAndRealCost(@RequestParam Integer pageIndex, @RequestParam Integer pageSize, Integer projectId,Integer taskType) {
+        return projectService.getProjectTaskPlanAndRealCost(pageIndex, pageSize, projectId,request,taskType);
+    }
+
+    @RequestMapping("/exportProjectTaskPlanAndRealCost")
+    public HttpRespMsg exportProjectTaskPlanAndRealCost(Integer projectId,Integer taskType) {
+        return projectService.exportProjectTaskPlanAndRealCost(projectId,request,taskType);
+    }
+
     //分页查询项目各个阶段的汇总工时成本
     @RequestMapping("/getProjectStagesCost")
     public HttpRespMsg getProjectStagesCost(@RequestParam Integer pageIndex, @RequestParam Integer pageSize, Integer projectId,String stageNames,String startDate,String endDate) {

+ 165 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java

@@ -3,23 +3,23 @@ package com.management.platform.controller;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.github.pagehelper.IPage;
 import com.management.platform.entity.*;
+import com.management.platform.entity.bo.QueryTaskChargePage;
 import com.management.platform.entity.vo.SysRichFunction;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MessageUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.poi.hssf.usermodel.*;
 import org.assertj.core.util.Lists;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
+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;
@@ -118,6 +118,51 @@ public class TaskController {
     @Autowired
     private ProjectCategoryMapper projectCategoryMapper;
 
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
+
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Autowired
+    private TaskFilesMapper taskFilesMapper;
+
+    @Resource
+    private WxCorpInfoMapper wxCorpInfoMapper;
+
+    @PostMapping("/getTaskChargePage")
+    public HttpRespMsg getTaskChargePage(
+            QueryTaskChargePage queryBO, HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
+        msg = taskService.getTaskChargePage(queryBO,request);
+        return msg;
+    }
+
+
+
+    @PostMapping("/getFileRejectReason")
+    public HttpRespMsg getFileRejectReason(@RequestParam("taskId")String taskId,HttpServletRequest request){
+        return taskService.getFileRejectReason(taskId,request);
+    }
+
+
+    /***
+     * 文件审核
+     * @param taskId 任务id
+     * @param projectId 项目id
+     * @param auditStatus 审核状态 1通过 2驳回
+     * @param reason 驳回理由
+     * @param request
+     * @return
+     */
+    @PostMapping("/auditFile")
+    public HttpRespMsg auditFile(@RequestParam("taskId")Integer taskId
+            ,@RequestParam("projectId")String projectId
+            ,@RequestParam("auditStatus")Integer auditStatus
+            ,@RequestParam(value = "reason",required = false)String reason
+            ,HttpServletRequest request) {
+        return taskService.auditFile(taskId,projectId,auditStatus,reason,request);
+    }
+
     @RequestMapping("/save")
     @Transactional
     public HttpRespMsg save(Task task) throws Exception {
@@ -135,6 +180,24 @@ public class TaskController {
             task.setCreaterName(user.getName());
             task.setCreatorColor(user.getColor());
             task.setCompanyId(user.getCompanyId());
+            TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+            Integer taskFileCharge = timeType.getTaskFileCharge();
+            if(1 == taskFileCharge){
+                //主管: 员工所在部门[department]的manager_id 阶段一
+                //项目经理:project表的in_charge_id 阶段二
+                Department department = departmentMapper.selectById(user.getDepartmentId());
+                Project project = projectMapper.selectById(task.getProjectId());
+                if(org.apache.commons.lang3.StringUtils.isBlank(department.getManagerId())){
+                    msg.setError("员工所在部门无负责人,请重新设置");
+                    return msg;
+                }
+                if(org.apache.commons.lang3.StringUtils.isBlank(project.getInchargerId())){
+                    msg.setError("该项目未设置项目经理,请重新设置");
+                    return msg;
+                }
+                task.setChargeOneId(department.getManagerId());
+                task.setChargeTwoId(project.getInchargerId());
+            }
         }
         if (!StringUtils.isEmpty(executorListStr)) {
             List<User> allUsers = userMapper.selectList(new QueryWrapper<User>().eq("company_id", user.getCompanyId()));
@@ -269,6 +332,15 @@ public class TaskController {
                 needRecalculateProgress = true;
             }
         } else {
+            task.setChargeOneId(null);
+            task.setChargeStage(null);
+            task.setChargeOneStatus(null);
+            task.setChargeOneTime(null);
+            task.setChargeTwoId(null);
+            task.setChargeTwoStatus(null);
+            task.setChargeTwoTime(null);
+            task.setFinalChargeStatus(null);
+            task.setFileRejectReason(null);
             //更新的情况,需要对比是否修改了任务标题,更新子任务的parentTname
             Task oldTask = taskService.getById(task.getId());
             if (!oldTask.getName().equals(task.getName())) {
@@ -818,6 +890,17 @@ public class TaskController {
             }
         });
         t.setExecutorList(executorList);
+        //设置人员选择下拉列表
+        List<User> userList = null;
+        if (executorList.size() > 0) {
+            List<String> exeIds = executorList.stream().map(TaskExecutor::getExecutorId).collect(Collectors.toList());
+            //查询包含执行人(即使被停用)和在职的员工
+            userList = userMapper.selectList(new QueryWrapper<User>().select("id, name, phone, job_number, department_id, is_active").eq("company_id", user.getCompanyId()).and(w->w.eq("is_active", 1).or().in("id", exeIds)));
+        } else {
+            //仅仅需要在职员工
+            userList = userMapper.selectList(new QueryWrapper<User>().select("id, name, phone, job_number, department_id, is_active").eq("company_id", user.getCompanyId()).eq("is_active", 1));
+        }
+        t.setUserList(userList);
         //查询项目负责人id以及所属任务分组负责人id
         Project project = projectService.getOne(new QueryWrapper<Project>().eq("id", t.getProjectId()));
         TaskGroup group = taskGroupService.getOne(new QueryWrapper<TaskGroup>().eq("id", t.getGroupId()));
@@ -1243,10 +1326,16 @@ public class TaskController {
         if(dateType!=null){
             switch (dateType){
                 case 0:
-                    queryWrapper.ge("start_date",startDate).le("start_date",endDate);
+                    if(null != startDate && null != endDate){
+                        queryWrapper.ge("start_date",startDate).le("start_date",endDate);
+                    }
+
                     break;
                 case 1:
-                    queryWrapper.ge("task.end_date",startDate).le("task.end_date",endDate);
+                    if(null != startDate && null != endDate){
+                        queryWrapper.ge("task.end_date",startDate).le("task.end_date",endDate);
+                    }
+
                     break;
             }
         }
@@ -1279,9 +1368,77 @@ public class TaskController {
         List<Integer> collect = list.stream().map(l -> l.getId()).distinct().collect(Collectors.toList());
         collect.add(-1);
         List<TaskExecutor> taskExecutorList = taskExecutorMapper.selectList(new QueryWrapper<TaskExecutor>().in("task_id", collect));
+        List<TaskFiles> taskFilesList = taskFilesMapper.selectList(new LambdaQueryWrapper<TaskFiles>().in(TaskFiles::getTaskId, collect));
         List<Integer> pids = list.stream().map(Task::getProjectId).collect(Collectors.toList());
         pids.add(-1);
         List<Project> projectList = projectService.list(new QueryWrapper<Project>().in("id", pids));
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id",user.getCompanyId()));
+        CompanyDingding dingding = companyDingdingService.getOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, user.getCompanyId()));
+        if(1 == timeType.getTaskFileCharge()){
+            List<String> userIds = new ArrayList<>();
+            List<String> chargeOneIds = list.stream().filter(t-> org.apache.commons.lang3.StringUtils.isNotBlank(t.getChargeOneId()))
+                    .map(Task::getChargeOneId).collect(Collectors.toList());
+            List<String> chargeTwoIds = list.stream().filter(t-> org.apache.commons.lang3.StringUtils.isNotBlank(t.getChargeTwoId()))
+                    .map(Task::getChargeTwoId).collect(Collectors.toList());
+            userIds.addAll(chargeOneIds);
+            userIds.addAll(chargeTwoIds);
+
+            Map<String, User> userIdMap = new HashMap<>();
+            if(CollectionUtils.isNotEmpty(userIds)){
+                userIds = userIds.stream().distinct().collect(Collectors.toList());
+                List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>()
+                                .select(User::getId,User::getName)
+                        .in(User::getId, userIds)
+                );
+                userIdMap = users.stream().collect(Collectors.toMap(User::getId, t->t));
+            }
+            Map<Integer, List<TaskFiles>> taskFilesMap = new HashMap<>();
+            if(CollectionUtils.isNotEmpty(taskFilesList)){
+                taskFilesMap  = taskFilesList.stream().collect(Collectors.groupingBy(TaskFiles::getTaskId));
+            }
+            for (Task task : list) {
+                List<TaskFiles> tmp = taskFilesMap.get(task.getId());
+                if(CollectionUtils.isEmpty(tmp)){
+                    continue;
+                }
+                if(org.apache.commons.lang3.StringUtils.isBlank(task.getChargeOneId()) || org.apache.commons.lang3.StringUtils.isBlank(task.getChargeTwoId())){
+                    continue;
+                }
+                if(1 == task.getFinalChargeStatus()){
+                    task.setFinalChargeStatusText("审核通过");
+                    task.setFileChargeStatus(1);
+                } else if (0 == task.getFinalChargeStatus()) {
+                    String name = "";
+                    String statusText = "";
+                    String userWxId = "";
+                    String fileChargeStatusText = "";
+                    int finalStatus = 1==task.getChargeStage()?task.getChargeOneStatus():task.getChargeTwoStatus();
+                    String tmpUserId = 1==task.getChargeStage()?task.getChargeOneId():task.getChargeTwoId();
+                    User chargeUser = userIdMap.get(tmpUserId);
+                    if(null != chargeUser){
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            userWxId = chargeUser.getCorpwxRealUserid();
+                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                            userWxId = chargeUser.getDingdingUserid();
+                        }
+                        name = chargeUser.getName();
+                    }
+                    switch (finalStatus)
+                    {
+                        case 0:  statusText = "待审核"; task.setFileChargeStatus(0);break;
+                        case 1:  statusText = "通过";break;
+                        case 2:  statusText = "驳回"; task.setFileChargeStatus(2);break;
+                    }
+                    if(org.apache.commons.lang3.StringUtils.isBlank(userWxId)){
+                        fileChargeStatusText = statusText+"("+name+")";
+                    }else{
+                        fileChargeStatusText = statusText+"("+("$userName=" + userWxId + "$")+")";
+                    }
+                    task.setFinalChargeStatusText(fileChargeStatusText);
+                }
+            }
+        }
         list.forEach(l->{
             List<TaskExecutor> executorList = taskExecutorList.stream().filter(tl -> tl.getTaskId().equals(l.getId())&&tl.getExecutorId()!=null).collect(Collectors.toList());
             l.setExecutorList(executorList);
@@ -1297,6 +1454,7 @@ public class TaskController {
         msg.data = map;
         return msg;
     }
+
     private List<Integer> getBranchDepartment(Integer departmentId, List<Department> departmentList) {
         List<Integer> list = new ArrayList<>();
         list.add(departmentId);
@@ -1414,6 +1572,7 @@ public class TaskController {
         titleList.add("优先级");
         titleList.add("任务名称");
         titleList.add("执行人");
+        titleList.add("文件审核状态");
         titleList.add("开始时间");
         titleList.add("截止时间");
         boolean anyMatch = projectList.stream().anyMatch(p -> p.getDeptId() != null && !StringUtils.isEmpty(p.getDeptId()));
@@ -1453,6 +1612,7 @@ public class TaskController {
                 }
             }).collect(Collectors.joining(","));
             item.add(executorString);
+            item.add(task.getFinalChargeStatusText());
             item.add(task.getStartDate()==null?"":df.format(task.getStartDate()));
             item.add(task.getEndDate()==null?"":df.format(task.getEndDate()));
             if(anyMatch){

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskExecutorController.java

@@ -1,10 +1,15 @@
 package com.management.platform.controller;
 
 
+import com.management.platform.entity.TaskExecutor;
+import com.management.platform.service.TaskExecutorService;
+import com.management.platform.util.HttpRespMsg;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
+
 /**
  * <p>
  *  前端控制器
@@ -16,6 +21,15 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/task-executor")
 public class TaskExecutorController {
+    @Resource
+    private TaskExecutorService taskExecutorService;
 
+    @RequestMapping("modify")
+    public HttpRespMsg modify(TaskExecutor taskExecutor) {
+        HttpRespMsg msg = new HttpRespMsg();
+        taskExecutorService.updateById(taskExecutor);
+        msg.data = taskExecutor;
+        return msg;
+    }
 }
 

+ 217 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskFilesController.java

@@ -1,17 +1,24 @@
 package com.management.platform.controller;
 
 
+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.management.platform.entity.*;
 import com.management.platform.mapper.*;
+import com.management.platform.service.CompanyDingdingService;
 import com.management.platform.service.ProjectDocumentService;
+import com.management.platform.service.WxCorpInfoService;
 import com.management.platform.task.SFTPAsyncUploader;
-import com.management.platform.util.*;
+import com.management.platform.util.DocumentTypeUtil;
+import com.management.platform.util.FileUtil;
+import com.management.platform.util.HttpRespMsg;
+import com.management.platform.util.MessageUtils;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.util.StringUtils;
 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.multipart.MultipartFile;
@@ -21,12 +28,9 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
+import java.time.LocalDateTime;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /**
  * <p>
@@ -53,6 +57,24 @@ public class TaskFilesController {
     @Autowired
     private SFTPAsyncUploader sftpAsyncUploader;
 
+    @Resource
+    private TaskMapper taskMapper;
+
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
+
+    @Resource
+    private TaskCommentMapper taskCommentMapper;
+
+    @Resource
+    private WxCorpInfoService wxCorpInfoService;
+
+    @Resource
+    private WxCorpInfoMapper wxCorpInfoMapper;
+
+    @Resource
+    private CompanyDingdingService companyDingdingService;
+
     /**
      * 获取该项目下的所有有效的文件列表
      * @param keyword
@@ -93,6 +115,33 @@ public class TaskFilesController {
         record.setServerName(document.getServerName());
         record.setUrl(document.getUrl());
         taskFilesMapper.insert(record);
+
+        //判断文件阶段做回退处理
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        if(1 == timeType.getTaskFileCharge()){
+            this.resetTaskChargeStatus(taskId);
+        }
+
+        //添加动态消息
+        Task task = taskMapper.selectById(taskId);
+        String content = user.getName()+"在任务:"+task.getName()+"中关联了文件:"+document.getDocumentName();
+        TaskComment comment =  new TaskComment();
+        comment.setTaskId(taskId);
+        comment.setUserId(user.getId());
+        comment.setContent(content);
+        comment.setCreateTime(LocalDateTime.now());
+        comment.setUserColor(user.getColor());
+        comment.setUserName(user.getName());
+        taskCommentMapper.insert(comment);
+
+//        WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
+//        if(1 == task.getChargeStage()){
+//            User tmpUser = userMapper.selectById(task.getChargeOneId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        } else if (2 == task.getChargeStage()) {
+//            User tmpUser = userMapper.selectById(task.getChargeTwoId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        }
         return msg;
     }
 
@@ -115,11 +164,13 @@ public class TaskFilesController {
             HttpServletResponse response) throws Exception {
         HttpRespMsg msg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("Token"));
+        StringBuilder fileNames = new StringBuilder();
         for (MultipartFile file : files) {
             TaskFiles record = new TaskFiles();
             record.setCreatorId(user.getId());
             record.setCreatorName(user.getName());
             record.setDocumentName(file.getOriginalFilename());
+            fileNames.append(file.getOriginalFilename()).append(",");
             record.setTaskId(taskId);
             record.setProjectId(projectId);
             if (file != null && !file.isEmpty()) {
@@ -193,12 +244,46 @@ public class TaskFilesController {
             }
         }
 
+        //判断文件阶段做回退处理
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        if(1 == timeType.getTaskFileCharge()){
+            this.resetTaskChargeStatus(taskId);
+        }
+
+        //添加动态消息
+        String resFileNames = fileNames.deleteCharAt(fileNames.length() - 1).toString();
+        Task task = taskMapper.selectById(taskId);
+        String content = user.getName()+"在任务:"+task.getName()+"中上传了文件:"+resFileNames;
+        TaskComment comment =  new TaskComment();
+        comment.setTaskId(taskId);
+        comment.setUserId(user.getId());
+        comment.setContent(content);
+        comment.setCreateTime(LocalDateTime.now());
+        comment.setUserColor(user.getColor());
+        comment.setUserName(user.getName());
+        taskCommentMapper.insert(comment);
+
+//        WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
+//        if(1 == task.getChargeStage()){
+//            User tmpUser = userMapper.selectById(task.getChargeOneId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        } else if (2 == task.getChargeStage()) {
+//            User tmpUser = userMapper.selectById(task.getChargeTwoId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        }
+
         return msg;
     }
 
     @RequestMapping(value="delete")
-    public HttpRespMsg delFile(@RequestParam Integer id) {
+    public HttpRespMsg delFile(@RequestParam Integer id,HttpServletRequest request) {
+        HttpRespMsg msg = new HttpRespMsg();
         TaskFiles taskFiles = taskFilesMapper.selectById(id);
+        User user = userMapper.selectById(request.getHeader("Token"));
+        if(!taskFiles.getCreatorId().equals(user.getId())){
+            msg.setError("您非文件上传人,无法删除");
+            return msg;
+        }
         if (taskFiles.getDocumentId() == null) {
             //仅当前任务上传的,需要把文件删掉
             File dir = new File(uploadPath);
@@ -209,7 +294,35 @@ public class TaskFilesController {
         }
         taskFilesMapper.deleteById(id);
 
-        HttpRespMsg msg = new HttpRespMsg();
+        //判断文件阶段做回退处理
+
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        if(1 == timeType.getTaskFileCharge()){
+            this.resetTaskChargeStatus(taskFiles.getTaskId());
+        }
+
+        //添加动态消息
+        Task task = taskMapper.selectById(taskFiles.getTaskId());
+        String content = user.getName()+"删除了任务:"+task.getName()+"中的文件:"+taskFiles.getDocumentName();
+        TaskComment comment =  new TaskComment();
+        comment.setTaskId(taskFiles.getTaskId());
+        comment.setUserId(user.getId());
+        comment.setContent(content);
+        comment.setCreateTime(LocalDateTime.now());
+        comment.setUserColor(user.getColor());
+        comment.setUserName(user.getName());
+        taskCommentMapper.insert(comment);
+
+//        WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", user.getCompanyId()));
+//        if(1 == task.getChargeStage()){
+//            User tmpUser = userMapper.selectById(task.getChargeOneId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        } else if (2 == task.getChargeStage()) {
+//            User tmpUser = userMapper.selectById(task.getChargeTwoId());
+//            wxCorpInfoService.sendWXCorpMsg(info,tmpUser.getCorpwxUserid(),content+",请及时审核",null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_TASK_FILE_UPDATE);
+//        }
+
+
         msg.data=MessageUtils.message("file.deleteSuc");
         return msg;
     }
@@ -235,12 +348,106 @@ public class TaskFilesController {
      * taskId 任务id
      */
     @RequestMapping(value="getTaskFiles")
-    public HttpRespMsg getTaskFiles(Integer taskId) {
-        List<TaskFiles> list = taskFilesMapper.selectList(new QueryWrapper<TaskFiles>().eq("task_id", taskId));
+    public HttpRespMsg getTaskFiles(Integer taskId,HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        Task task = taskMapper.selectById(taskId);
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id",user.getCompanyId()));
+        CompanyDingding dingding = companyDingdingService.getOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, user.getCompanyId()));
+        String fileChargeStatusText = "";
+        int fileChargeStatus = 0;
+        if(1 == timeType.getTaskFileCharge()){
+            if(org.apache.commons.lang3.StringUtils.isNotBlank(task.getChargeOneId())
+                    &&org.apache.commons.lang3.StringUtils.isNotBlank(task.getChargeTwoId())){
+                if(1 == task.getFinalChargeStatus()){
+                    fileChargeStatusText = "审核通过";
+                    fileChargeStatus = 1;
+                } else if (0 == task.getFinalChargeStatus()) {
+                    String name = "";
+                    String statusText = "";
+                    String userWxId = "";
+                    int finalStatus = 1==task.getChargeStage()?task.getChargeOneStatus():task.getChargeTwoStatus();
+                    String userId = 1==task.getChargeStage()?task.getChargeOneId():task.getChargeTwoId();
+                    User chargeUser = userMapper.selectById(userId);
+                    if(null != chargeUser){
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            userWxId = chargeUser.getCorpwxRealUserid();
+                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                            userWxId = chargeUser.getDingdingUserid();
+                        }
+                        name = chargeUser.getName();
+                    }
+                    switch (finalStatus)
+                    {
+                        case 0:  statusText = "待审核";fileChargeStatus = 0;break;
+                        case 1:  statusText = "通过";break;
+                        case 2:  statusText = "驳回";fileChargeStatus = 2;break;
+                    }
+                    if(org.apache.commons.lang3.StringUtils.isBlank(userWxId)){
+                        fileChargeStatusText = statusText+"("+name+")";
+                    }else{
+                        fileChargeStatusText = statusText+"("+("$userName=" + userWxId + "$")+")";
+                    }
+
+                }
+            }
+        }
+        List<TaskFiles> list = taskFilesMapper.selectList(new LambdaQueryWrapper<TaskFiles>()
+                .eq(TaskFiles::getTaskId,taskId)
+        );
+        if(CollectionUtils.isNotEmpty(list)){
+            for (TaskFiles taskFiles : list) {
+                taskFiles.setFileChargeStatusText(fileChargeStatusText);
+                taskFiles.setFileChargeStatus(fileChargeStatus);
+            }
+        }
         HttpRespMsg msg = new HttpRespMsg();
         msg.data = list;
         return msg;
     }
 
+
+    public void resetTaskChargeStatus(Integer taskId){
+        Task task = taskMapper.selectById(taskId);
+        if(1 == task.getFinalChargeStatus()){
+            //重置阶段一、二的时间、状态、理由
+            //重置 文件审核阶段为 1 阶段一  最终审核状态为 0未通过
+            taskMapper.update(null,new LambdaUpdateWrapper<Task>()
+                    .set(Task::getFinalChargeStatus, 0)
+                    .set(Task::getChargeStage,1)
+                    .set(Task::getChargeOneStatus,0)
+                    .set(Task::getChargeTwoStatus,0)
+                    .set(Task::getChargeOneTime,null)
+                    .set(Task::getChargeTwoTime,null)
+                    .set(Task::getFileRejectReason,null)
+//                    .set(Task::getChargeOneReason,null)
+//                    .set(Task::getChargeTwoReason,null)
+                    .eq(Task::getId,task.getId())
+            );
+        } else if (0 == task.getFinalChargeStatus()) {
+            //判断当前阶段
+            if(1==task.getChargeStage()){
+                //重置阶段一的时间、状态、理由
+                taskMapper.update(null,new LambdaUpdateWrapper<Task>()
+                        .set(Task::getChargeOneStatus,0)
+                        .set(Task::getChargeOneTime,null)
+                        .set(Task::getFileRejectReason,null)
+                        .eq(Task::getId,task.getId())
+                );
+            } else if (2 == task.getChargeStage()) {
+                //重置阶段二的时间、状态、理由
+                taskMapper.update(null,new LambdaUpdateWrapper<Task>()
+                        .set(Task::getChargeStage,1)
+                        .set(Task::getChargeOneStatus,0)
+                        .set(Task::getChargeOneTime,null)
+                        .set(Task::getChargeTwoStatus,0)
+                        .set(Task::getChargeTwoTime,null)
+                        .set(Task::getFileRejectReason,null)
+                        .eq(Task::getId,task.getId())
+                );
+            }
+        }
+    }
+
 }
 

+ 21 - 21
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java

@@ -114,7 +114,7 @@ public class UserCorpwxTimeController {
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);
         List<Map> list = new ArrayList<Map>();
-        int onlyWorkDays = 1;//只要工作日
+//        int onlyWorkDays = 0;//只要工作日
         List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "全部企微考勤");
         if (functionList.size() > 0) {
             //查看全部人员的
@@ -135,17 +135,17 @@ public class UserCorpwxTimeController {
         }
 
         //工作日处理,排除常规周末和法定节假日
-        if (onlyWorkDays == 1) {
-            DateTimeFormatter dtf =  DateTimeFormatter.ofPattern("yyyy/MM/dd");
-            list = list.stream().filter(time->{
-                String date = (String)time.get("createDate");
-                if (WorkDayCalculateUtils.isWorkDay(LocalDate.parse(date, dtf))) {
-                    return true;
-                } else {
-                    return false;
-                }
-            }).collect(Collectors.toList());
-        }
+//        if (onlyWorkDays == 1) {
+//            DateTimeFormatter dtf =  DateTimeFormatter.ofPattern("yyyy/MM/dd");
+//            list = list.stream().filter(time->{
+//                String date = (String)time.get("createDate");
+//                if (WorkDayCalculateUtils.isWorkDay(LocalDate.parse(date, dtf))) {
+//                    return true;
+//                } else {
+//                    return false;
+//                }
+//            }).collect(Collectors.toList());
+//        }
         DateTimeFormatter standFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         HashMap item = new HashMap();
         item.put("list", list);
@@ -231,14 +231,14 @@ public class UserCorpwxTimeController {
         list = userCorpwxTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, null, user.getId());
         //工作日处理,排除常规周末和法定节假日
         DateTimeFormatter dtf =  DateTimeFormatter.ofPattern("yyyy/MM/dd");
-        list = list.stream().filter(time->{
-            String date = (String)time.get("createDate");
-            if (WorkDayCalculateUtils.isWorkDay(LocalDate.parse(date, dtf))) {
-                return true;
-            } else {
-                return false;
-            }
-        }).collect(Collectors.toList());
+//        list = list.stream().filter(time->{
+//            String date = (String)time.get("createDate");
+//            if (WorkDayCalculateUtils.isWorkDay(LocalDate.parse(date, dtf))) {
+//                return true;
+//            } else {
+//                return false;
+//            }
+//        }).collect(Collectors.toList());
         DateTimeFormatter standFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         HashMap item = new HashMap();
         item.put("list", list);
@@ -465,7 +465,7 @@ public class UserCorpwxTimeController {
         CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
         Integer manageDeptId = user.getManageDeptId();
 
-        List<Map> list = new ArrayList<Map>();
+        List<Map> list;
         List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "全部企微考勤");
         //只取当月中工作日的数据
         DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Project.java

@@ -13,6 +13,7 @@ import java.util.List;
 import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
 import com.management.platform.config.PropertyMsg;
 import com.management.platform.entity.vo.GroupEstimatedWorkVO;
 import lombok.Data;
@@ -31,6 +32,7 @@ import org.springframework.format.annotation.DateTimeFormat;
 @Data
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
 public class Project extends Model<Project> {
 
     private static final long serialVersionUID=1L;

+ 16 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApproval.java

@@ -88,7 +88,7 @@ public class ProjectApproval extends Model<ProjectApproval> {
     private Integer level;
 
     /**
-     * 立项状态 0-待审核 1-已通过 2-已驳回
+     * 立项状态 -1-待提交 0-待审核 1-已通过 2-已驳回
      */
     @TableField("status")
     private Integer status;
@@ -247,6 +247,12 @@ public class ProjectApproval extends Model<ProjectApproval> {
     @TableField("wx_corp_sp_no")
     private String wxCorpSpNo;
 
+    /**
+     * 驳回原因
+     */
+    @TableField("reject_info")
+    private String rejectInfo;
+
     @TableField(exist = false)
     private List<ProjectApprovalBasecost> projectApprovalBasecostList;
 
@@ -265,6 +271,15 @@ public class ProjectApproval extends Model<ProjectApproval> {
     @TableField(exist = false)
     private Integer canChecked;
 
+    @TableField(exist = false)
+    private Boolean isFirstCheckUser;
+
+    @TableField(exist = false)
+    private Boolean isCheckedUser;
+
+    @TableField(exist = false)
+    private String auditStr;
+
 
     @Override
     protected Serializable pkVal() {

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApprovalCheck.java

@@ -37,7 +37,7 @@ public class ProjectApprovalCheck extends Model<ProjectApprovalCheck> {
     private String userId;
 
     /**
-     * 审核状态 0-待审核 1-审核通过 2-已驳回
+     * 审核状态 -1待提交 0-待审核 1-审核通过 2-已驳回
      */
     @TableField("status")
     private Integer status;

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApprovalLog.java

@@ -43,7 +43,7 @@ public class ProjectApprovalLog extends Model<ProjectApprovalLog> {
     private LocalDateTime createTime;
 
     /**
-     * 操作类型 0-提交 1-编辑 2-通过 3-驳回 4-撤销
+     * 操作类型 -1 暂存 0-提交 1-编辑 2-通过 3-驳回 4-撤销
      */
     @TableField("type")
     private Integer type;

+ 95 - 24
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Task.java

@@ -21,7 +21,7 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2023-12-06
+ * @since 2025-01-04
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -112,18 +112,6 @@ public class Task extends Model<Task> {
     @TableField("project_id")
     private Integer projectId;
 
-    /**
-     * 项目负责人id
-     */
-    @TableField(exist = false)
-    private String projectInchargerId;
-
-    /**
-     * 分组负责人id
-     */
-    @TableField(exist = false)
-    private String groupInchargerId;
-
     /**
      * 当前阶段id
      */
@@ -178,7 +166,6 @@ public class Task extends Model<Task> {
     @TableField("parent_tname")
     private String parentTname;
 
-
     /**
      * 完成日期
      */
@@ -214,6 +201,92 @@ public class Task extends Model<Task> {
     @TableField("meeting_id")
     private String meetingId;
 
+    /**
+     * 前置任务ids,多个逗号隔开
+     */
+    @TableField("ahead_tid")
+    private String aheadTid;
+
+    /**
+     * SAP项目任务编号
+     */
+    @TableField("sap_task_code")
+    private String sapTaskCode;
+
+    /**
+     * 文件审核人一id
+     */
+    @TableField("charge_one_id")
+    private String chargeOneId;
+
+    /**
+     * 文件审核人一的审核状态 0待审核 1通过 2驳回
+     */
+    @TableField("charge_one_status")
+    private Integer chargeOneStatus;
+
+    /**
+     * 文件审核人一的审核时间
+     */
+    @TableField("charge_one_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime chargeOneTime;
+
+    /**
+     * 文件审核人二id
+     */
+    @TableField("charge_two_id")
+    private String chargeTwoId;
+
+    /**
+     * 文件审核人二的审核状态 0待审核 1通过 2驳回
+     */
+    @TableField("charge_two_status")
+    private Integer chargeTwoStatus;
+
+    /**
+     * 文件审核人二的审核时间
+     */
+    @TableField("charge_two_time")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime chargeTwoTime;
+
+    /**
+     * 文件最终审核状态 0未通过 1通过
+     */
+    @TableField("final_charge_status")
+    private Integer finalChargeStatus;
+
+    /**
+     * 文件审核阶段 1审核人一 2审核人二 3都审核通过
+     */
+    @TableField("charge_stage")
+    private Integer chargeStage;
+
+    /**
+     * 文件审核驳回原因
+     */
+    @TableField("file_reject_reason")
+    private String fileRejectReason;
+
+    /**
+     * 任务预估成本
+     */
+    @TableField("plan_cost")
+    private Integer planCost;
+    /**
+     * 项目负责人id
+     */
+    @TableField(exist = false)
+    private String projectInchargerId;
+
+    /**
+     * 分组负责人id
+     */
+    @TableField(exist = false)
+    private String groupInchargerId;
     @TableField(exist = false)
     private List<TaskExecutor> executorList;
 
@@ -230,18 +303,16 @@ public class Task extends Model<Task> {
     @TableField(exist = false)
     private boolean canAddTask;
 
-    /**
-     * 前置任务ids,多个逗号隔开
-     */
-    @TableField("ahead_tid")
-    private String aheadTid;
+    @TableField(exist = false)
+    private String finalChargeStatusText;
 
-    /**
-     * SAP项目任务编号
-     */
-    @TableField("sap_task_code")
-    private String sapTaskCode;
+    //执行人下拉列表
+    @TableField(exist = false)
+    private List<User> userList;
 
+    @TableField(exist = false)
+    /**0 待审核 1通过 2驳回*/
+    private int fileChargeStatus;
 
     @Override
     protected Serializable pkVal() {

+ 12 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TaskExecutor.java

@@ -5,21 +5,20 @@ import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * <p>
  * 
  * </p>
  *
  * @author Seyason
- * @since 2023-12-13
+ * @since 2025-01-04
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -67,6 +66,13 @@ public class TaskExecutor extends Model<TaskExecutor> {
     @TableField("service_id")
     private Integer serviceId;
 
+    /**
+     * 任务预估成本开启的情况下对应的实际研发费用,可手动修改
+     */
+    @TableField("real_cost")
+    private Integer realCost;
+
+
     @TableField(exist = false)
     private String serviceName;
 
@@ -104,4 +110,5 @@ public class TaskExecutor extends Model<TaskExecutor> {
         executor.setExecutorName(task.getExecutorName());
         executor.setExecutorColor(task.getExecutorColor());
     }
+
 }

+ 17 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TaskFiles.java

@@ -1,14 +1,17 @@
 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 java.io.Serializable;
+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;
 
 /**
  * <p>
@@ -44,7 +47,9 @@ public class TaskFiles extends Model<TaskFiles> {
     private String url;
 
     @TableField("indate")
-    private LocalDateTime indate;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date indate;
 
     @TableField("size")
     private String size;
@@ -70,6 +75,13 @@ public class TaskFiles extends Model<TaskFiles> {
     @TableField("document_id")
     private Integer documentId;
 
+    @TableField(exist = false)
+    private String fileChargeStatusText;
+
+    /**0 待审核 1通过 2驳回*/
+    @TableField(exist = false)
+    private Integer fileChargeStatus;
+
 
     @Override
     protected Serializable pkVal() {

+ 21 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -17,7 +17,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-10-21
+ * @since 2025-01-04
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -297,7 +297,7 @@ public class TimeType extends Model<TimeType> {
     private Integer mainProjectState;
 
     /**
-     * 日报的审核类型, 0-项目审核人审核,1-分组负责人审核,2-先分组负责人审核再项目负责人(PM)审核;3-员工自由选择审批人 4-项目所属BU审核 5-直属审核人或部门负责人审核,6-直属或部门负责人审核->项目日报审核人审核,7-项目和部门并行审核;8-项目设置复审人;9-分组负责人审核->项目日报审核人审核
+     * 日报的审核类型, 0-项目审核人审核,1-分组负责人审核,2-先分组负责人审核再项目负责人(PM)审核;3-员工自由选择审批人 4-项目所属BU审核 5-直属审核人或部门负责人审核,6-直属或部门负责人审核->项目日报审核人审核,7-项目和部门并行审核;8-项目设置复审人;9-分组负责人审核->项目日报审核人审核;10-普通员工到项目经理,项目经理到单独审核人
      */
     @TableField("report_audit_type")
     private Integer reportAuditType;
@@ -608,26 +608,37 @@ public class TimeType extends Model<TimeType> {
     @TableField("only_show_percent")
     private Integer onlyShowPercent;
 
-    @TableField(exist = false)
-    private List<User> userList;
-    @TableField(exist = false)
-    private List<TimeAutoExclude> excludeTimeList;
-    @TableField(exist = false)
-    private Integer saasSyncContact;
-
     /**
      * 是否校验考勤数据的加班时长
      */
     @TableField("verify_card_overtime")
     private Integer verifyCardOvertime;
 
-
     /**
      * 驳回日报原因是否必填
      */
     @TableField("force_reject_reason")
     private Integer forceRejectReason;
 
+    /**
+     * 任务文件审核 0不启用 1启用
+     */
+    @TableField("task_file_charge")
+    private Integer taskFileCharge;
+
+    /**
+     * 任务预估成本功能是否开启,0-不启用,1-启用
+     */
+    @TableField("task_plan_cost")
+    private Integer taskPlanCost;
+
+
+    @TableField(exist = false)
+    private List<User> userList;
+    @TableField(exist = false)
+    private List<TimeAutoExclude> excludeTimeList;
+    @TableField(exist = false)
+    private Integer saasSyncContact;
 
     @Override
     protected Serializable pkVal() {

+ 1 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/User.java

@@ -27,6 +27,7 @@ import org.springframework.format.annotation.DateTimeFormat;
 @Data
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
 public class User extends Model<User> {
 
     private static final long serialVersionUID=1L;

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/bo/QueryTaskChargePage.java

@@ -0,0 +1,14 @@
+package com.management.platform.entity.bo;
+
+import lombok.Data;
+
+@Data
+public class QueryTaskChargePage {
+    private Integer deptId;
+    private Integer projectId;
+//    private Integer taskId;
+    private String taskName;
+    private Integer pageIndex = 1;
+    private Integer pageSize;
+    private String userId;
+}

+ 77 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/excel/SpecialContractMonthSalary.java

@@ -0,0 +1,77 @@
+package com.management.platform.entity.excel;
+
+import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Data
+public class SpecialContractMonthSalary {
+    private String projectCode;
+    private String project;
+    private String userName;
+    private String departmentName;
+    private String plate1;
+    private String plate2;
+    private String plate3;
+    private String plate4;
+    private String plate5;
+    private Double workingTime;
+    private BigDecimal salary;
+    private BigDecimal bonus;
+    private BigDecimal allowance;
+    private BigDecimal old;
+    private BigDecimal medical;
+    private BigDecimal loseJob;
+    private BigDecimal injury;
+    private BigDecimal house;
+    private BigDecimal field1;
+    private BigDecimal field2;
+    private BigDecimal field3;
+    private BigDecimal field4;
+    private BigDecimal field5;
+    private BigDecimal field6;
+    private BigDecimal field7;
+    private BigDecimal cost;
+
+    public static void main(String[] args) {
+        List<SpecialContractMonthSalary> list =new ArrayList<>();
+        SpecialContractMonthSalary s1 = new SpecialContractMonthSalary();
+        s1.setPlate1("合同主体1");s1.setSalary(new BigDecimal("4"));
+
+        SpecialContractMonthSalary s2 = new SpecialContractMonthSalary();
+        s2.setPlate1("合同主体2");s2.setSalary(new BigDecimal("3"));
+
+        SpecialContractMonthSalary s3 = new SpecialContractMonthSalary();
+        s3.setPlate1("合同主体2");s3.setSalary(new BigDecimal("2"));
+
+        SpecialContractMonthSalary s4 = new SpecialContractMonthSalary();
+        s4.setSalary(new BigDecimal("2"));
+        SpecialContractMonthSalary s5 = new SpecialContractMonthSalary();
+        s5.setSalary(new BigDecimal("1"));
+
+        list.add(s1);
+        list.add(s2);
+        list.add(s3);
+        list.add(s4);
+        list.add(s5);
+
+        Map<String, List<SpecialContractMonthSalary>> collect = list.stream()
+                .collect(Collectors.groupingBy(t-> StringUtils.isBlank(t.getPlate1())?"":t.getPlate1()));
+        System.out.println("map size== "+collect.size());
+        for (Map.Entry<String, List<SpecialContractMonthSalary>> entry : collect.entrySet()) {
+            System.out.println("entry key== "+entry.getKey());
+            for (SpecialContractMonthSalary salary : entry.getValue()) {
+                System.out.println("salary== "+salary.getSalary());
+            }
+        }
+
+        System.out.println("---------------------");
+        BigDecimal bigDecimal = new BigDecimal(12);
+        System.out.println(bigDecimal.add(null));
+    }
+}

+ 100 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/ContractBonusDetailVO.java

@@ -0,0 +1,100 @@
+package com.management.platform.entity.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class ContractBonusDetailVO {
+
+    /**
+     * 主键id
+     */
+    private Long id;
+
+    /**
+     * 公司表外键
+     */
+
+    private Integer companyId;
+
+    /**
+     * 员工id
+     */
+
+    private String userId;
+
+    /**员工姓名*/
+    private String userName;
+
+    /**
+     * 所属合同主体[user表plate1]
+     */
+
+    private String contract;
+
+    /**
+     * 项目id
+     */
+
+    private int projectId;
+    /**项目名称*/
+    private String projectName;
+
+    /**项目该月工时*/
+
+    private BigDecimal projectWorkingTime;
+
+    /**
+     * 奖金类型[中文]
+     */
+
+    private String bonusType;
+
+    /**
+     * 奖金金额
+     */
+
+    private BigDecimal bonusValue;
+
+    /**
+     * 总奖金金额
+     */
+
+    private BigDecimal totalBonusValue;
+
+    /**
+     * 年月 2024-01
+     */
+
+    private String ym;
+
+    /**
+     * 年 2024
+     */
+
+    private int year;
+
+    /**
+     * 月 08
+     */
+
+    private int month;
+
+    /**
+     * 创建人id
+     */
+
+    private String createBy;
+
+    /**
+     * 创建时间
+     */
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+}

+ 26 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/TaskChargePageVO.java

@@ -0,0 +1,26 @@
+package com.management.platform.entity.vo;
+
+import com.management.platform.entity.TaskExecutor;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class TaskChargePageVO {
+    private Integer taskId;
+    private String taskName;
+    private Integer projectId;
+    private String projectName;
+    private String createrId;
+    private Integer chargeStage;
+    private String chargeOneId;
+    private Integer chargeOneStatus;
+    private String chargeTwoId;
+    private Integer chargeTwoStatus;
+    private String finalChargeId;
+    private String finalChargeName;
+    private String executorId;
+    private List<TaskExecutor> executorList;
+    private int fileChargeStatus;
+    private Integer groupId;
+}

+ 4 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ContractBonusDetailMapper.java

@@ -3,6 +3,7 @@ package com.management.platform.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.management.platform.entity.ContractBonusDetail;
 import com.management.platform.entity.excel.ProjectContractBonusExcelHead;
+import com.management.platform.entity.vo.ContractBonusDetailVO;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
@@ -10,5 +11,7 @@ import java.util.List;
 public interface ContractBonusDetailMapper extends BaseMapper<ContractBonusDetail> {
     void batchInsert(@Param("toAddList") List<ContractBonusDetail> toAddBonusDetailList);
 
-    List<ProjectContractBonusExcelHead> getExportContractProjectBonus(Integer year, Integer companyId);
+    List<ProjectContractBonusExcelHead> getExportContractProjectBonusForSpecial(Integer year, Integer companyId);
+
+    List<ContractBonusDetailVO> getExportContractProjectBonusForCommon(@Param("companyId") Integer companyId, @Param("year") Integer year);
 }

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

@@ -3,6 +3,8 @@ package com.management.platform.mapper;
 import com.management.platform.entity.TaskFiles;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 
+import java.util.List;
+
 /**
  * <p>
  *  Mapper 接口
@@ -13,4 +15,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface TaskFilesMapper extends BaseMapper<TaskFiles> {
 
+    List<Integer> getAllTaskIds();
 }

+ 15 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/TaskMapper.java

@@ -1,11 +1,12 @@
 package com.management.platform.mapper;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.management.platform.entity.Task;
 import com.management.platform.entity.TimeTask;
+import com.management.platform.entity.bo.QueryTaskChargePage;
+import com.management.platform.entity.vo.TaskChargePageVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Update;
 
@@ -61,4 +62,17 @@ public interface TaskMapper extends BaseMapper<Task> {
     List<Map<String, Object>> getTaskByUserId(String startDate, String endDate, String userId, Integer pageStart, Integer pageSize);
 
     Integer getTaskByUserIdCount(String startDate, String endDate, String userId);
+
+    List<Task> getTaskWithProjectNameWithCharge(@Param(Constants.WRAPPER) Wrapper wrapper, Integer pageStart, Integer pageSize,Integer companyId,List<Integer> deptIds,String userId);
+
+    int selectCountWithCharge(@Param(Constants.WRAPPER) Wrapper wrapper,String userId);
+
+    List<TaskChargePageVO> getTaskChargePage(@Param("queryBO") QueryTaskChargePage queryBO, @Param("deptIds") List<Integer> deptIds);
+
+    int getTaskChargePageTotal(@Param("queryBO") QueryTaskChargePage queryBO, @Param("deptIds")List<Integer> branchDepartment);
+
+    List<Map<String,Object>> getTaskPlanRealData(Integer companyId, Integer pageStart, Integer pageSize, Integer projectId,Integer taskType,List<Integer> inchagerIds);
+
+    int getTaskPlanRealCount(Integer companyId, Integer projectId, Integer taskType, List<Integer> inchagerIds);
+
 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectApprovalService.java

@@ -24,5 +24,5 @@ public interface ProjectApprovalService extends IService<ProjectApproval> {
 
     HttpRespMsg exportData(String keyword, Integer searchField, String statuses, Integer category, Integer viewId);
 
-    HttpRespMsg check(Integer id, Integer checkType, Integer revokeType);
+    HttpRespMsg check(Integer id, Integer checkType, Integer revokeType,String rejectInfo);
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java

@@ -317,4 +317,8 @@ public interface ProjectService extends IService<Project> {
     HttpRespMsg getProjectEstimatedWorkNew(Integer pageIndex, Integer pageSize, Integer projectId, Integer type,Integer isWarn, HttpServletRequest request);
 
     HttpRespMsg exportProjectEstimatedWorkNew( Integer projectId, Integer type, Integer isWarn, HttpServletRequest request);
+
+    HttpRespMsg getProjectTaskPlanAndRealCost(Integer pageIndex, Integer pageSize, Integer projectId,  HttpServletRequest request, Integer taskType);
+
+    HttpRespMsg exportProjectTaskPlanAndRealCost(Integer projectId, HttpServletRequest request, Integer taskType);
 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/TaskFilesService.java

@@ -1,7 +1,7 @@
 package com.management.platform.service;
 
-import com.management.platform.entity.TaskFiles;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.TaskFiles;
 
 /**
  * <p>

+ 7 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/TaskService.java

@@ -1,10 +1,10 @@
 package com.management.platform.service;
 
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.management.platform.entity.Task;
 import com.management.platform.entity.TaskGroup;
+import com.management.platform.entity.bo.QueryTaskChargePage;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -54,4 +54,10 @@ public interface TaskService extends IService<Task> {
     HttpRespMsg exportUserTaskCompleteDetail(String startDate, String endDate, String userId, Integer deptId);
 
     HttpRespMsg getTaskByUserId(String startDate, String endDate, String userId, Integer pageIndex, Integer pageSize);
+
+    HttpRespMsg auditFile(Integer taskId, String projectId, Integer auditStatus, String reason, HttpServletRequest request);
+
+    HttpRespMsg getFileRejectReason(String taskId, HttpServletRequest request);
+
+    HttpRespMsg getTaskChargePage(QueryTaskChargePage queryBO, HttpServletRequest request);
 }

+ 77 - 28
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ContractBonusDetailServiceImpl.java

@@ -6,19 +6,14 @@ 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.extension.service.impl.ServiceImpl;
-import com.management.platform.entity.ContractBonusDetail;
-import com.management.platform.entity.ContractBonusSummary;
-import com.management.platform.entity.User;
-import com.management.platform.entity.WxCorpInfo;
+import com.management.platform.entity.*;
 import com.management.platform.entity.bo.BonusDataBO;
 import com.management.platform.entity.excel.ProjectContractBonusExcelHead;
-import com.management.platform.entity.vo.ImportBonusTemplateVO;
-import com.management.platform.entity.vo.ProjectBonusTimeVO;
-import com.management.platform.entity.vo.ProjectBonusTotalTimeVO;
-import com.management.platform.entity.vo.UserProjectBonusTimeVO;
+import com.management.platform.entity.vo.*;
 import com.management.platform.mapper.*;
 import com.management.platform.service.ContractBonusDetailService;
 import com.management.platform.service.WxCorpInfoService;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.MessageUtils;
 import com.management.platform.util.converter.ExcelMergeStrategy;
@@ -68,6 +63,9 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
     @Resource
     private BonusExcludeProjectMapper bonusExcludeProjectMapper;
 
+    @Resource
+    private UserCustomMapper userCustomMapper;
+
     public static Map<Integer,String> bonusTypeMap = new HashMap<>();
     static {
         bonusTypeMap.put(1,"第一季度奖");
@@ -253,7 +251,12 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
             if(CollectionUtils.isNotEmpty(emptyJNUserList)){
                 List<User> checkEmptyPlate1 = emptyJNUserList.stream().filter(t -> StringUtils.isBlank(t.getPlate1())).collect(Collectors.toList());
                 if(CollectionUtils.isNotEmpty(checkEmptyPlate1)){
-                    httpRespMsg.setError("员工:"+checkEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 没有合同主体");
+                    if(7737 == user.getCompanyId()){
+                        httpRespMsg.setError("员工:"+checkEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 没有合同主体");
+                    }else{
+                        httpRespMsg.setError("员工:"+checkEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 自定义字段没有值");
+                    }
+
                     return httpRespMsg;
                 }
                 checkEmptyPlate1.clear();
@@ -262,7 +265,11 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
             if(CollectionUtils.isNotEmpty(notEmptyJNUsers)){
                 List<User> checkNotEmptyPlate1 = notEmptyJNUsers.stream().filter(t -> StringUtils.isBlank(t.getPlate1())).collect(Collectors.toList());
                 if(CollectionUtils.isNotEmpty(checkNotEmptyPlate1)){
-                    httpRespMsg.setError("员工:"+checkNotEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 没有合同主体");
+                    if(7737 == user.getCompanyId()){
+                        httpRespMsg.setError("员工:"+checkNotEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 没有合同主体");
+                    }else{
+                        httpRespMsg.setError("员工:"+checkNotEmptyPlate1.stream().map(User::getName).collect(Collectors.toList())+" 自定义字段没有值");
+                    }
                     return httpRespMsg;
                 }
                 checkNotEmptyPlate1.clear();
@@ -394,17 +401,18 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
                 }
 
                 //该员工在该年内只能存在一条同奖金类型的数据
+                //12-25上传逻辑修改为删除所有再插入
                 contractBonusSummaryMapper.delete(new LambdaQueryWrapper<ContractBonusSummary>()
                         .eq(ContractBonusSummary::getCompanyId, user.getCompanyId())
                         .eq(ContractBonusSummary::getYear, useYear)
                         .eq(ContractBonusSummary::getBonusType,bonusType)
-                        .in(ContractBonusSummary::getUserId, checkUserTimeIdList)
+//                        .in(ContractBonusSummary::getUserId, checkUserTimeIdList)
                 );
                 contractBonusDetailMapper.delete(new LambdaQueryWrapper<ContractBonusDetail>()
                         .eq(ContractBonusDetail::getCompanyId, user.getCompanyId())
                         .eq(ContractBonusDetail::getBonusType,bonusType)
                         .eq(ContractBonusDetail::getYear,useYear)
-                        .in(ContractBonusDetail::getUserId,checkUserTimeIdList)
+//                        .in(ContractBonusDetail::getUserId,checkUserTimeIdList)
                 );
 
                 //插入数据
@@ -443,7 +451,7 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
         if(null ==user){
             httpRespMsg.setError("登录凭证有误,请联系管理员");
         }
-        List<ProjectContractBonusExcelHead> contractProjectBonusList=contractBonusDetailMapper.getExportContractProjectBonus(year,user.getCompanyId());
+        List<ProjectContractBonusExcelHead> contractProjectBonusList=contractBonusDetailMapper.getExportContractProjectBonusForSpecial(year,user.getCompanyId());
         httpRespMsg.setData(contractProjectBonusList);
         return httpRespMsg;
     }
@@ -459,7 +467,6 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
                 e.printStackTrace();
             }
         }
-        List<ProjectContractBonusExcelHead> dataList=contractBonusDetailMapper.getExportContractProjectBonus(year,user.getCompanyId());
 
         String fileName = null;
         try {
@@ -467,23 +474,65 @@ public class ContractBonusDetailServiceImpl extends ServiceImpl<ContractBonusDet
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }
-
         response.setContentType("application/vnd.ms-excel;chartset=utf-8");
         response.setHeader("Content-Disposition", "attachment;filename=" + null==fileName?"file":fileName);
-
-        try (ServletOutputStream outputStream = response.getOutputStream()){
-            EasyExcel.write(outputStream, ProjectContractBonusExcelHead.class)
-                    .excelType(ExcelTypeEnum.XLSX)
-                    .autoCloseStream(true)
-                    .registerWriteHandler(new ExcelMergeStrategy(
-                            dataList.stream().map(ProjectContractBonusExcelHead::getContract)
-                                    .collect(Collectors.toList()), 0
-                    ))
+        if(7737 == user.getCompanyId()){
+            List<ProjectContractBonusExcelHead> dataList=contractBonusDetailMapper.getExportContractProjectBonusForSpecial(year,user.getCompanyId());
+            try (ServletOutputStream outputStream = response.getOutputStream()){
+                EasyExcel.write(outputStream, ProjectContractBonusExcelHead.class)
+                        .excelType(ExcelTypeEnum.XLSX)
+                        .autoCloseStream(true)
+                        .registerWriteHandler(new ExcelMergeStrategy(
+                                dataList.stream().map(ProjectContractBonusExcelHead::getContract)
+                                        .collect(Collectors.toList()), 0
+                        ))
 //                    .registerWriteHandler(new WidthStyleStrategy())
-                    .sheet("sheet01")
-                    .doWrite(dataList);
-        } catch (IOException e) {
-            e.printStackTrace();
+                        .sheet("sheet01")
+                        .doWrite(dataList);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }else{
+            //其他公司需要用ext1的自定义配置项读取
+            List<List<String>> allDatas = new ArrayList<>();
+            List<String> headList = new ArrayList<>();
+            UserCustom ext1UserCustom = userCustomMapper.selectOne(new LambdaQueryWrapper<UserCustom>()
+                    .eq(UserCustom::getCompanyId, user.getCompanyId())
+                    .last(" limit 1 ")
+            );
+            boolean checkExt1Exist = false;
+            if(null != ext1UserCustom){
+                checkExt1Exist = true;
+                String ext1Titile = ext1UserCustom.getName();
+                headList.add(ext1Titile);
+            }
+            headList.add("员工姓名");
+            headList.add("奖金类型");
+            headList.add("项目名称");
+            headList.add("员工该项目奖金金额");
+            headList.add("年月");
+            allDatas.add(headList);
+
+            List<ContractBonusDetailVO> contractBonusDetails = contractBonusDetailMapper.getExportContractProjectBonusForCommon(user.getCompanyId(),year);
+            for (ContractBonusDetailVO detailVO : contractBonusDetails) {
+                List<String> rowData = new ArrayList<>();
+                if(checkExt1Exist){
+                    rowData.add(detailVO.getContract());
+                }
+                rowData.add(detailVO.getUserName());
+                rowData.add(detailVO.getBonusType());
+                rowData.add(detailVO.getProjectName());
+                rowData.add(detailVO.getBonusValue().setScale(4, BigDecimal.ROUND_HALF_UP).toString());
+                rowData.add(detailVO.getYm());
+                allDatas.add(rowData);
+            }
+            try {
+                ExcelUtil.writeEasyExcel(fileName,allDatas,response);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
         }
+
     }
 }

+ 366 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.entity.*;
+import com.management.platform.entity.excel.SpecialContractMonthSalary;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
 import com.management.platform.util.ExcelUtil;
@@ -696,11 +697,18 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                 headList.add(MessageUtils.message("entry.projectName"));
                 //headList.add("人员");
                 headList.add(MessageUtils.message("entry.personnel"));
-                headList.add(MessageUtils.message("entry.dept"));
-                for (UserCustom userCustom : userCustoms) {
-                    headList.add(userCustom.getName());
-                    fixedColCount++;
+//                headList.add(userCustoms.get(0).getName());
+
+                if(companyId == 7737){ //
+                    headList.add(userCustoms.get(0).getName());
+                }else{
+                    headList.add(MessageUtils.message("entry.dept"));
+                    for (UserCustom userCustom : userCustoms) {
+                        headList.add(userCustom.getName());
+                        fixedColCount++;
+                    }
                 }
+
                 //headList.add("工时(h)");
                 headList.add(MessageUtils.message("entry.workHours")+"(h)");
                 fixedColCount++;
@@ -1185,9 +1193,15 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                     rowData.add(p.projectCode);
                     rowData.add(p.project);
                     rowData.add("项目合计");
-                    //部门空出来
-                    rowData.add("");
-                    userCustoms.forEach(userCustom -> {rowData.add("");});
+
+                    if(7737 == companyId){//
+                        rowData.add("");
+                    }else{
+                        //部门空出来
+                        rowData.add("");
+                        userCustoms.forEach(userCustom -> {rowData.add("");});
+                    }
+
                     rowData.add(workTimeFormatter.format(p.workingTime));
                     rowData.add(p.salary.toPlainString());
                     rowData.add(p.bonus.toPlainString());
@@ -1221,6 +1235,109 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                     rowData.add(p.cost.toPlainString());
                     allList.add(rowData);
 
+                    if(0 == onlyTotal){
+                        if(7737 == companyId){//
+                            List<SpecialContractMonthSalary> list =new ArrayList<>();
+                            //普通
+                            List<SpecialContractMonthSalary> tmpListOne =this.transProjectTimeToMonthSalary(projectTimeList,userTime,p
+                                    ,userList,finances,allDepartments,wxCorpInfo,dingding,userCustoms,cusColList);
+                            list.addAll(tmpListOne);
+                            //无项目人员成本
+                            if (assignNoProUser != null && assignNoProUser){
+                                List<SpecialContractMonthSalary> tmpListTwo = this.transNoProjectUserToMonthSalary(noProjectUser,p
+                                        ,userList,noPUserDataList,allDepartments,wxCorpInfo,dingding,userCustoms,cusColList);
+                                list.addAll(tmpListTwo);
+                            }
+
+                            if(CollectionUtils.isNotEmpty(list)){
+                                //按照合同主体分类 plate1
+//                                DecimalFormat workTimeFormatter = new DecimalFormat("0.00");
+                                Map<String, List<SpecialContractMonthSalary>> contractMap = list.stream()
+                                        .collect(Collectors.groupingBy(t-> StringUtils.isBlank(t.getPlate1())?"":t.getPlate1()));
+                                for (Map.Entry<String, List<SpecialContractMonthSalary>> entry : contractMap.entrySet()) {
+                                    String plate1 = entry.getKey();
+                                    List<SpecialContractMonthSalary> salaryList = entry.getValue();
+                                    List<String> plate1List = new ArrayList<>();
+                                    plate1List.add("");//projectCode
+                                    plate1List.add("");//project
+                                    plate1List.add("");//$userName
+//                                    plate1List.add("");//$departmentName
+                                    plate1List.add(plate1);//plate1
+
+                                    BigDecimal workingTime = new BigDecimal(0);
+                                    BigDecimal salary = new BigDecimal(0);
+                                    BigDecimal bonus = new BigDecimal(0);
+                                    BigDecimal allowance = new BigDecimal(0);
+                                    BigDecimal old = new BigDecimal(0);
+                                    BigDecimal medical = new BigDecimal(0);
+                                    BigDecimal loseJob = new BigDecimal(0);
+                                    BigDecimal injury = new BigDecimal(0);
+                                    BigDecimal house = new BigDecimal(0);
+                                    BigDecimal field1 = new BigDecimal(0);
+                                    BigDecimal field2 = new BigDecimal(0);
+                                    BigDecimal field3 = new BigDecimal(0);
+                                    BigDecimal field4 = new BigDecimal(0);
+                                    BigDecimal field5 = new BigDecimal(0);
+                                    BigDecimal field6 = new BigDecimal(0);
+                                    BigDecimal field7 = new BigDecimal(0);
+                                    BigDecimal cost = new BigDecimal(0);
+                                    for (SpecialContractMonthSalary monthSalary : salaryList) {
+                                        workingTime = workingTime.add(BigDecimal.valueOf(null == monthSalary.getWorkingTime()?0:monthSalary.getWorkingTime()));
+                                        salary = salary.add(null == monthSalary.getSalary()?new BigDecimal(0):monthSalary.getSalary());
+                                        bonus = bonus.add(null == monthSalary.getBonus()?new BigDecimal(0):monthSalary.getBonus());
+                                        allowance= allowance.add(null == monthSalary.getAllowance()?new BigDecimal(0):monthSalary.getAllowance());
+                                        old = old.add(null == monthSalary.getOld()?new BigDecimal(0):monthSalary.getOld());
+                                        medical= medical.add(null == monthSalary.getMedical()?new BigDecimal(0):monthSalary.getMedical());
+                                        loseJob = loseJob.add(null == monthSalary.getLoseJob()?new BigDecimal(0):monthSalary.getLoseJob());
+                                        injury= injury.add(null == monthSalary.getInjury()?new BigDecimal(0):monthSalary.getInjury());
+                                        house= house.add(null == monthSalary.getHouse()?new BigDecimal(0):monthSalary.getHouse());
+                                        field1 = field1.add(null == monthSalary.getField1()?new BigDecimal(0):monthSalary.getField1());
+                                        field2 = field2.add(null == monthSalary.getField2()?new BigDecimal(0):monthSalary.getField2());
+                                        field3= field3.add(null == monthSalary.getField3()?new BigDecimal(0):monthSalary.getField3());
+                                        field4 = field4.add(null == monthSalary.getField4()?new BigDecimal(0):monthSalary.getField4());
+                                        field5  = field5.add(null == monthSalary.getField5()?new BigDecimal(0):monthSalary.getField5());
+                                        field6 = field6.add(null == monthSalary.getField6()?new BigDecimal(0):monthSalary.getField6());
+                                        field7 = field7.add(null == monthSalary.getField7()?new BigDecimal(0):monthSalary.getField7());
+                                        cost = cost.add(null == monthSalary.getCost()?new BigDecimal(0):monthSalary.getCost());
+                                    }
+                                    plate1List.add(workingTime.setScale(2 ,BigDecimal.ROUND_HALF_UP).toPlainString());//workingTime
+                                    plate1List.add(salary.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//salary
+                                    plate1List.add(bonus.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//bonus
+                                    plate1List.add(allowance.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//allowance
+                                    plate1List.add(old.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//old
+                                    plate1List.add(medical.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//medical
+                                    plate1List.add(loseJob.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//loseJob
+                                    plate1List.add(injury.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//injury
+                                    plate1List.add(house.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());//house
+                                    if (cusColList.size() > 0) {
+                                        plate1List.add(field1.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 1) {
+                                        plate1List.add(field2.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 2) {
+                                        plate1List.add(field3.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 3) {
+                                        plate1List.add(field4.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 4) {
+                                        plate1List.add(field5.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 5) {
+                                        plate1List.add(field6.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    if (cusColList.size() > 6) {
+                                        plate1List.add(field7.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    }
+                                    plate1List.add(cost.setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString());
+                                    allList.add(plate1List);
+                                }
+                            }
+
+                        }
+                    }
+
                     if(0 != onlyTotal){
                         //获取成员
                         for (Map<String, Object> membMap : projectTimeList) {
@@ -1449,6 +1566,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                                 }
                             }
                         }
+
                     }
                 }
             } else {
@@ -1630,6 +1748,247 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
         return httpRespMsg;
     }
 
+    private List<SpecialContractMonthSalary> transNoProjectUserToMonthSalary(List<Finance> noProjectUser,ProjectSumItem p
+            ,List<User> userList, List<Map> noPUserDataList
+            , List<Department> allDepartments, WxCorpInfo wxCorpInfo, CompanyDingding dingding, List<UserCustom> userCustoms
+            , List<FinanceTblcuscol> cusColList) {
+        List<SpecialContractMonthSalary> resList =new ArrayList<>();
+        for (Finance npu : noProjectUser) {
+            SpecialContractMonthSalary toAddItem = new SpecialContractMonthSalary();
+            toAddItem.setProjectCode(p.projectCode);
+            toAddItem.setProject(p.project);
+            Double workingTime = new Double(0);
+            Finance userFinance = npu;
+            Optional<Map> op = noPUserDataList.stream().filter(map->((Integer)map.get("projectId")).equals(p.projectId) && ((String)map.get("creatorId")).equals(npu.getUserId())).findFirst();
+            if (op.isPresent()) {
+                Map percentMap = op.get();
+                double percent = (double)percentMap.get("percent");
+                if (percent > 0) {
+                    //各项收入按比例计算,累加到当前项目上
+                    Finance newFinance = Finance.getByPercent(npu, percent);
+
+                    BigDecimal cost = newFinance.getTotalCost();
+                    BigDecimal salary = newFinance.getMonthCost();
+                    BigDecimal bonus = newFinance.getBonus();
+                    BigDecimal allowance = newFinance.getAllowance();
+                    BigDecimal old = newFinance.getInsuranceOld();
+                    BigDecimal medical = newFinance.getInsuranceMedical();
+                    BigDecimal loseJob = newFinance.getInsuranceLosejob();
+                    BigDecimal injury = newFinance.getInsuranceInjury();
+                    BigDecimal house = newFinance.getHouseFund();
+                    BigDecimal field1 = newFinance.getCustomField1() == null? new BigDecimal(0):newFinance.getCustomField1();
+                    BigDecimal field2 = newFinance.getCustomField2() == null? new BigDecimal(0):newFinance.getCustomField2();
+                    BigDecimal field3 = newFinance.getCustomField3() == null? new BigDecimal(0):newFinance.getCustomField3();
+                    BigDecimal field4 = newFinance.getCustomField4() == null? new BigDecimal(0):newFinance.getCustomField4();
+                    BigDecimal field5 = newFinance.getCustomField5() == null? new BigDecimal(0):newFinance.getCustomField5();
+                    BigDecimal field6 = newFinance.getCustomField6() == null? new BigDecimal(0):newFinance.getCustomField6();
+                    BigDecimal field7 = newFinance.getCustomField7() == null? new BigDecimal(0):newFinance.getCustomField7();
+                    User us = userList.stream().filter(u->u.getId().equals(npu.getUserId())).findFirst().get();
+                    if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                        toAddItem.setUserName("$userName="+(us.getCorpwxUserid()==null?"":us.getCorpwxUserid())+"$");
+                    }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                        toAddItem.setUserName("$userName="+us.getName()+"$");
+                    }else {
+                        toAddItem.setUserName(npu.getName());
+                    }
+
+                    //增加部门
+                    Optional<Department> first = allDepartments.stream().filter(dp -> dp.getDepartmentId().equals(us.getDepartmentId())).findFirst();
+                    if (first.isPresent()) {
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            toAddItem.setDepartmentName("$departmentName="+(first.get().getCorpwxDeptid()==null?"":first.get().getCorpwxDeptid())+"$");
+                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                            toAddItem.setDepartmentName("$departmentName="+(first.get().getDdDeptid()==null?"":first.get().getDdDeptid())+"$");
+                        }else {
+                            toAddItem.setDepartmentName(first.get().getDepartmentName());
+                        }
+                    } else {
+                        toAddItem.setDepartmentName("");
+                    }
+                    //自定义字段的显示
+                    for (int i = 0; i < userCustoms.size(); i++) {
+                        switch (i){
+                            case 0:
+                                toAddItem.setPlate1(us.getPlate1()==null?"":us.getPlate1());
+                                break;
+                            case 1:
+                                toAddItem.setPlate2(us.getPlate2()==null?"":us.getPlate2());
+                                break;
+                            case 2:
+                                toAddItem.setPlate3(us.getPlate3()==null?"":us.getPlate3());
+                                break;
+                            case 3:
+                                toAddItem.setPlate4(us.getPlate4()==null?"":us.getPlate4());
+                                break;
+                            case 4:
+                                toAddItem.setPlate5(us.getPlate5()==null?"":us.getPlate5());
+                                break;
+                        }
+                    }
+                    toAddItem.setWorkingTime(workingTime);
+                    toAddItem.setSalary(salary);
+                    toAddItem.setBonus(bonus);
+                    toAddItem.setAllowance(allowance);
+                    toAddItem.setOld(old);
+                    toAddItem.setMedical(medical);
+                    toAddItem.setLoseJob(loseJob);
+                    toAddItem.setInjury(injury);
+                    toAddItem.setHouse(house);
+                    //自定义字段
+                    if (cusColList.size() > 0) {
+                        toAddItem.setField1(field1);
+                    }
+                    if (cusColList.size() > 1) {
+                        toAddItem.setField2(field2);
+                    }
+                    if (cusColList.size() > 2) {
+                        toAddItem.setField3(field3);
+                    }
+                    if (cusColList.size() > 3) {
+                        toAddItem.setField4(field4);
+                    }
+                    if (cusColList.size() > 4) {
+                        toAddItem.setField5(field5);
+                    }
+                    if (cusColList.size() > 5) {
+
+                        toAddItem.setField6(field6);
+                    }
+                    if (cusColList.size() > 6) {
+                        toAddItem.setField7(field7);
+                    }
+                    toAddItem.setCost(cost);
+                    resList.add(toAddItem);
+                }
+            }
+        }
+        return resList;
+    }
+
+    private List<SpecialContractMonthSalary> transProjectTimeToMonthSalary(
+            List<Map<String, Object>> projectTimeList,HashMap<String,UserWorkTime> userTime
+    ,ProjectSumItem p,List<User> userList,List<Finance> finances
+            ,List<Department> allDepartments
+            ,WxCorpInfo wxCorpInfo,CompanyDingding dingding
+            ,List<UserCustom> userCustoms,List<FinanceTblcuscol> cusColList
+    ) {
+        List<SpecialContractMonthSalary> resList= new ArrayList<>();
+        for (Map<String, Object> membMap : projectTimeList) {
+            SpecialContractMonthSalary toAddItem = new SpecialContractMonthSalary();
+            Integer projectId = ((Long) membMap.get("projectId")).intValue();
+            //修改为按照projectId匹配
+            if (projectId.equals(p.projectId)) {
+                //匹配到项目了
+                toAddItem.setProjectCode(p.projectCode);
+                toAddItem.setProject(p.project);
+                Double workingTime = (Double) membMap.get("workingTime");
+                String creatorId = (String) membMap.get("creatorId");
+                User us = userList.stream().filter(u->u.getId().equals(creatorId)).findFirst().get();
+                Optional<Finance> first = finances.stream().filter(f -> f.getUserId().equals(creatorId)).findFirst();
+                if (!first.isPresent()) {
+                    logger.info(MessageUtils.message("report.userNull",us.getName()));
+                    continue;
+                }
+                Finance userFinance = first.get();
+
+                BigDecimal cost = userFinance.getTotalCost().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal salary = userFinance.getMonthCost().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal bonus = userFinance.getBonus().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal allowance = userFinance.getAllowance().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal old = userFinance.getInsuranceOld().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal medical = userFinance.getInsuranceMedical().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal loseJob = userFinance.getInsuranceLosejob().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal injury = userFinance.getInsuranceInjury().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal house = userFinance.getHouseFund().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field1 = userFinance.getCustomField1() == null? new BigDecimal(0):userFinance.getCustomField1().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field2 = userFinance.getCustomField2() == null? new BigDecimal(0):userFinance.getCustomField2().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field3 = userFinance.getCustomField3() == null? new BigDecimal(0):userFinance.getCustomField3().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field4 = userFinance.getCustomField4() == null? new BigDecimal(0):userFinance.getCustomField4().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field5 = userFinance.getCustomField5() == null? new BigDecimal(0):userFinance.getCustomField5().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field6 = userFinance.getCustomField6() == null? new BigDecimal(0):userFinance.getCustomField6().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                BigDecimal field7 = userFinance.getCustomField7() == null? new BigDecimal(0):userFinance.getCustomField6().multiply(new BigDecimal(workingTime)).divide(userTime.get(creatorId).workingTime, 6, BigDecimal.ROUND_HALF_UP);
+                //增加部门
+                Optional<Department> findDept = allDepartments.stream().filter(dp -> dp.getDepartmentId().equals(us.getDepartmentId())).findFirst();
+                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                    toAddItem.setUserName("$userName="+(us.getCorpwxUserid()==null?"":us.getCorpwxUserid())+"$");
+                    if (findDept.isPresent()) {
+                        toAddItem.setDepartmentName("$departmentName="+findDept.get().getDepartmentName()+"$");
+                    } else {
+                        toAddItem.setDepartmentName("");
+                    }
+                }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                    toAddItem.setUserName("$userName="+us.getName()+"$");
+                    if (findDept.isPresent()) {
+                        toAddItem.setDepartmentName("$departmentName="+findDept.get().getDepartmentName()+"$");
+                    } else {
+                        toAddItem.setDepartmentName("");
+                    }
+                }else {
+                    toAddItem.setUserName(us.getName());
+                    if (findDept.isPresent()) {
+                        toAddItem.setDepartmentName(findDept.get().getDepartmentName());
+                    } else {
+                        toAddItem.setDepartmentName("");
+                    }
+                }
+                for (int i = 0; i < userCustoms.size(); i++) {
+                    switch (i){
+                        case 0:
+                            toAddItem.setPlate1(us.getPlate1()==null?"":us.getPlate1());
+                            break;
+                        case 1:
+                            toAddItem.setPlate2(us.getPlate2()==null?"":us.getPlate2());
+                            break;
+                        case 2:
+                            toAddItem.setPlate3(us.getPlate3()==null?"":us.getPlate3());
+                            break;
+                        case 3:
+                            toAddItem.setPlate4(us.getPlate4()==null?"":us.getPlate4());
+                            break;
+                        case 4:
+                            toAddItem.setPlate5(us.getPlate5()==null?"":us.getPlate5());
+                            break;
+                    }
+                }
+                toAddItem.setWorkingTime(workingTime);
+                toAddItem.setSalary(salary);
+                toAddItem.setBonus(bonus);
+                toAddItem.setAllowance(allowance);
+                toAddItem.setOld(old);
+                toAddItem.setMedical(medical);
+                toAddItem.setLoseJob(loseJob);
+                toAddItem.setInjury(injury);
+                toAddItem.setHouse(house);
+                //自定义字段
+                if (cusColList.size() > 0) {
+                    toAddItem.setField1(field1);
+                }
+                if (cusColList.size() > 1) {
+                    toAddItem.setField2(field2);
+                }
+                if (cusColList.size() > 2) {
+                    toAddItem.setField3(field3);
+                }
+                if (cusColList.size() > 3) {
+                    toAddItem.setField4(field4);
+                }
+                if (cusColList.size() > 4) {
+                    toAddItem.setField5(field5);
+                }
+                if (cusColList.size() > 5) {
+
+                    toAddItem.setField6(field6);
+                }
+                if (cusColList.size() > 6) {
+                    toAddItem.setField7(field7);
+                }
+                toAddItem.setCost(cost);
+                resList.add(toAddItem);
+            }
+        }
+        return resList;
+    }
+
     @Override
     public HttpRespMsg getTimeCost(String yearMonth, Boolean assignNoProUser, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();

+ 0 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/PermissionServiceImpl.java


Some files were not shown because too many files changed in this diff