ソースを参照

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

Guo1B0 1 年間 前
コミット
3f2103feb2
77 ファイル変更3498 行追加395 行削除
  1. 17 17
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/api.ts
  2. 40 41
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/index.vue
  3. 0 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/type.d.ts
  4. 7 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  5. 76 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/attachment.vue
  6. 74 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/information.vue
  7. 58 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/operationRecord.vue
  8. 93 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/products.vue
  9. 84 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/relatedTasks.vue
  10. 157 19
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue
  11. 9 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/api.ts
  12. 76 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/attachment.vue
  13. 89 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/information.vue
  14. 58 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/operationRecord.vue
  15. 87 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedBusiness.vue
  16. 91 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedContacts.vue
  17. 89 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedOrders.vue
  18. 92 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedProducts.vue
  19. 84 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedTasks.vue
  20. 167 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/detail/index.vue
  21. 216 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue
  22. 77 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ExportModal.vue
  23. 44 4
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ImportModal.vue
  24. 26 12
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/api.ts
  25. 137 35
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/index.vue
  26. 23 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/type.d.ts
  27. 5 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue
  28. 10 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/styles/global.scss
  29. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts
  30. 59 18
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts
  31. 7 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts
  32. 10 5
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/TaskController.java
  33. 14 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java
  34. 8 5
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Clue.java
  35. 3 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Custom.java
  36. 7 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Order.java
  37. 4 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/TaskExecutor.java
  38. 4 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/TaskLog.java
  39. 17 6
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/vo/TasKVo.java
  40. 6 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/OrderMapper.java
  41. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/TaskService.java
  42. 207 6
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/TaskServiceImpl.java
  43. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml
  44. 2 2
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/CustomMapper.xml
  45. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/OrderMapper.xml
  46. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskExecutorMapper.xml
  47. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskLogMapper.xml
  48. 15 52
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskMapper.xml
  49. 17 6
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/GroupBudgetReviewController.java
  50. 77 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalController.java
  51. 38 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalLogController.java
  52. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  53. 6 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Project.java
  54. 55 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectApprovalLog.java
  55. 23 17
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  56. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectApprovalLogMapper.java
  57. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectApprovalLogService.java
  58. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectApprovalService.java
  59. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java
  60. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectApprovalLogServiceImpl.java
  61. 554 14
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectApprovalServiceImpl.java
  62. 31 21
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  63. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  64. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  65. 19 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectApprovalLogMapper.xml
  66. 6 17
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  67. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  68. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/立项导入模板.xlsx
  69. 13 0
      fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue
  70. 21 1
      fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue
  71. 2 0
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  72. 18 11
      fhKeeper/formulahousekeeper/timesheet/src/views/project/projectInside.vue
  73. 152 7
      fhKeeper/formulahousekeeper/timesheet/src/views/projectApproval/projectApproval.vue
  74. 20 22
      fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue
  75. 16 18
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  76. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  77. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue

+ 17 - 17
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/api.ts

@@ -1,17 +1,17 @@
 export const defalutModalForm = {
   taskName: "", //任务名称
   priority: "", //优先级
-  taskType: "0", //  任务类型
+  taskType: 2, //  任务类型
   customerId: null, //  客户id 0
   businessId: null, //商机id 1
   orderId: null, //  订单id 2
   clueId: null, //线索id 3
   contactsId: null, //联系人id
   executorId: null, //执行人id
-  repeat: "0", //是否重复
-  repeatType: "0", //重复类型
-  endType: "1", //结束类型
-  repeatEndNever: "1",
+  repeat: 0, //是否重复
+  repeatType: 0, //重复类型
+  endType: 1, //结束类型
+  repeatEndNever: 1,
   repeatEndCount: null, //重复指定次数次数后结束
   repeatEndDate: null, //重复到指定日期后结束
   repeatDesignDay: null, //自定义日期
@@ -23,17 +23,17 @@ export const defalutModalForm = {
 };
 export const PRIORITY = [
   //优先级
-  { label: "高", value: "0" },
-  { label: "中", value: "1" },
-  { label: "低", value: "2" },
+  { label: "高", value: 0 },
+  { label: "中", value: 1 },
+  { label: "低", value: 2 },
 ];
 
 export const TASK_TYPE = [
   // 弹窗任务类型
-  { label: "客户", value: "0", show: true },
-  { label: "商机", value: "1", show: true },
-  { label: "销售订单", value: "2", show: true },
-  { label: "线索", value: "3", show: false },
+  { label: "客户", value: 0, show: true },
+  { label: "商机", value: 1, show: true },
+  { label: "销售订单", value: 2, show: true },
+  { label: "线索", value: 3, show: false },
 ];
 export const TASK_TYPE_FIELD = [
   {
@@ -55,11 +55,11 @@ export const TASK_TYPE_FIELD = [
 ];
 
 export const REPEAT_TYPE = [
-  { label: "每天", value: "0" },
-  { label: "每周", value: "1" },
-  { label: "每月", value: "2" },
-  { label: "自定义周期", value: "3" },
-  { label: "自定义日期", value: "4" },
+  { label: "每天", value: 0 },
+  { label: "每周", value: 1 },
+  { label: "每月", value: 2 },
+  { label: "自定义周期", value: 3 },
+  { label: "自定义日期", value: 4 },
 ];
 export const defaultGenerateFormData = {
   list: [],

+ 40 - 41
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-dialog v-model="modalVisible" width="800px" :show-close="false" :close-on-click-modal="false" top="10vh">
+  <el-dialog v-model="props.visible" width="800px" :show-close="false" :close-on-click-modal="false" top="10vh">
     <template #header="{ titleId, titleClass }">
       <div class="flex justify-between items-center border-b pb-3">
         <h4 :id="titleId" :class="titleClass">{{ editForm ? '编辑任务' : '新增任务' }}</h4>
@@ -12,13 +12,13 @@
         </div>
       </div>
     </template>
-    <div class="h-[55vh] overflow-y-auto scroll-bar">
-      <el-form ref="formRef" :model="form" label-width="100px" :rules="rules" class="flex flex-wrap form">
-        <el-form-item label="任务名称" prop="taskName" required>
+    <div class="h-[55vh] overflow-y-auto scroll-bar mt-5">
+      <el-form ref="formRef" :model="form" label-width="7em" :rules="rules" class="flex flex-wrap form">
+        <el-form-item label="任务名称:" prop="taskName" required>
           <el-input v-model="form.taskName" type="textarea" placeholder="请输入任务名称" clearable maxlength="100"
             show-word-limit />
         </el-form-item>
-        <el-form-item prop="priority" label="优先级" required>
+        <el-form-item prop="priority" label="优先级:" required>
           <el-select v-model="form.priority" placeholder="请选择" clearable>
             <el-option v-for="item in PRIORITY " :key="item.value" :value="item.value" :label="item.label" />
           </el-select>
@@ -36,30 +36,30 @@
             </el-select>
           </template>
         </el-form-item>
-        <el-form-item label="联系人" v-if="TASK_TYPE.find(v => v.value === (form.taskType || '1'))?.show">
+        <el-form-item label="联系人:" v-if="TASK_TYPE.find(v => v.value === (form.taskType || '1'))?.show">
           <el-select v-model="form.contactsId" placeholder="请选择" clearable filterable>
             <el-option v-for="item in contactValueData" :key="item.value" :value="item.value" :label="item.label" />
           </el-select>
         </el-form-item>
-        <el-form-item label="执行人">
+        <el-form-item label="执行人:">
           <el-select v-model="form.executorId" placeholder="请选择" clearable multiple filterable>
             <el-option v-for="item in executorValueData" :key="item.value" :value="item.value" :label="item.label" />
           </el-select>
         </el-form-item>
-        <el-form-item label="重复提醒">
-          <el-switch v-model="form.repeat" active-value="1" inactive-value="0" @change="changeRepeat" />
+        <el-form-item label="重复提醒:">
+          <el-switch v-model="form.repeat" :active-value="1" :inactive-value="0" @change="changeRepeat" />
         </el-form-item>
-        <template v-if="form.repeat === '1'">
-          <el-form-item label="重复类型" label-width="7em">
+        <template v-if="form.repeat === 1">
+          <el-form-item label="重复类型:">
             <el-select v-model="form.repeatType" placeholder="请选择" @change="changeRepeatType">
               <el-option v-for="item in REPEAT_TYPE" :key="item.value" :value="item.value" :label="item.label" />
             </el-select>
           </el-form-item>
-          <template v-if="['0', '1', '2', '3'].includes(form.repeatType)">
-            <el-form-item label="每" label-width="7em" v-if="form.repeatType == 3">
+          <template v-if="[0,1,2,3].includes(form.repeatType)">
+            <el-form-item label="每:" v-if="form.repeatType == 3">
               <el-input-number v-model="form.repeatDesignSameday" controls-position="right" :min="1" />天
             </el-form-item>
-            <el-form-item label="结束" label-width="7em">
+            <el-form-item label="结束:">
               <el-radio-group v-model="form.endType" @change="changeEndType">
                 <el-radio label="1" class="w-full">永不</el-radio>
                 <el-radio label="2" class="w-full mb-3"><el-input-number v-model="form.repeatEndCount" :min="1"
@@ -74,7 +74,7 @@
               </el-radio-group>
             </el-form-item>
           </template>
-          <template v-if="['4'].includes(form.repeatType)">
+          <template v-if="[4].includes(form.repeatType)">
             <el-form-item v-for="(v, i) in customeDate" class="flex justify-between items-center customeDate">
               <div>
                 第{{ i + 1 }}次重复在
@@ -97,10 +97,10 @@
             </el-form-item>
           </template>
         </template>
-        <el-form-item label="开始时间" label-width="7em" class="w50">
+        <el-form-item label="开始时间:" class="w50">
           <el-date-picker v-model="form.startDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" />
         </el-form-item>
-        <el-form-item label="截止时间" label-width="7em" class="w50">
+        <el-form-item label="截止时间:" class="w50">
           <el-date-picker v-model="form.endDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" />
         </el-form-item>
       </el-form>
@@ -109,7 +109,7 @@
         <el-form-item label="操作记录" label-width="7em">
           <div class="w-full">
             <div v-for="item in form.taskLogs" class=" border-b-2 w-full pl-3">
-              {{ `${item.operateTime} ${item.operateUser} ${item.operateDesc}` }}
+              {{ `${item.modTime} ${item.userName} ${item.content}` }}
             </div>
           </div>
         </el-form-item>
@@ -126,7 +126,7 @@ import { get } from '@/utils/request';
 import { Delete, Plus } from "@element-plus/icons-vue"
 import { FormInstance } from 'element-plus';
 import { getFromValue } from '@/utils/tools';
-import { Props, Emits, TASK_VALUE_TYPE, REPEAT_VALUE_TYPE, } from './type';
+import { Props, Emits } from './type';
 const props = defineProps<Props>()
 const emits = defineEmits<Emits>();
 watch(() => props.saveLoading, (val) => {
@@ -137,7 +137,6 @@ watch(() => props.saveLoading, (val) => {
 })
 watch(() => props.visible, (val) => {
   formRef.value?.resetFields();
-  modalVisible.value = val
   if (val) {
     get(CUSTOMER_FORM_URL).then(res => {
       if (Array.isArray(res.data) && res.data.length > 0) {
@@ -150,17 +149,18 @@ watch(() => props.editForm, (val) => {
   if (!val) {
     //TODO 如果是新增
     form.value = { ...defalutModalForm };
-    taskTypeValueData.value = [{ label: '客户1', value: '1' }, { label: '客户2', value: '2' }];
-    contactValueData.value = [{ label: '联系人1', value: '1' }, { label: '联系人2', value: '2' }];
-    executorValueData.value = [{ label: '执行人1', value: '1' }, { label: '执行人2', value: '2' }];
+    taskTypeValueData.value = [{ label: '客户1', value: 1 }, { label: '客户2', value: 2 }];
+    contactValueData.value = [{ label: '联系人1', value: 1 }, { label: '联系人2', value: 2 }];
+    executorValueData.value = [{ label: '执行人1', value: 1 }, { label: '执行人2', value: 2 }];
     return
   }
   //TODO 如果是编辑
   form.value = { ...val };
-  customeDate.value = (form.value.repeatDesignSameday || "").split(',').filter(Boolean);
+  customeDate.value = (form.value.repeatDesignDay || "").split(',').filter(Boolean);
+  console.log("customeDate.value ", customeDate.value, form.value.repeatDesignDay);
   changeTaskType(form.value.taskType)
-  contactValueData.value = [{ label: '联系人1', value: '1' }, { label: '联系人2', value: '2' }];
-  executorValueData.value = [{ label: '执行人1', value: '1' }, { label: '执行人2', value: '2' }];
+  contactValueData.value = [{ label: '联系人1', value: 1 }, { label: '联系人2', value: 2 }];
+  executorValueData.value = [{ label: '执行人1', value: 1 }, { label: '执行人2', value: 2 }];
 
 })
 const rules = ref({
@@ -175,7 +175,6 @@ const form = ref<any>({});
 const formRef = ref<FormInstance>();
 const generateFormRef = ref<InstanceType<typeof GenerateForm>>();
 const generateFormData = ref<any>(defaultGenerateFormData);
-const modalVisible = ref(false);
 function closeVisible() {
   generateFormData.value = defaultGenerateFormData;
   emits('close')
@@ -212,28 +211,28 @@ function changeTaskType(value: TASK_VALUE_TYPE) {
     contactsId: null, //联系人id
   }
   switch (value) {
-    case "0":
+    case 0:
       taskTypeValueData.value = [];
       setTimeout(() => {
-        taskTypeValueData.value = [{ label: '客户1', value: '1' }, { label: '客户2', value: '2' }];
+        taskTypeValueData.value = [{ label: '客户1', value: 1 }, { label: '客户2', value: 2 }];
       }, 500)
       break;
-    case '1':
+    case 1:
       taskTypeValueData.value = [];
       setTimeout(() => {
-        taskTypeValueData.value = [{ label: "商机1", value: "1" }, { label: "商机2", value: "2" }]
+        taskTypeValueData.value = [{ label: "商机1", value: 1 }, { label: "商机2", value: 2 }]
       }, 500)
       break;
-    case '2':
+    case 2:
       taskTypeValueData.value = [];
       setTimeout(() => {
-        taskTypeValueData.value = [{ label: "订单1", value: "1" }, { label: "订单2", value: "2" }]
+        taskTypeValueData.value = [{ label: "订单1", value: 1 }, { label: "订单2", value: 2 }]
       }, 500)
       break;
-    case '3':
+    case 3:
       taskTypeValueData.value = [];
       setTimeout(() => {
-        taskTypeValueData.value = [{ label: "线索1", value: "1" }, { label: "线索2", value: "2" }]
+        taskTypeValueData.value = [{ label: "线索1", value: 1 }, { label: "线索2", value: 2}]
       }, 500)
       break;
     default:
@@ -250,9 +249,9 @@ function changeRepeat(value: string | number | boolean) {
   form.value = {
     ...form.value,
     repeat: value,
-    repeatType: "0", //重复类型
-    endType: "1", //结束类型
-    repeatEndNever: "1",
+    repeatType: 0, //重复类型
+    endType: 1, //结束类型
+    repeatEndNever: 1,
     repeatEndCount: null, //重复指定次数次数后结束
     repeatEndDate: null, //重复到指定日期后结束
     repeatDesignDay: null, //自定义日期
@@ -265,8 +264,8 @@ function changeRepeatType(value: REPEAT_VALUE_TYPE) {
   form.value = {
     ...form.value,
     repeatType: value, //重复类型
-    endType: "1", //结束类型
-    repeatEndNever: "1",
+    endType: 1, //结束类型
+    repeatEndNever: 1,
     repeatEndCount: null, //重复指定次数次数后结束
     repeatEndDate: null, //重复到指定日期后结束
     repeatDesignDay: null, //自定义日期
@@ -279,7 +278,7 @@ function changeEndType(value: string | number | boolean) {
   form.value = {
     ...form.value,
     endType: value, //重复类型
-    repeatEndNever: value == "1" ? "1" : null,//永不结束
+    repeatEndNever: value == 1 ? 1 : null,//永不结束
     repeatEndCount: null, //重复指定次数次数后结束
     repeatEndDate: null, //重复到指定日期后结束
     repeatDesignDay: null, //自定义日期

+ 0 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/TaskModal/type.d.ts

@@ -1,5 +1,3 @@
-type TASK_VALUE_TYPE = "0" | "1" | "2" | "3";
-type REPEAT_VALUE_TYPE = "0" | "1" | "2" | "3" | "4";
 export interface Props {
   /**
    *  弹窗是否显示

+ 7 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts

@@ -2,4 +2,10 @@ export const MOD = "/business";
 export const prefix = "/clue";
 export const GETSYSFILED = "/sys-dict/getListByCode";
 export const GETPERSONNEL = "/user/getSimpleActiveUserList";
-export const GETGENERATEFOEM = `sys-form/getListByCode${MOD}`
+export const GETGENERATEFOEM = `sys-form/getListByCode${MOD}`
+
+export const stageStatus = [
+    { id: 1, name: "赢单", progress: "100%" },
+    { id: 2, name: "输单", progress: "0%" },
+    { id: 3, name: "无效", progress: "0%" }
+]

+ 76 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/attachment.vue

@@ -0,0 +1,76 @@
+<template>
+    <div class="attachment pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">附件</div>
+            <div>
+                <el-button type="primary">上传</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="attachmenttable" border style="width: 100%;height: 200px;">
+                <el-table-column prop="fileName" label="附件名称" width="180" />
+                <el-table-column prop="fileSize" label="附件大小" width="120" />
+                <el-table-column prop="uploader" label="上传人" width="120" />
+                <el-table-column prop="uploadTime" label="上传时间" width="180" />
+                <el-table-column label="操作" width="180" fixed="right">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">下载</el-button>
+                        <el-button link type="primary" size="large">重命名</el-button>
+                        <el-button link type="danger" size="large">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const attachmenttable = ref([{
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.attachment {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

ファイルの差分が大きいため隠しています
+ 74 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/information.vue


+ 58 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/operationRecord.vue

@@ -0,0 +1,58 @@
+<template>
+    <div class="operationRecord pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">操作记录</div>
+        </div>
+        <div class="flex-1 overflow-auto pt-5">
+            <el-table :data="operationRecordtable" border style="width: 100%;height: 278px;">
+                <el-table-column prop="operatingTime" label="操作时间" width="140" />
+                <el-table-column prop="operator" label="操作人" width="120" />
+                <el-table-column prop="operationContent" label="操作内容" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const operationRecordtable = ref([{
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+},])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.operationRecord {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 93 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/products.vue

@@ -0,0 +1,93 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关产品</div>
+            <div>
+                <el-button type="primary">编辑产品</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+                <el-table-column label="序号" width="80">
+                    <template #default="scope">
+                        {{ scope.$index + 1 }}
+                    </template>
+                </el-table-column>
+                <el-table-column prop="taskName" label="产品名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="产品类别" width="130" />
+                <el-table-column prop="status" label="产品类型" width="130" />
+                <el-table-column prop="executor" label="单位" width="130" />
+                <el-table-column prop="startTime" label="标准价格" width="130" />
+                <el-table-column prop="endTime" label="库存" width="130" />
+                <el-table-column prop="endTime" label="售价" width="130" />
+                <el-table-column prop="endTime" label="数量" width="130" />
+                <el-table-column prop="endTime" label="折扣(%)" width="130" />
+                <el-table-column prop="endTime" label="合计" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 84 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/relatedTasks.vue

@@ -0,0 +1,84 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关任务</div>
+            <div>
+                <el-button type="primary">新建任务</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 100%;">
+                <el-table-column prop="taskName" label="任务名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="优先级" width="130" />
+                <el-table-column prop="status" label="状态" width="130" />
+                <el-table-column prop="executor" label="执行人" width="130" />
+                <el-table-column prop="startTime" label="开始时间" width="130" />
+                <el-table-column prop="endTime" label="截至时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 157 - 19
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue

@@ -1,30 +1,168 @@
 <template>
-    <div class="h-full flex">
-      <div class="p-5 w-80 pr-0">
-        <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
-          <div class="flex-1 p-3 overflow-y-auto">
-            
-          </div>
-          <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
-            <El-button class="w-full">重置</El-Button>
-            <El-button type="primary" class="w-full">搜索</El-Button>
-          </div>
+  <div class="h-full flex p-3 flex-col businessDetail">
+    <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center">
+      <div class="icon mr-4">
+        <el-link :underline="false" @click="backPath()">
+          <el-icon class="el-icon--right"><icon-view /></el-icon> 返回商机列表
+        </el-link>
+      </div>
+      <div class="mr-8">
+        <el-select v-model="value" placeholder="请选择" style="width: 150px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </div>
+      <div class="flex-1 flex h-full justify-end overflow-auto scroll-bar-hide cursor-pointer" @wheel="handleScroll">
+        <div :class="`${index === 0 ? 'startStep' : 'nextStep'} relative rounded-md flex items-center backGray pl-6 pr-6`"
+          v-for="(item, index) in 2" :key="index">
+          <div class="pr-3 text-nowrap">验证客户</div>
+          <div class="text-nowrap">30%</div>
+        </div>
+      </div>
+      <div class="relative rounded-md flex items-center itemPing backGray endStep item pl-6 pr-6 mr-4">
+        <el-select v-model="stageStatusVal" placeholder="结束" style="width: 100px" class="selectClas">
+          <el-option v-for="item in stageStatus" :key="item.id" :label="item.name" :value="item.id" />
+        </el-select>
+      </div>
+      <div class="bg-[#0052CC] rounded-md text itemPing pl-2 pr-2 flex items-center">
+        <el-link :underline="false">推进至阶段【验证客户】</el-link>
+      </div>
+    </div>
+    <!-- 内容 -->
+    <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar">
+      <div class="w-full h-auto flex justify-between">
+        <div class="bg-white shadow-md rounded-md" style="width: 46%;">
+          <Information />
+        </div>
+        <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+          <Attachment />
+        </div>
+      </div>
+
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md" style="width: 65%;">
+          <RelatedTasks />
+        </div>
+        <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+          <OperationRecord />
         </div>
       </div>
-      <div class="flex-1 p-5 overflow-auto">
-        <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
 
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md w-full">
+          <Products />
         </div>
       </div>
     </div>
-  </template>
+  </div>
+</template>
   
-  <script lang="ts" setup>
-  import { ref, reactive, onMounted, inject } from "vue";
-  import type { FormInstance, FormRules } from 'element-plus'
+<script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import type { FormInstance, FormRules } from 'element-plus'
+import { Edit, ArrowLeft as IconView } from '@element-plus/icons-vue'
+import { backPath } from '@/utils/tools'
+import { stageStatus } from '../api'
 
-  </script>
+import Information from '../component/information.vue'
+import Attachment from '../component/attachment.vue'
+import RelatedTasks from '../component/relatedTasks.vue';
+import OperationRecord from '../component/operationRecord.vue';
+import Products from '../component/products.vue';
+
+const value = ref('')
+const stageStatusVal = ref('')
+const options = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+  {
+    value: 'Option3',
+    label: 'Option3',
+  },
+  {
+    value: 'Option4',
+    label: 'Option4',
+  },
+  {
+    value: 'Option5',
+    label: 'Option5',
+  },
+]
+
+
+function handleScroll(event: any) { // 滚表横向滚动
+  if (event.deltaY) {
+    event.preventDefault();
+    const element = event.currentTarget;
+    element.scrollLeft += event.deltaY;
+  }
+}
+</script>
   
-  <style lang="scss" scoped>
+<style lang="scss" scoped>
+.businessDetail {
+  .icon {
+    .el-link {
+      color: #0052CC;
+    }
+  }
+
+  .text {
+    .el-link {
+      color: #fff;
+      font-size: 14px;
+    }
+  }
+
+  .backDarkBlue {
+    background-color: #0052CC;
+    color: #fff;
+  }
+
+  .backGray {
+    background-color: #F4F5F7;
+    color: #000;
+  }
+
+  .startStep {
+    clip-path: polygon(0% 0%,
+        90% 0%,
+        100% 50%,
+        90% 100%,
+        0% 100%);
+  }
+
+  .nextStep {
+    clip-path: polygon(0% 0%,
+        90% 0%,
+        100% 50%,
+        90% 100%,
+        0% 100%,
+        10% 50%);
+  }
+
+  .endStep {
+
+    clip-path: polygon(0% 0%,
+        100% 0%,
+        100% 100%,
+        0% 100%,
+        10% 50%);
+  }
+
+  .itemPing {
+    padding-top: 4px;
+    padding-bottom: 4px;
+  }
 
-  </style>
+  .selectClas >>> .el-select__wrapper {
+    background-color: none !important;
+    box-shadow: none !important;
+  }
+}
+</style>

+ 9 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/api.ts

@@ -0,0 +1,9 @@
+export const MOD = '/customer'
+export const GETSYSFILED = '/sys-dict/getListByCode'
+export const GETPERSONNEL = '/user/getSimpleActiveUserList'
+
+export const stageStatus = [
+    { id: 1, name: "赢单", progress: "100%" },
+    { id: 2, name: "输单", progress: "0%" },
+    { id: 3, name: "无效", progress: "0%" }
+]

+ 76 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/attachment.vue

@@ -0,0 +1,76 @@
+<template>
+    <div class="attachment pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">附件</div>
+            <div>
+                <el-button type="primary">上传</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="attachmenttable" border style="width: 100%;height: 200px;">
+                <el-table-column prop="fileName" label="附件名称" width="180" />
+                <el-table-column prop="fileSize" label="附件大小" width="120" />
+                <el-table-column prop="uploader" label="上传人" width="120" />
+                <el-table-column prop="uploadTime" label="上传时间" width="180" />
+                <el-table-column label="操作" width="180" fixed="right">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">下载</el-button>
+                        <el-button link type="primary" size="large">重命名</el-button>
+                        <el-button link type="danger" size="large">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const attachmenttable = ref([{
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}, {
+    fileName: '文件附件',
+    fileSize: '3.1MB',
+    uploader: '张三',
+    uploadTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.attachment {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

ファイルの差分が大きいため隠しています
+ 89 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/information.vue


+ 58 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/operationRecord.vue

@@ -0,0 +1,58 @@
+<template>
+    <div class="operationRecord pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">操作记录</div>
+        </div>
+        <div class="flex-1 overflow-auto pt-5">
+            <el-table :data="operationRecordtable" border style="width: 100%;height: 278px;">
+                <el-table-column prop="operatingTime" label="操作时间" width="140" />
+                <el-table-column prop="operator" label="操作人" width="120" />
+                <el-table-column prop="operationContent" label="操作内容" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const operationRecordtable = ref([{
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+}, {
+    operationContent: '转移线索',
+    operator: '张三',
+    operatingTime: '2024-04-01',
+},])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.operationRecord {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 87 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedBusiness.vue

@@ -0,0 +1,87 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关商机</div>
+            <div>
+                <el-button type="primary">新建商机</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+                <el-table-column prop="taskName" label="商机名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="客户名称" width="130" />
+                <el-table-column prop="status" label="负责人" width="130" />
+                <el-table-column prop="executor" label="商机金额" width="130" />
+                <el-table-column prop="startTime" label="预计成交时间" width="130" />
+                <el-table-column prop="endTime" label="商机阶段" width="130" />
+                <el-table-column prop="endTime" label="创建人" width="130" />
+                <el-table-column prop="endTime" label="创建时间" width="130" />
+                <el-table-column prop="endTime" label="修改时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 91 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedContacts.vue

@@ -0,0 +1,91 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关联系人</div>
+            <div>
+                <el-button type="primary">新建联系人</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+                <el-table-column label="序号" width="80">
+                    <template #default="scope">
+                        {{ scope.$index + 1 }}
+                    </template>
+                </el-table-column>
+                <el-table-column prop="taskName" label="联系人姓名">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="电话号码" width="130" />
+                <el-table-column prop="status" label="邮箱" width="130" />
+                <el-table-column prop="executor" label="职务" width="130" />
+                <el-table-column prop="startTime" label="性别" width="130" />
+                <el-table-column prop="endTime" label="负责人" width="130" />
+                <el-table-column prop="endTime" label="创建人" width="130" />
+                <el-table-column prop="endTime" label="创建时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 89 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedOrders.vue

@@ -0,0 +1,89 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关销售订单</div>
+            <div>
+                <el-button type="primary">新建销售订单</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+                <el-table-column prop="priority" label="订单编号" width="130" />
+                <el-table-column prop="taskName" label="订单名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="客户名称" width="130" />
+                <el-table-column prop="status" label="订单金额" width="130" />
+                <el-table-column prop="executor" label="已回款" width="130" />
+                <el-table-column prop="startTime" label="未回款" width="130" />
+                <el-table-column prop="endTime" label="订单类型" width="130" />
+                <el-table-column prop="endTime" label="下单时间" width="130" />
+                <el-table-column prop="endTime" label="负责人" width="130" />
+                <el-table-column prop="endTime" label="创建人" width="130" />
+                <el-table-column prop="endTime" label="创建时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 92 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedProducts.vue

@@ -0,0 +1,92 @@
+<template>
+    <div>
+        <el-table ref="productTableRef" :data="productTable" border :row-class-name="tableRowClassName"
+            @row-click="tableRowItem" style="width: 100%;height: 200px">
+            <el-table-column label="序号" width="80">
+                <template #default="scope">
+                    <span>{{ scope.$index + 1 }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="productName" label="产品名称" width="180">
+                <template #default="scope">
+                    <el-select v-model="productTable[scope.$index].productName" placeholder="请选择"
+                        v-if="productTableIndex == scope.$index" clearable @clear="clearTableItem(scope.$index)"
+                        @change="selectChange(scope.$index, productTable[scope.$index].productName)">
+                        <el-option v-for="item in productArrar" :key="item.id" :label="item.productName" :value="item.id" />
+                    </el-select>
+                    <span v-else>{{ productTable[scope.$index].productName }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="productType" label="产品类型" width="180"></el-table-column>
+            <el-table-column prop="unit" label="单位" width="80"></el-table-column>
+            <el-table-column prop="price" label="标准价格" width="120"></el-table-column>
+            <el-table-column prop="stock" label="库存" width="80"></el-table-column>
+            <el-table-column prop="sellingPrice" label="售价" width="180">
+                <template #default="scope">
+                    <el-input-number v-model="productTable[scope.$index].sellingPrice" class="mx-4" :min="0" :max="100000000" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <span v-else>{{ productTable[scope.$index].sellingPrice }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="quantity" label="数量" width="180">
+                <template #default="scope">
+                    <el-input-number v-model="productTable[scope.$index].quantity" class="mx-4" :min="0" :max="100000000" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <span v-else>{{ productTable[scope.$index].quantity }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="discount" label="折扣(%)" width="180">
+                <template #default="scope">
+                    <el-input-number v-model="productTable[scope.$index].discount" class="mx-4" :min="0" :max="100" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <span v-else>{{ productTable[scope.$index].discount }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="total" label="合计" width="180"></el-table-column>
+            <el-table-column label="操作" fixed="right" width="120">
+                <template #default="scope">
+                    <el-button link type="primary" size="large" @click.stop="addTableItem(scope.$index)">添加</el-button>
+                    <el-button link type="danger" size="large" v-if="productTable.length > 1"
+                        @click.stop="deteleTableItem(scope.$index)">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+    </div>
+</template>
+  
+<script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+
+const productTable: any = ref([{}])
+const productTableIndex = ref(0) // 可以编辑索引
+const productArrar = ref([
+    { id: 1, productName: '产品1', productType: '类别1', unit: '台', price: '1122', stock: '100', sellingPrice: 0, quantity: 0, discount: 0, total: '' },
+    { id: 2, productName: '产品2', productType: '类别2', unit: '台', price: '2211', stock: '300', sellingPrice: 0, quantity: 0, discount: 0, total: '' },
+])
+
+function selectChange(index: number, val: number | string) {
+    let newObj = productArrar.value.find((item: any) => item.id == val)
+    console.log(newObj)
+    productTable.value.splice(index, 1, newObj)
+}
+
+function tableRowItem(row: any) {
+    productTableIndex.value = row.index
+}
+
+function tableRowClassName({ row, rowIndex, }: { row: any, rowIndex: number }) {
+    row.index = rowIndex
+    return ''
+}
+
+function addTableItem(index: number) {
+    productTable.value.splice(index + 1, 0, {})
+}
+
+function clearTableItem(index: number) {
+    productTable.value.splice(index, 0, {})
+}
+
+function deteleTableItem(index: number) {
+    productTable.value.splice(index, 1)
+}
+</script>
+  
+<style lang="scss" scoped></style>

+ 84 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedTasks.vue

@@ -0,0 +1,84 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关任务</div>
+            <div>
+                <el-button type="primary">新建任务</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 100%;">
+                <el-table-column prop="taskName" label="任务名称">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.taskName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="priority" label="优先级" width="130" />
+                <el-table-column prop="status" label="状态" width="130" />
+                <el-table-column prop="executor" label="执行人" width="130" />
+                <el-table-column prop="startTime" label="开始时间" width="130" />
+                <el-table-column prop="endTime" label="截至时间" width="130" />
+            </el-table>
+        </div>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+
+const relatedTaskstable = ref([{
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}, {
+    taskName: '任务名称20240316-tempalsbls',
+    priority: '中',
+    status: '进行中',
+    executor: '张三',
+    startTime: '2024-04-01',
+    endTime: '2024-04-01',
+}])
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 167 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/detail/index.vue

@@ -0,0 +1,167 @@
+<template>
+  <div class="h-full flex p-3 flex-col businessDetail">
+    <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center">
+      <div class="icon mr-4">
+        <el-link :underline="false" @click="backPath()">
+          <el-icon class="el-icon--right"><icon-view /></el-icon> 返回客户列表
+        </el-link>
+      </div>
+      <div class="mr-8">
+        <el-select v-model="value" placeholder="请选择" style="width: 150px">
+          <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+        </el-select>
+      </div>
+    </div>
+    <!-- 内容 -->
+    <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar">
+      <div class="w-full h-auto flex justify-between">
+        <div class="bg-white shadow-md rounded-md" style="width: 46%;">
+          <Information />
+        </div>
+        <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+          <Attachment />
+        </div>
+      </div>
+
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md" style="width: 65%;">
+          <RelatedTasks />
+        </div>
+        <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+          <OperationRecord />
+        </div>
+      </div>
+
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md w-full">
+          <RelatedContacts />
+        </div>
+      </div>
+
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md w-full">
+          <RelatedBusiness />
+        </div>
+      </div>
+
+      <div class="w-full h-auto flex justify-between mt-2">
+        <div class="bg-white shadow-md rounded-md w-full">
+          <RelatedOrders />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+  
+<script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import type { FormInstance, FormRules } from 'element-plus'
+import { backPath } from '@/utils/tools'
+import { Edit, ArrowLeft as IconView } from '@element-plus/icons-vue'
+import { stageStatus } from '../api'
+
+import Information from '../component/information.vue'
+import Attachment from '../component/attachment.vue'
+import RelatedTasks from '../component/relatedTasks.vue';
+import OperationRecord from '../component/operationRecord.vue';
+import RelatedBusiness from '../component/relatedBusiness.vue';
+import RelatedContacts from "../component/relatedContacts.vue";
+import RelatedOrders from "../component/relatedOrders.vue"
+
+const value = ref('')
+const stageStatusVal = ref('')
+const options = [
+  {
+    value: 'Option1',
+    label: 'Option1',
+  },
+  {
+    value: 'Option2',
+    label: 'Option2',
+  },
+  {
+    value: 'Option3',
+    label: 'Option3',
+  },
+  {
+    value: 'Option4',
+    label: 'Option4',
+  },
+  {
+    value: 'Option5',
+    label: 'Option5',
+  },
+]
+
+
+function handleScroll(event: any) { // 滚表横向滚动
+  if (event.deltaY) {
+    event.preventDefault();
+    const element = event.currentTarget;
+    element.scrollLeft += event.deltaY;
+  }
+}
+</script>
+  
+<style lang="scss" scoped>
+.businessDetail {
+  .icon {
+    .el-link {
+      color: #0052CC;
+    }
+  }
+
+  .text {
+    .el-link {
+      color: #fff;
+      font-size: 14px;
+    }
+  }
+
+  .backDarkBlue {
+    background-color: #0052CC;
+    color: #fff;
+  }
+
+  .backGray {
+    background-color: #F4F5F7;
+    color: #000;
+  }
+
+  .startStep {
+    clip-path: polygon(0% 0%,
+        90% 0%,
+        100% 50%,
+        90% 100%,
+        0% 100%);
+  }
+
+  .nextStep {
+    clip-path: polygon(0% 0%,
+        90% 0%,
+        100% 50%,
+        90% 100%,
+        0% 100%,
+        10% 50%);
+  }
+
+  .endStep {
+
+    clip-path: polygon(0% 0%,
+        100% 0%,
+        100% 100%,
+        0% 100%,
+        10% 50%);
+  }
+
+  .itemPing {
+    padding-top: 4px;
+    padding-bottom: 4px;
+  }
+
+  .selectClas >>> .el-select__wrapper {
+    background-color: none !important;
+    box-shadow: none !important;
+  }
+}
+</style>

+ 216 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue

@@ -1,11 +1,225 @@
 <template>
-  <div>
-    customer
+  <div class="h-full flex">
+    <div class="p-5 w-80 pr-0">
+      <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
+        <div class="flex-1 p-3 overflow-y-auto">
+          <el-form :model="customerCriteriaForm" label-width="70px" style="max-width: 600px">
+            <el-form-item label="客户名称">
+              <el-input v-model="customerCriteriaForm.clueName" clearable placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-form-item label="客户来源">
+              <el-select v-model="customerCriteriaForm.clueSourceId" placeholder="请选择">
+                <el-option v-for="item in fixedData.CustomSources" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="电话号码">
+              <el-input v-model="customerCriteriaForm.phone" clearable placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-form-item label="邮箱">
+              <el-input v-model="customerCriteriaForm.email" clearable placeholder="请输入"></el-input>
+            </el-form-item>
+            <el-form-item label="客户行业">
+              <el-select v-model="customerCriteriaForm.customerIndustryId" placeholder="请选择">
+                <el-option v-for="item in fixedData.CustomIndustry" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="客户级别">
+              <el-select v-model="customerCriteriaForm.customerLevelId" placeholder="请选择">
+                <el-option v-for="item in fixedData.CustomLevel" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="负责人">
+              <el-select v-model="customerCriteriaForm.inchargerId" placeholder="请选择">
+                <el-option v-for="item in fixedData.Personnel" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="创建时间">
+              <el-date-picker v-model="customerCriteriaForm.startTime" type="date" placeholder="请选择" :clearable="false"
+                format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+            <el-form-item label="">
+              <el-date-picker v-model="customerCriteriaForm.endTime" type="date" placeholder="请选择" :clearable="false"
+                format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+            </el-form-item>
+          </el-form>
+        </div>
+        <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
+          <El-button class="w-full" @click="resetTable()">重置</El-Button>
+          <El-button type="primary" class="w-full" @click="searchTable()">搜索</El-Button>
+        </div>
+      </div>
+    </div>
+    <div class="flex-1 p-5 overflow-auto">
+      <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
+        <div class="flex justify-end pb-3">
+          <el-button type="primary">新建客户</el-button>
+          <el-button type="primary" @click="batchTransfer()">批量转移</el-button>
+          <el-button type="primary" @clicl="batchDelete()">批量删除</el-button>
+          <el-button type="primary">回收站</el-button>
+          <el-button type="primary">导入</el-button>
+          <el-button type="primary">导出</el-button>
+        </div>
+        <div class="flex-1 w-full overflow-hidden">
+          <el-table ref="clueTableRef" :data="clueTable" border v-loading="allLoading.clueTableLading"
+            style="width: 100%;height: 100%;">
+            <el-table-column type="selection" width="55" />
+            <el-table-column prop="clueName" label="客户名称" width="180">
+              <template #default="scope">
+                <el-button link type="primary" size="large" @click.prevent="toCustomerTableDetail(scope.row)">{{ scope.row.clueName
+                }}</el-button>
+              </template>
+            </el-table-column>
+            <el-table-column prop="clueSourceId" label="客户来源" width="180"></el-table-column>
+            <el-table-column prop="phone" label="公司电话" width="180"></el-table-column>
+            <el-table-column prop="email" label="邮箱" width="180"></el-table-column>
+            <el-table-column prop="customerIndustryId" label="客户行业" width="180"></el-table-column>
+            <el-table-column prop="customerLevelId" label="客户级别" width="180"></el-table-column>
+            <el-table-column prop="inchargerId" label="负责人" width="190"></el-table-column>
+            <el-table-column prop="createName" label="创建人" width="180"></el-table-column>
+            <el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
+            <el-table-column label="操作" fixed="right" width="200">
+              <template #default="scope">
+                <el-button link type="primary" size="large">编辑</el-button>
+                <el-button link type="primary" size="large">新建客户</el-button>
+                <el-button link type="danger" size="large" @click.prevent="deleteRow(scope.$index)">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div class="flex justify-end pt-3">
+          <el-pagination layout="total, prev, pager, next, sizes" :total="clueTotalTable" :hide-on-single-page="true" />
+        </div>
+      </div>
+    </div>
+
+    <!-- 弹窗 -->
+    
   </div>
 </template>
 
 <script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import { MOD, GETSYSFILED, GETPERSONNEL } from './api.ts'
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
+import { post, get } from "@/utils/request";
+import { useRouter, useRoute } from "vue-router";
+
+// 定义类型
+interface fixedDataInterface {
+  id: string | number,
+  companyId: string | number,
+  code: string,
+  name: string,
+  seq: string | number,
+}
+
+interface personnelInterface {
+  id: string | number,
+  name: string,
+  phone: string,
+  jobNumber: string
+}
+
+interface customerCriteriaFormType { // 线索筛选条件类型
+  clueName: string,
+  clueSourceId: string | number,
+  phone: string,
+  email: string,
+  customerIndustryId: string | number,
+  customerLevelId: string | number,
+  inchargerId: string | number,
+  startTime: string | number,
+  endTime: string | number,
+  pageIndex: string | number,
+  pageFrom: string | number
+}
+
+// 定义变量
+const route = useRoute()
+const router = useRouter()
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const customerCriteriaForm = reactive<customerCriteriaFormType>({ // 筛选条件form
+  clueName: '',
+  clueSourceId: '',
+  phone: '',
+  email: '',
+  customerIndustryId: '',
+  customerLevelId: '',
+  inchargerId: '',
+  startTime: getFirstDayOfMonth(new Date()),
+  endTime: formatDate(new Date()),
+  pageIndex: 1,
+  pageFrom: 10
+})
+const allLoading = reactive({
+  clueTableLading: false,
+})
+const fixedData = reactive({
+  CustomSources: [] as fixedDataInterface[],
+  CustomIndustry: [] as fixedDataInterface[],
+  CustomLevel: [] as fixedDataInterface[],
+  Personnel: [] as personnelInterface[]
+})
+const clueTable = ref([{ clueName: '客户名称', clueSourceId: '客户来源', id: 123456789 }]) // 线索table数据
+const clueTotalTable = ref(0) // 线索 table 数据总数
+
+// 定义方法
+function searchTable() {
+  getClueTable()
+}
+
+function resetTable() {
+  let newResetForm = resetFromValue(customerCriteriaForm, { startTime: getFirstDayOfMonth(new Date()), endTime: formatDate(new Date()), pageIndex: 1, pageFrom: 10 })
+  Object.assign(customerCriteriaForm, newResetForm)
+  getClueTable()
+}
+
+function deleteRow(_row: any) {
+  console.log('点击了删除')
+}
+
+function batchTransfer() {
+  console.log('点击了批量转移')
+}
+
+function batchDelete() {
+  console.log('批量删除')
+}
+
+function toCustomerTableDetail(_row: any) {
+  console.log('点击跳转详情')
+  router.push({path: `${MOD}/detail`, query: {id: _row.id}})
+}
+
+function getClueTable() {
+  let valueForm = getFromValue(customerCriteriaForm)
+  console.log(valueForm, '<=== valueForm')
+}
+
+async function getSystemField() {
+  const systemField = getAllListByCode(['客户来源', '客户行业', '客户级别'])
+  for (let i in systemField) {
+    const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
+    for (let key of Object.keys(fixedData)) {
+      if (systemField[i] == key) {
+        Object.assign(fixedData, { [key]: data })
+      }
+    }
+  }
+
+  const { data } = await post(GETPERSONNEL, {})
+  fixedData.Personnel = data.map((item: any) => {
+    const { id, name, phone, jobNumber } = item
+    return {
+      id, name, phone, jobNumber
+    }
+  })
+}
 
+onMounted(() => {
+  getSystemField()
+  getClueTable()
+})
 </script>
 
 <style lang="scss" scoped></style>

+ 77 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ExportModal.vue

@@ -0,0 +1,77 @@
+<template>
+  <el-dialog v-model="props.visible" width="500px" :show-close="false" :close-on-click-modal="false" top="10vh"
+    class="exportModal">
+    <template #header="{ titleId, titleClass }">
+      <div class="flex justify-between items-center border-b pb-3">
+        <h4 :id="titleId" :class="titleClass">导出任务</h4>
+        <div>
+          <el-button type="primary" :loading="['2'].includes(props.saveLoading)" @click="submit()">
+            导出
+          </el-button>
+          <el-button @click="closeVisible()">取消</el-button>
+        </div>
+      </div>
+    </template>
+    <div class="mt-5">
+      <el-form ref="formRef" :model="form" label-width="6em" class="flex flex-wrap form">
+        <el-form-item label="优先级:">
+          <el-select v-model="form.priority" placeholder="请选择" clearable>
+            <el-option v-for="item in PRIORITY " :key="item.value" :value="item.value" :label="item.label" />
+          </el-select>
+        </el-form-item>
+          <el-form-item label="执行人:">
+          <el-select v-model="form.executorId" placeholder="请选择" clearable multiple filterable>
+            <el-option v-for="item in executorValueData" :key="item.value" :value="item.value" :label="item.label" />
+          </el-select>
+        </el-form-item>
+         <el-form-item label="开始时间:" class="w50">
+          <el-date-picker v-model="form.startDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" />
+        </el-form-item>
+        <el-form-item label="截止时间:" class="w50">
+          <el-date-picker v-model="form.endDate" type="date" placeholder="选择日期" value-format="YYYY-MM-DD" />
+        </el-form-item>
+      </el-form>
+    </div>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref, watch } from 'vue';
+import { Props, Emits } from "./type"
+import { defalutExportForm,PRIORITY } from './api';
+const props = defineProps<Props>();
+const emits = defineEmits<Emits>();
+watch(() => props.visible, (val) => {
+  if (val) {
+    form.value = { ...defalutExportForm }
+    executorValueData.value = [{ label: '执行人1', value: '1' }, { label: '执行人2', value: '2' }];
+  }
+ })
+function closeVisible() {
+  emits("close")
+ }
+function submit() {
+  const { executorId, ...rest } = form.value;
+  const data = {
+    ...rest,
+    executorId: executorId.join(','),
+  }
+  emits("submit",data)
+ }
+
+const formRef = ref();
+const form = ref<any>({})
+const executorValueData = ref<any>([])
+</script>
+
+<style lang="scss">
+.el-form-item {
+  width: 100%;
+}
+
+.form {
+  .w50 {
+    @apply w-1/2;
+  }
+}
+</style>

+ 44 - 4
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ImportModal.vue

@@ -1,9 +1,49 @@
 <template>
-<div>
-</div>
+  <el-dialog v-model="props.visible" width="500px" :show-close="false" :close-on-click-modal="false" top="10vh"
+    class="importModal">
+    <template #header="{ titleId, titleClass }">
+      <div class="flex justify-between items-center border-b pb-3">
+        <h4 :id="titleId" :class="titleClass">导入任务</h4>
+        <div>
+          <el-button type="primary" :loading="['2'].includes(props.saveLoading)" @click="submit()">
+            <input type="file" v-show="false" ref="fileInputRef" @change="changeFile" accept=".xls,.xlsx" />
+            导入
+          </el-button>
+          <el-button @click="closeVisible()">取消</el-button>
+        </div>
+      </div>
+    </template>
+    <div class="text-lg p-5">
+      <div class="pb-5">1. 点击下载 <a href="###" download class="text-[#79BBFF]">任务导入模板</a></div>
+      <div>2. 填写excel文件, 任务名称与优先级必填</div>
+    </div>
+  </el-dialog>
 </template>
 <script lang="ts" setup>
+import { ref, watch } from "vue";
+import { Props, Emits } from "./type"
 
+const props = defineProps<Props>();
+const emits = defineEmits<Emits>();
+
+watch(() => props.saveLoading, (newValue) => {
+  if (!fileInputRef.value) return
+  fileInputRef.value.value = ""
+  if (newValue == "3") {
+    emits("close")
+    return
+  }
+})
+
+const fileInputRef = ref<HTMLInputElement>();
+
+function submit() {
+  fileInputRef.value?.click()
+}
+function closeVisible() {
+  emits("close")
+}
+function changeFile(e: any) {
+  emits("submit", e.target.files[0])
+}
 </script>
-<style lang="scss" scoped>
-</style>

+ 26 - 12
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/api.ts

@@ -1,6 +1,6 @@
 import { EpPropMergeType } from "element-plus/es/utils";
 
-export const MDO = "/tasks";
+export const MOD = "/tasks";
 
 type StatusType = {
   label: "全部" | "未开始" | "进行中" | "已完成" | "已超时";
@@ -13,21 +13,26 @@ type StatusType = {
 };
 export const defaultSearchForm = {
   //默认搜索条件
-  taskName: "",
-  customerName: "",
-  tel: "",
-  priority: "",
-  status: "",
-  startDate: "",
-  endDate: "",
-  page: 1,
+  taskName: "", //任务名称
+  customName: "", //客户名称
+  contactsName: "", //联系人
+  executorName: "", //执行人
+  businessName: "", //商机明恒
+  orderName: "", //销售订单
+  phone: "", //电话
+  clueName: "", //线索名称
+  priority: "",//优先级
+  status: "",//任务状态
+  startDate: "",//开始时间
+  endDate: "",//截止时间
+  pageIndex: 1,
   pageSize: 10,
 };
 export const PRIORITY = [
   //优先级
-  { label: "高", value: "0" },
-  { label: "中", value: "1" },
-  { label: "低", value: "2" },
+  { label: "高", value: 0 },
+  { label: "中", value: 1 },
+  { label: "低", value: 2 },
 ];
 export const STATUS: StatusType[] = [
   //任务状态
@@ -36,3 +41,12 @@ export const STATUS: StatusType[] = [
   { label: "已完成", value: "2", type: "success" },
   { label: "已超时", value: "3", type: "danger" },
 ];
+
+export const defalutExportForm = {
+  priority: "", //优先级
+  executorId: null, //执行人id
+  startDate: "", //开始日期
+  endDate: "", //截止日期
+};
+
+export const PAGE_LIST = `/task/pageTask`;

+ 137 - 35
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/index.vue

@@ -51,22 +51,23 @@
         </div>
       </div>
     </div>
-    <div class="flex-1 p-5 overflow-auto">
+    <div class="flex-1 p-5 overflow-auto" v-loading="loading">
       <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
         <div class="ml-auto p-3">
           <el-button type="primary" @click="createTasks()">创建任务</el-Button>
-          <el-button type="primary" @click="deleteTasks()">批量删除</el-Button>
-          <el-button type="primary" @click="importTasks()">导入</el-Button>
-          <el-button type="primary" @click="exportTasks()">导出</el-Button>
+          <el-button type="primary" :loading="btnLoading" @click="deleteTasks()">批量删除</el-Button>
+          <el-button type="primary" @click="openImportModal()">导入</el-Button>
+          <el-button type="primary" :loading="btnLoading" @click="exportTasks()">导出</el-Button>
         </div>
         <div class="flex-1">
-          <el-table :data="tableData" style="width: 100%;height: 100%;">
+          <el-table :data="tableData" style="width: 100%;height: 100%;" ref="tableRef">
+            <el-table-column type="selection" width="55" />
             <el-table-column prop="taskName" label="任务名称" header-align="center" align="center" show-overflow-tooltip
               width="200" />
             <el-table-column prop="priority" label="优先级" width="90" :sortable="true" header-align="center"
               align="center">
               <template #default="scope">
-                {{ PRIORITY[scope.row.priority]?.label }}
+                {{ PRIORITY.find(item => item.value == scope.row.priority)?.label }}
               </template>
             </el-table-column>
             <el-table-column prop="status" label="状态" width="100" header-align="center" align="center">
@@ -110,33 +111,54 @@
           </el-table>
         </div>
         <div class="ml-auto">
-          <el-pagination layout="total, prev, pager, next, sizes" :total="20" hide-on-single-page />
+          <el-pagination layout="total, prev, pager, next, sizes" :total="totalCount"
+            :current-page="searchForm.pageIndex" hide-on-single-page @size-change="sizeChage"
+            @current-change="currentChange" />
         </div>
       </div>
     </div>
-    <TaskModal :visible="taskModalVisible" :save-loading="taskLoading" :edit-form="taskForm" @close="closeModal"
+    <TaskModal :visible="taskModalVisible" :save-loading="taskLoading" :edit-form="taskForm" @close="closeTaskModal"
       @submit="submitForm" />
+    <ImportModal :visible="importVisible" :save-loading="importLoading" @close="closeImportModal"
+      @submit="importExcel" />
+    <ExportModal :visible="exportVisible" :save-loading="exportLoading" @close="closeExportModal"
+      @submit="exportExcel" />
   </div>
 </template>
 
 <script lang="ts" setup>
-import { onBeforeMount, ref } from 'vue';
+import { inject, onBeforeMount, onMounted, ref } from 'vue';
 import { useStore } from '@/store';
-import { MDO, PRIORITY, STATUS, defaultSearchForm } from './api';
-import { dayjs } from 'element-plus';
+import { MOD, PRIORITY, STATUS, defaultSearchForm, PAGE_LIST } from './api';
+import { dayjs, ElTable } from 'element-plus';
 import TaskModal from '@/components/TaskModal/index.vue';
+import ImportModal from './ImportModal.vue';
+import ExportModal from './ExportModal.vue';
+import { post, uploadFile } from '@/utils/request';
+import { getFromValue } from '@/utils/tools';
 const { getFunctionList } = useStore()
-
+const globalPopup = inject<GlobalPopup>('globalPopup')
 const pagePermission = ref<any[]>();
 const taskModalVisible = ref(false);
 const taskForm = ref<any>();
 const taskLoading = ref<saveLoadingType>("1");
-function closeModal() {
+function closeTaskModal() {
   taskModalVisible.value = false;
   taskForm.value = null;
 }
 function submitForm(data: any, isClose: boolean) {
-  console.log("提交的数据水水水水", data, isClose);
+  const { executorId } = data;
+  console.log("原有的数据", data, isClose);
+  let params = {
+    ...data
+  }
+  if (executorId) {
+    params = {
+      ...params,
+      executorId: executorId.join(',')
+    }
+  }
+  console.log("提交的数据水水水水", params, isClose);
   taskLoading.value = "2";
   setTimeout(() => {
     taskLoading.value = "3";
@@ -145,40 +167,46 @@ function submitForm(data: any, isClose: boolean) {
 
 }
 const searchForm = ref<any>();
+
+const tableRef = ref<InstanceType<typeof ElTable>>();
+const loading = ref<boolean>(false);
+const totalCount = ref<number>(0);
 const tableData = ref<any[]>([
   {
     taskName: '任务1111111111111111111111111',
     contactsTel: "15100111111",
     contactsName: "水水水水",
     customerName: '李四',
-    status: '0',
-    priority: '0',
+    status: 0,
+    priority: 0,
+    executorId: [1, 2], //执行人id
     startDate: dayjs().format('YYYY-MM-DD'),
     endDate: dayjs().format('YYYY-MM-DD'),
-    taskType: '1',
-    endType: '1',
+    repeat:1,
+    taskType: 1,
+    endType: 1,
     taskDesc: "任务秒数",
-    repeat_design_sameday: "1,4,3,4",
+    repeatType:4,
+    repeatDesignDay: "1,4,3,4",
     taskLogs: [
       {
         id: 3,
-        operateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
-        operateType: '3',
-        operateUser: '张三',
-        operateDesc: '删除任务'
+        modTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+        userName: '张三',
+        content: '删除任务'
       },
       {
         id: 2,
-        operateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+        modTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
         operateType: '2',
-        operateUser: '张三',
-        operateDesc: '修改任务'
+        userName: '张三',
+        content: '修改任务'
       }, {
         id: 1,
-        operateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+        modTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
         operateType: '1',
-        operateUser: '张三',
-        operateDesc: '创建任务'
+        userName: '张三',
+        content: '创建任务'
       }
     ]
   },
@@ -235,11 +263,39 @@ const tableData = ref<any[]>([
   }
 ])
 function search() {
-  console.log("searchForm", searchForm.value);
+  loading.value = true;
+  post(PAGE_LIST, getFromValue(searchForm.value)).then(({ data }) => {
+    loading.value = false;
+    const { total, record } = data;
+    totalCount.value = total;
+    // tableData.value = record;
+  }).catch(err => {
+    console.log("err", err);
+    loading.value = false;
+  })
 }
 function reset() {
   searchForm.value = { ...defaultSearchForm };
 }
+function sizeChage(currentSize: number): void {
+  console.log("object", currentSize);
+  searchForm.value = {
+    ...searchForm.value,
+    pageSize: currentSize,
+    pageIndex: 1
+  }
+  search()
+}
+
+function currentChange(currentPage: number): void {
+  console.log("object", currentPage);
+  searchForm.value = {
+    ...searchForm.value,
+    pageIndex: currentPage
+  }
+  search()
+}
+
 function createTasks() {
   taskModalVisible.value = true;
   taskForm.value = null;
@@ -249,14 +305,57 @@ function deleteTasks() {
   console.log(dayjs().format('YYYY-MM-DD'));
 }
 
-function importTasks() {
-  console.log("importTasks", searchForm.value);
+
+const importVisible = ref(false);
+const importLoading = ref<saveLoadingType>("1");
+function openImportModal() {
+  importVisible.value = true;
+}
+function closeImportModal() {
+  importVisible.value = false;
+}
+function importExcel(data: any) {
+  console.log("需要提交的数据,importExcel", data);
+  return
+  const formData = new FormData();
+  formData.append("file", data);
+  formData.append("projectId", "32277");
+  importLoading.value = "2";
+  uploadFile("/document/uploadDocument", formData).then(_res => {
+    globalPopup?.showSuccess("导入成功")
+    importLoading.value = "3";
+  }).catch(err => {
+    globalPopup?.showError(err)
+    importLoading.value = "4";
+  })
 }
 
+const exportVisible = ref(false);
+const exportLoading = ref<saveLoadingType>("1");
+const btnLoading = ref(false);
 function exportTasks() {
-  console.log("exportTasks", searchForm.value);
+  const data = tableRef.value?.getSelectionRows()
+  if (data.length === 0) {
+    // TODO 
+    exportVisible.value = true;
+    return
+  }
+  btnLoading.value = true;
+  setTimeout(() => {
+    btnLoading.value = false;
+  }, 2000)
+}
+function closeExportModal() {
+  exportVisible.value = false;
+}
+function exportExcel(data: any) {
+  console.log("export", data);
+  exportLoading.value = "2";
+  setTimeout(() => {
+    exportLoading.value = "3";
+    exportVisible.value = false;
+  }, 2000)
 }
-
 function editRow(row: any) {
   taskModalVisible.value = true;
   taskForm.value = row;
@@ -271,9 +370,12 @@ function deleteRow(index: any) {
   console.log("deleteRow", index);
 }
 onBeforeMount(() => {
-  pagePermission.value = getFunctionList(MDO);
+  pagePermission.value = getFunctionList(MOD);
   searchForm.value = { ...defaultSearchForm };
 })
+onMounted(() => {
+  search()
+})
 </script>
 
 <style lang="scss" scoped></style>

+ 23 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/type.d.ts

@@ -0,0 +1,23 @@
+export interface Props {
+  /**
+   *  弹窗是否显示
+   */
+  visible: boolean;
+  /**
+   *  保存按钮loading
+   */
+  saveLoading: saveLoadingType;
+}
+
+export interface Emits {
+  /**
+   *  关闭弹窗事件
+   */
+  (event: "close"): void;
+  /**
+   * 提交表单事件
+   * @param submitData 表单数据
+   * @param isClose 是否关闭弹窗
+   */
+  (event: "submit", submitData: Object): void;
+}

+ 5 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue

@@ -52,10 +52,10 @@
     <div class="flex-1 p-5 overflow-auto">
       <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
         <div class="flex justify-end pb-3">
-          <el-button type="primary">回收站</el-button>
           <el-button type="primary">新建线索</el-button>
           <el-button type="primary" @click="batchTransfer()">批量转移</el-button>
           <el-button type="primary" @clicl="batchDelete()">批量删除</el-button>
+          <el-button type="primary">回收站</el-button>
           <el-button type="primary">导入</el-button>
           <el-button type="primary">导出</el-button>
         </div>
@@ -74,7 +74,7 @@
             <el-table-column prop="email" label="邮箱" width="180"></el-table-column>
             <el-table-column prop="customerIndustryId" label="客户行业" width="180"></el-table-column>
             <el-table-column prop="customerLevelId" label="客户级别" width="180"></el-table-column>
-            <el-table-column prop="inchargerId" label="负责人" width="180"></el-table-column>
+            <el-table-column prop="inchargerId" label="负责人" width="190"></el-table-column>
             <el-table-column prop="createName" label="创建人" width="180"></el-table-column>
             <el-table-column prop="createTime" label="创建时间" width="180"></el-table-column>
             <el-table-column label="操作" fixed="right" width="200">
@@ -184,8 +184,10 @@ function getClueTable() {
   allLoading.clueTableLading = true
   let valueForm = getFromValue(filterCriteriaForm)
   post(GETTABLE, { ...valueForm }).then((res: any) => {
+    const { total, data } = res
+    clueTable.value = data
+    clueTotalTable.value = total
     allLoading.clueTableLading = false
-    console.log(res, '<==== 接口返回的数据')
   }).catch((_error) => {
     allLoading.clueTableLading = false
   })

+ 10 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/styles/global.scss

@@ -41,3 +41,13 @@ $modena: #6f4afe;
 .scroll-bar::-webkit-scrollbar-button:hover {
   background-color: #999999;
 }
+
+.scroll-bar-hide::-webkit-scrollbar {
+  width: 1px;
+  height: 0px;
+}
+
+.el-dialog__header,.el-dialog__body{
+  margin: 0;
+  padding: 0;
+}

+ 3 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts

@@ -22,3 +22,6 @@ type ListByCodeType = (
 )[];
 
 type saveLoadingType = "1" | "2" | "3" | "4"; //1是没有保存, 2是正在保存, 3是保存成功, 4是保存失败
+
+type TASK_VALUE_TYPE = 0 | 1 | 2 | 3; //0是客户, 1是商机, 2是销售订单 ,3是线索
+type REPEAT_VALUE_TYPE = 0 | 1 | 2 | 3 | 4; //0是每天, 1是每周, 2是每月, 3是自定义周期, 4是自定义日期

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

@@ -13,8 +13,8 @@ const instance = axios.create({
 instance.interceptors.request.use(
   (config: AxiosRequestConfig): any => {
     // 可在请求发送前对config进行修改,如添加请求头等
-    const { getToken } = useStore()
-    const token = getToken
+    const { getToken } = useStore();
+    const token = getToken;
     const headers = config.headers || {};
     headers["Token"] = token;
     config.headers = headers;
@@ -46,24 +46,65 @@ instance.interceptors.response.use(
 
 // 封装GET请求
 export async function get(url: string, params?: any): Promise<any> {
-  return instance
-    .get(url, { params })
-    .then((response) => response.data)
-    .catch((error) => {
-      throw error;
-    });
+  return new Promise((resolve, reject) => {
+    instance
+      .get(url, { params })
+      .then(({ data }: any) => {
+        const { code } = data;
+        if (code === "ok") {
+          resolve(data);
+          return;
+        }
+        reject(data);
+      })
+      .catch((error) => {
+        reject(error);
+      });
+  });
 }
 
 // 封装POST请求
 export async function post(url: string, data?: any): Promise<any> {
-  return instance
-    .post(url, data, {
-      headers: {
-        "Content-type": " application/x-www-form-urlencoded; charset=UTF-8",
-      },
-    })
-    .then((response) => response.data)
-    .catch((error) => {
-      throw error;
-    });
+  return new Promise((resolve, reject) => {
+    instance
+      .post(url, data, {
+        headers: {
+          "Content-type": " application/x-www-form-urlencoded; charset=UTF-8",
+        },
+      })
+      .then(({ data }: any) => {
+        const { code } = data;
+        if (code === "ok") {
+          resolve(data);
+          return;
+        }
+        reject(data);
+      })
+      .catch((error) => {
+        reject(error);
+      });
+  });
+}
+
+// 封装文件上传请求
+export async function uploadFile(url: string, data?: any): Promise<any> {
+  return new Promise((resolve, reject) => {
+    instance
+      .post(url, data, {
+        headers: {
+          "Content-type": "multipart/form-data",
+        },
+      })
+      .then(({ data }: any) => {
+        const { code } = data;
+        if (code === "ok") {
+          resolve(data);
+          return;
+        }
+        reject(data);
+      })
+      .catch((error) => {
+        reject(error);
+      });
+  });
 }

+ 7 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts

@@ -138,3 +138,10 @@ export function formatDate(date: Date) {
   const day = date.getDate().toString().padStart(2, '0');  
   return `${year}-${month}-${day}`;  
 }  
+
+/**
+ * 返回上一级
+ */
+export function backPath() {
+  window.history.go(-1);
+}

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

@@ -53,22 +53,22 @@ public class TaskController {
 
 
     @RequestMapping("addTask")
-    public HttpRespMsg addTask(@RequestBody TaskDto taskDto){
+    public HttpRespMsg addTask(TaskDto taskDto){
         return taskService.addTask(taskDto, request);
     }
 
     @RequestMapping("pageTask")
-    public HttpRespMsg pageTask(@RequestBody TaskDto taskDto){
+    public HttpRespMsg pageTask(TaskDto taskDto){
         return taskService.pageTask(taskDto,request);
     }
 
     @RequestMapping("updateTask")
-    public HttpRespMsg updateTask(@RequestBody TaskDto taskDto){
+    public HttpRespMsg updateTask( TaskDto taskDto){
         return taskService.updateTask(taskDto ,request);
     }
 
     @RequestMapping("deleteTasks")
-    public HttpRespMsg deleteTasks(@RequestBody List<Integer> taskIds){
+    public HttpRespMsg deleteTasks(List<Integer> taskIds){
         return taskService.deleteTasks(taskIds);
     }
 
@@ -78,9 +78,14 @@ public class TaskController {
     }
 
     @RequestMapping("exportData")
-    public HttpRespMsg exportData(@RequestBody TaskDto taskDto) throws Exception {
+    public HttpRespMsg exportData(TaskDto taskDto) throws Exception {
         return taskService.exportData(taskDto,request);
     }
 
+    @RequestMapping("updateTaskStatus")
+    public HttpRespMsg updateTaskStatus(TaskDto taskDto) throws Exception {
+        return taskService.updateTaskStatus(taskDto,request);
+    }
+
 }
 

+ 14 - 4
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java

@@ -18,7 +18,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-03-13
+ * @since 2024-03-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -33,9 +33,7 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
     @TableField("company_id")
     private Integer companyId;
 
-    private Integer productId;
-
-
+    @TableField(exist = false)
     private String productName;
 
     /**
@@ -55,7 +53,13 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("customer_id")
     private Integer customerId;
+    @TableField(exist = false)
     private String customerName;
+    /**
+     * 产品id
+     */
+    @TableField("product_id")
+    private Integer productId;
 
     /**
      * 商机金额
@@ -80,7 +84,9 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("create_time")
     private Date createTime;
+    @TableField(exist = false)
     private Date startTime;
+    @TableField(exist = false)
     private Date endTIme;
     /**
      * 修改时间
@@ -93,12 +99,14 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("creator_id")
     private String creatorId;
+    @TableField(exist = false)
     private String creatorName;
     /**
      * 负责人
      */
     @TableField("incharger_id")
     private String inchargerId;
+    @TableField(exist = false)
     private String inchargerName;
 
     /**
@@ -112,7 +120,9 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("is_delete")
     private Integer isDelete;
+    @TableField(exist = false)
     private Integer pageIndex;
+    @TableField(exist = false)
     private Integer pageFrom;
 
     /**

+ 8 - 5
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Clue.java

@@ -47,6 +47,7 @@ public class Clue extends Model<Clue> {
     @TableField("clue_name")
     private String clueName;
 
+    @TableField(exist = false)
     private String companyName;
 
     /**
@@ -146,13 +147,15 @@ public class Clue extends Model<Clue> {
      */
     @TableField("create_time")
     private Date createTime;
-    private String startTime;
-
-    private String endTime;
-
+    @TableField(exist = false)
+    private Date startTime;
+    @TableField(exist = false)
+    private Date endTime;
+    @TableField(exist = false)
     private Integer pageIndex;
+    @TableField(exist = false)
     private Integer pageFrom;
-
+    @TableField(exist = false)
     private List<ClueLog> clueLogList;
 
 

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-02-28
+ * @since 2024-03-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -178,8 +178,8 @@ public class Custom extends Model<Custom> {
     /**
      * 描述
      */
-    @TableField("desc")
-    private String desc;
+    @TableField("custom_desc")
+    private String customDesc;
 
     /**
      * 回收站(是否删除) 0-否 1-是

+ 7 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Order.java

@@ -17,7 +17,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-02-28
+ * @since 2024-03-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -98,6 +98,12 @@ public class Order extends Model<Order> {
     @TableField("creator_id")
     private String creatorId;
 
+    /**
+     * 回收站(是否删除) 0-否 1-是
+     */
+    @TableField("is_delete")
+    private Integer isDelete;
+
     /**
      * 自定义字段存值
      */

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

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-03-08
+ * @since 2024-03-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -27,6 +27,9 @@ public class TaskExecutor extends Model<TaskExecutor> {
     @TableId(value = "task_executor_id", type = IdType.AUTO)
     private Integer taskExecutorId;
 
+    @TableField("company_id")
+    private Integer companyId;
+
     @TableField("task_id")
     private Integer taskId;
 

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-05-04
+ * @since 2024-03-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -28,6 +28,9 @@ public class TaskLog extends Model<TaskLog> {
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @TableField("company_id")
+    private Integer companyId;
+
     /**
      * 任务id
      */

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

@@ -7,17 +7,28 @@ import lombok.experimental.Accessors;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 @EqualsAndHashCode(callSuper = true)
 @Data
 @Accessors(chain = true)
 public class TasKVo extends Task {
-    private Custom custom;
-    private BusinessOpportunity businessOpportunity;
-    private Order order;
-    private Clue clue;
-    private Contacts contacts;
-    private List<TaskExecutor> taskExecutors;
+//    private Custom custom;
+//    private BusinessOpportunity businessOpportunity;
+//    private Order order;
+//    private Clue clue;
+//    private Contacts contacts;
+//    private List<TaskExecutor> taskExecutors;
     private List<TaskLog> taskLogs;
 
+    private String customName;
+    private String businessName;
+    private String orderName;
+    private String clueName;
+    private String contactsName;
+    private String contactsPhone;
+
+    private List taskExecutors;
+
+
 }

+ 6 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/OrderMapper.java

@@ -2,6 +2,10 @@ package com.management.platform.mapper;
 
 import com.management.platform.entity.Order;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.User;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +17,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface OrderMapper extends BaseMapper<Order> {
 
+    @Select("select * from `order` WHERE company_id = #{companyId} AND is_delete = 0 ")
+    List<Order> getList(User user);
 }

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

@@ -33,4 +33,6 @@ public interface TaskService extends IService<Task> {
     HttpRespMsg importData(MultipartFile multipartFile, HttpServletRequest request);
 
     HttpRespMsg exportData(TaskDto taskDto, HttpServletRequest request) throws Exception;
+
+    HttpRespMsg updateTaskStatus(TaskDto taskDto, HttpServletRequest request);
 }

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

@@ -42,6 +42,7 @@ import java.util.*;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * <p>
@@ -85,9 +86,25 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
     @Resource
     private TaskLogService taskLogService;
 
+    @Resource
+    private CustomMapper customMapper;
+
+    @Resource
+    private BusinessOpportunityMapper businessOpportunityMapper;
+
+    @Resource
+    private OrderMapper orderMapper;
+
+    @Resource
+    private ClueMapper clueMapper;
+
+    @Resource
+    private ContactsMapper contactsMapper;
+
+
 
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg addTask(TaskDto taskDto, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
 
@@ -134,7 +151,8 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                 User selectedUser = userMapper.selectById(executorId);
                 taskExecutor.setExecutorId(executorId)
                         .setTaskId(task.getId())
-                        .setExecutorName(selectedUser.getName());
+                        .setExecutorName(selectedUser.getName())
+                        .setCompanyId(user.getCompanyId());
                 taskExecutorMapper.insert(taskExecutor);
             }
         }
@@ -146,6 +164,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
         taskLog.setUserId(userId);
         taskLog.setUserName(user.getName());
         taskLog.setModTime(LocalDateTime.now());
+        taskLog.setCompanyId(user.getCompanyId());
         taskLogMapper.insert(taskLog);
 
 
@@ -158,7 +177,33 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
         String token = String.valueOf(request.getHeader("Token"));
         User user = userMapper.selectById(token);
         taskDto.setCompanyId(user.getCompanyId());
+        List<TaskExecutor> taskExecutorList = taskExecutorMapper.selectList(new LambdaQueryWrapper<TaskExecutor>().eq(TaskExecutor::getCompanyId,user.getCompanyId()));
+        List<TaskLog> taskLogList = taskLogMapper.selectList(new LambdaQueryWrapper<TaskLog>().eq(TaskLog::getCompanyId,user.getCompanyId()));
         List<TasKVo> taskVoList =taskMapper.getPageListTask(taskDto);
+        for (TasKVo tasKVo : taskVoList) {
+            System.out.println(tasKVo.getStartDate());
+            System.out.println(tasKVo.getEndDate());
+            System.out.println(tasKVo.getCreateDate());
+            if (!taskExecutorList.isEmpty()){
+                List<TaskExecutor> collect = taskExecutorList.stream().
+                        filter(taskExecutor -> taskExecutor.getTaskId().equals(tasKVo.getId())).
+                        filter(taskExecutor -> taskExecutor.getCompanyId().equals(user.getCompanyId()))
+                        .collect(Collectors.toList());
+                if (!collect.isEmpty()){
+                    List<String> collect1 = collect.stream().map(TaskExecutor::getExecutorId).collect(Collectors.toList());
+                    ArrayList<String> taskExecutorIds = new ArrayList<>(collect1);
+                    tasKVo.setTaskExecutors(taskExecutorIds);
+                }
+            }
+            if (!taskLogList.isEmpty()){
+                List<TaskLog> collect = taskLogList.stream().filter(taskLog -> taskLog.getTaskId().equals(tasKVo.getId()))
+                        .filter(taskLog -> taskLog.getCompanyId().equals(user.getCompanyId())).collect(Collectors.toList());
+                if (!collect.isEmpty()){
+                    tasKVo.setTaskLogs(collect);
+                }
+            }
+
+        }
         if (!taskVoList.isEmpty()){
             taskVoList.forEach(tasKVo -> {
                 if (!tasKVo.getTaskExecutors().isEmpty()){
@@ -180,7 +225,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
     }
 
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg updateTask(TaskDto taskDto, HttpServletRequest request) {
         HttpRespMsg msg=new HttpRespMsg();
         String token = String.valueOf(request.getHeader("Token"));
@@ -234,7 +279,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
     }
 
     @Override
-    @Transactional
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg deleteTasks(List<Integer> taskIds) {
         HttpRespMsg msg = new HttpRespMsg();
         List<Task> taskList = taskMapper.selectList(new QueryWrapper<Task>().in("id", taskIds));
@@ -296,6 +341,12 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
             List<String> userNameList=new ArrayList<>();
             HttpRespMsg respMsg=new HttpRespMsg();
 
+            List<Contacts> contactsList = contactsMapper.selectList(new LambdaQueryWrapper<Contacts>().eq(Contacts::getIsDelete,0).eq(Contacts::getCompanyId, user.getCompanyId()));
+            List<Custom> customList=customMapper.selectList(new LambdaQueryWrapper<Custom>().eq(Custom::getCompanyId, user.getCompanyId()).eq(Custom::getIsDelete,0));
+            List<BusinessOpportunity> opportunityList = businessOpportunityMapper.selectList(new LambdaQueryWrapper<BusinessOpportunity>()
+                    .eq(BusinessOpportunity::getCompanyId, user.getCompanyId()).eq(BusinessOpportunity::getIsDelete, 0));
+            List<Order> orderList = orderMapper.getList(user);
+             List<Clue> clueList = clueMapper.selectList(new LambdaQueryWrapper<Clue>().eq(Clue::getCompanyId, user.getCompanyId()).eq(Clue::getIsDelete, 0).orderByDesc(Clue::getCreateTime));
             for (int rowIndex = 1; rowIndex <= rowNum; rowIndex++) {
                 HSSFRow row = sheet.getRow(rowIndex);
                 if (row == null) {
@@ -405,7 +456,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                         }
                     }*/
                     if(modelName.equals("executorId")){
-                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
 
                             List<String> executorNames = new ArrayList<>();
                             String stringCellValue = cell.getStringCellValue();
@@ -451,7 +502,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                         }
                     }
                     if(modelName.equals("priority")){
-                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
                             switch (cell.getStringCellValue()){
                                 case "低":task.setPriority(0);
                                     break;
@@ -481,11 +532,141 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                         }
                     }
 
+                    if(modelName.equals("contactsId")){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String contactsName = cell.getStringCellValue();
+                            List<Contacts> collect = contactsList.stream().filter(contacts -> contacts.getName().equals(contactsName)).collect(Collectors.toList());
+                            if (collect.size()>1){
+                                throw new Exception("["+contactsName+"]对应的联系人存在多个");
+                            }else if (collect.size()==1) {
+                                Contacts contacts = collect.get(0);
+                                task.setContactsId(contacts.getId());
+                            }else {
+                                throw new Exception("["+contactsName+"]对应的联系人不存在");
+                            }
+                        }
+                    }
+
+                    if(modelName.equals("customId")){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String customName = cell.getStringCellValue();
+                            List<Custom> customs = customList.stream().filter(custom -> customName.equals(custom.getCustomName())).collect(Collectors.toList());
+                            if (customs.size()>1){
+                                throw new Exception("["+customName+"]对应的客户存在多个");
+                            }else if (customs.size()==1){
+                                Custom custom = customs.get(0);
+                                if (task.getContactsId()!=null){
+                                    boolean isExist = contactsList.stream().anyMatch(contacts -> custom.getId().equals(contacts.getCustomId())&&contacts.getId().equals(task.getContactsId()));
+                                    if(!isExist){
+                                        throw new Exception("["+customName+"]对应的客户没有对应填写的联系人");
+                                    }
+                                    else {
+                                        task.setCustomId(custom.getId());
+                                    }
+                                }else {
+                                    task.setCustomId(custom.getId());
+                                }
+                            }else {
+                                throw new Exception("["+customName+"]对应的客户不存在");
+                            }
+                        }
+                    }
+                    if (modelName.equals("businessOpportunityId")){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String businessName = cell.getStringCellValue();
+                            List<BusinessOpportunity> collect = opportunityList.stream().filter(o -> businessName.equals(o.getName())).collect(Collectors.toList());
+                            if (collect.size()>1){
+                                throw new Exception("["+businessName+"]对应的商机存在多个");
+                            }else if (collect.size()==1) {
+                                BusinessOpportunity businessOpportunity = collect.get(0);
+                                if (task.getContactsId()!=null){
+                                    if (!businessOpportunity.getContactsId().equals(task.getContactsId())){
+                                        throw new Exception("["+businessName+"]对应的商机没有对应填写的联系人");
+                                    }else {
+                                        task.setBusinessOpportunityId(businessOpportunity.getId());
+                                    }
+                                }
+                            }else {
+                                throw new Exception("["+businessName+"]对应的商机不存在");
+                            }
+
+                        }
+                    }
+                    if (modelName.equals("orderId")){
+                        if(cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String orderName = cell.getStringCellValue();
+                            List<Order> collect = orderList.stream().filter(order -> orderName.equals(order.getOrderName())).collect(Collectors.toList());
+                            if (collect.size()>1){
+                                throw new Exception("["+orderName+"]对应的销售订单存在多个");
+                            }else if (collect.size()==1) {
+                                Order order=collect.get(0);
+                                if (task.getContactsId()!=null){
+                                    if (!order.getContactsId().equals(task.getContactsId())){
+                                        throw new Exception("["+orderName+"]对应的销售订单没有对应填写的联系人");
+                                    }
+                                    else {
+                                        task.setOrderId(order.getId());
+                                    }
+                                }
+                            }else {
+                                throw new Exception("["+orderName+"]对应的销售订单不存在");
+                            }
+                        }
+                    }
+
+                    if (modelName.equals("clueId")) {
+                        if (cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())) {
+                            String clueName = cell.getStringCellValue();
+                            if (task.getContactsId()!=null){
+                                throw new Exception("["+clueName+"]对应的线索不存在联系人");
+                            }
+                            List<Clue> collect = clueList.stream().filter(clue -> clueName.equals(clue.getClueName())).collect(Collectors.toList());
+                            if (!collect.isEmpty()) {
+                                Clue clue = collect.get(0);
+                                task.setClueId(clue.getId());
+                            }else {
+                                throw new Exception("["+clueName+"]对应的线索不存在");
+                            }
+                        }
+                    }
+
+                    if (modelName.equals("phone")) {
+                        if (cell!=null&&!StringUtils.isEmpty(cell.getStringCellValue())) {
+                            String contactsPhone = cell.getStringCellValue();
+                            List<Contacts> collect = contactsList.stream().filter(contacts -> contactsPhone.equals(contacts.getPhone())).collect(Collectors.toList());
+                            if (collect.size()>1){
+                                throw new Exception("联系人号码"+"["+contactsPhone+"]对应的联系人存在多个");
+                            } else if (collect.size() == 1) {
+                                Contacts contacts = collect.get(0);
+                                if (task.getContactsId()!=null){
+                                    if (!contacts.getId().equals(task.getContactsId())){
+                                        throw new Exception("联系人号码"+"["+contactsPhone+"]与联系人不绑定");
+                                    }
+                                }else {
+                                    task.setContactsId(contacts.getId());
+                                }
+                            }
+
+                        }
+                    }
                 }
                 importTaskList.add(task);
             }
 
             if(importTaskList.size()>0){
+                for (Task task : importTaskList) {
+                    if (task.getClueId()!=null &&(task.getCustomId()!=null||task.getBusinessOpportunityId()!=null||task.getOrderId()!=null||task.getContactsId()!=null)){
+                        msg.setError("任务:"+task.getTaskName()+"中线索不应与客户/商机/销售订单/联系人一同存在");
+                        return msg;
+                    }
+                    if ((task.getCustomId()!=null&&task.getBusinessOpportunityId()!=null)
+                            ||(task.getCustomId()!=null&&task.getOrderId()!=null)
+                            ||(task.getOrderId()!=null&&task.getBusinessOpportunityId()!=null)
+                            ||(task.getCustomId()!=null&&task.getBusinessOpportunityId()!=null&&task.getOrderId()!=null)){
+                        msg.setError("任务:"+task.getTaskName()+"中客户/商机/销售订单不应一同存在");
+                        return msg;
+                    }
+                }
                 if(saveOrUpdateBatch(importTaskList)){
                     ArrayList<TaskExecutor> taskExecutors = new ArrayList<>();
                     ArrayList<TaskLog> taskLogs = new ArrayList<>();
@@ -499,6 +680,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                                 taskExecutor.setTaskId(task.getId());
                                 taskExecutor.setExecutorId(userList.stream().filter(u -> u.getId().equals(userId)).collect(Collectors.toList()).get(0).getId());
                                 taskExecutor.setExecutorName(userList.stream().filter(u -> u.getId().equals(userId)).collect(Collectors.toList()).get(0).getName());
+                                taskExecutor.setCompanyId(user.getCompanyId());
                                 taskExecutors.add(taskExecutor);
                             }
                         }
@@ -508,6 +690,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                         taskLog.setModTime(LocalDateTime.now());
                         taskLog.setUserName(user.getName());
                         taskLog.setUserId(user.getId());
+                        taskLog.setCompanyId(user.getCompanyId());
                         taskLogs.add(taskLog);
                     }
                     boolean b1 = taskExecutorService.saveOrUpdateBatch(taskExecutors);
@@ -620,6 +803,24 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
         return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,fileName,dataList,path);
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public HttpRespMsg updateTaskStatus(TaskDto taskDto, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("token"));
+        HttpRespMsg msg = new HttpRespMsg();
+        if (taskDto==null||taskDto.getId()==null){
+            msg.setError("缺少用户的关键信息");
+        }
+        LambdaUpdateWrapper<Task> luw = new LambdaUpdateWrapper<>();
+        luw.set(Task::getStatus,taskDto.getStatus()).eq(Task::getId,taskDto.getStatus());
+        taskMapper.update(null,luw);
+        TaskLog taskLog = new TaskLog();
+        taskLog.setUserId(user.getId()).setTaskId(taskDto.getId()).setUserName(user.getName())
+                .setContent("修改任务状态").setModTime(LocalDateTime.now());
+        taskLogMapper.insert(taskLog);
+        return msg;
+    }
+
     public void updateTaskRepeatConfigure(Task task){
         task.setRepeatType(null).setRepeatEndNever(null).setRepeatEndCount(null)
                 .setRepeatEndDate(null).setRepeatDesignDay(null).setRepeatDesignSameday(null);

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

@@ -9,6 +9,7 @@
         <result column="name" property="name" />
         <result column="contacts_id" property="contactsId" />
         <result column="customer_id" property="customerId" />
+        <result column="product_id" property="productId" />
         <result column="amount_of_money" property="amountOfMoney" />
         <result column="expected_transaction_date" property="expectedTransactionDate" />
         <result column="stage" property="stage" />
@@ -27,7 +28,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, name, contacts_id, customer_id, amount_of_money, expected_transaction_date, stage, create_time, edit_time, creator_id, incharger_id, remark, is_delete, plate1, plate2, plate3, plate4, plate5
+        id, company_id, name, contacts_id, customer_id, product_id, amount_of_money, expected_transaction_date, stage, create_time, edit_time, creator_id, incharger_id, remark, is_delete, plate1, plate2, plate3, plate4, plate5
     </sql>
 
 </mapper>

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

@@ -30,13 +30,13 @@
         <result column="plate4" property="plate4" />
         <result column="plate5" property="plate5" />
         <result column="creator_id" property="creatorId" />
-        <result column="desc" property="desc" />
+        <result column="custom_desc" property="customDesc" />
         <result column="is_delete" property="isDelete" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, custom_name, custom_source_id, customer_industry_id, customer_level_id, email, owner_id, custom_code, tel_phone, fax, country_billing, province_billing, city_billing, street_billing, country_delivery, province_delivery, city_delivery, street_delivery, create_time, plate1, plate2, plate3, plate4, plate5, creator_id, desc, is_delete
+        id, company_id, custom_name, custom_source_id, customer_industry_id, customer_level_id, email, owner_id, custom_code, tel_phone, fax, country_billing, province_billing, city_billing, street_billing, country_delivery, province_delivery, city_delivery, street_delivery, create_time, plate1, plate2, plate3, plate4, plate5, creator_id, custom_desc, is_delete
     </sql>
 
 </mapper>

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

@@ -18,6 +18,7 @@
         <result column="incharger_id" property="inchargerId" />
         <result column="create_time" property="createTime" />
         <result column="creator_id" property="creatorId" />
+        <result column="is_delete" property="isDelete" />
         <result column="plate1" property="plate1" />
         <result column="plate2" property="plate2" />
         <result column="plate3" property="plate3" />
@@ -27,7 +28,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, order_name, order_code, custom_id, business_opportunity_id, price, contacts_id, type, received_payment, un_received_payment, incharger_id, create_time, creator_id, plate1, plate2, plate3, plate4, plate5
+        id, company_id, order_name, order_code, custom_id, business_opportunity_id, price, contacts_id, type, received_payment, un_received_payment, incharger_id, create_time, creator_id, is_delete, plate1, plate2, plate3, plate4, plate5
     </sql>
 
 </mapper>

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

@@ -5,6 +5,7 @@
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.management.platform.entity.TaskExecutor">
         <id column="task_executor_id" property="taskExecutorId" />
+        <result column="company_id" property="companyId" />
         <result column="task_id" property="taskId" />
         <result column="executor_id" property="executorId" />
         <result column="executor_name" property="executorName" />
@@ -16,7 +17,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        task_executor_id, task_id, executor_id, executor_name, executor_color, plan_hours, project_id, service_id
+        task_executor_id, company_id, task_id, executor_id, executor_name, executor_color, plan_hours, project_id, service_id
     </sql>
 
 </mapper>

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

@@ -5,6 +5,7 @@
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.management.platform.entity.TaskLog">
         <id column="id" property="id" />
+        <result column="company_id" property="companyId" />
         <result column="task_id" property="taskId" />
         <result column="mod_time" property="modTime" />
         <result column="user_id" property="userId" />
@@ -14,7 +15,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, task_id, mod_time, user_id, user_name, content
+        id, company_id, task_id, mod_time, user_id, user_name, content
     </sql>
 
 </mapper>

+ 15 - 52
fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/TaskMapper.xml

@@ -62,55 +62,29 @@
         <result column="repeat_type" property="repeatType" />
         <result column="repeat_end_never" property="repeatEndNever" />
         <result column="repeat_end_count" property="repeatEndCount" />
-        <result column="repeat_end_date" property="repeatEndDate" />
+        <result column="repeat_end_date" property="repeatEndDate" jdbcType="DATE" javaType="java.time.LocalDate" />
         <result column="repeat_design_day" property="repeatDesignDay" />
         <result column="repeat_design_sameday" property="repeatDesignSameday" />
         <result column="task_desc" property="taskDesc" />
         <result column="creater_id" property="createrId" />
         <result column="creater_name" property="createrName" />
-        <result column="create_date" property="createDate" />
-        <result column="end_date" property="endDate" />
-        <result column="start_date" property="startDate" />
-        <result column="finish_date" property="finishDate" />
+        <result column="create_date" property="createDate" jdbcType="DATE" javaType="java.time.LocalDate" />
+        <result column="end_date" property="endDate" jdbcType="DATE" javaType="java.time.LocalDate" />
+        <result column="start_date" property="startDate" jdbcType="DATE" javaType="java.time.LocalDate" />
+        <result column="finish_date" property="finishDate" jdbcType="DATE" javaType="java.time.LocalDate" />
         <result column="is_delete" property="isDelete" />
         <result column="plate1" property="plate1" />
         <result column="plate2" property="plate2" />
         <result column="plate3" property="plate3" />
         <result column="plate4" property="plate4" />
         <result column="plate5" property="plate5" />
-        <association property="custom" javaType="com.management.platform.entity.Custom">
-            <id column="custom_id" property="id"/>
-            <result column="custom_name" property="customName"/>
-        </association>
-        <association property="businessOpportunity" javaType="com.management.platform.entity.BusinessOpportunity">
-            <id column="business_opportunity_id" property="id"/>
-            <result column="businessName" property="name"/>
-        </association>
-        <association property="order" javaType="com.management.platform.entity.Order">
-            <id column="order_id" property="id"/>
-            <result column="order_name" property="orderName"/>
-        </association>
-        <association property="clue" javaType="com.management.platform.entity.Clue">
-            <id column="clue_id" property="id"/>
-            <result column="clue_name" property="clueName"/>
-        </association>
-        <association property="contacts" javaType="com.management.platform.entity.Contacts">
-            <id column="contacts_id" property="id"/>
-            <result column="contacts_name" property="name"/>
-            <result column="phone" property="phone"/>
-        </association>
-        <collection property="taskExecutors"  ofType="com.management.platform.entity.TaskExecutor">
-            <id column="task_executor_id" property="taskExecutorId"/>
-            <result column="taskExecutor_executor_id" property="executorId"/>
-            <result column="executor_name" property="executorName"/>
-        </collection>
-        <collection property="taskLogs"  ofType="com.management.platform.entity.TaskLog">
-            <id column="taskLogId" property="id"/>
-            <result column="taskLogModTime" property="modTime"/>
-            <result column="taskLogUserName" property="userName"/>
-            <result column="taskLogContent" property="content"/>
-        </collection>
 
+        <result column="custom_name" property="customName"/>
+        <result column="businessName" property="businessName"/>
+        <result column="order_name" property="orderName"/>
+        <result column="clue_name" property="clueName"/>
+        <result column="contacts_name" property="contactsName"/>
+        <result column="phone" property="contactsPhone"/>
     </resultMap>
 
     <select id="getPageListTask" resultMap="TaskVoMap">
@@ -119,17 +93,14 @@
                business_opportunity.name businessName,
               `order`.order_name ,
                clue.clue_name,
-               contacts.name contacts_name ,contacts.phone,
-               task_executor.task_executor_id, task_executor.executor_id taskExecutor_executor_id,task_executor.executor_name,
-               task_log.id taskLogId,task_log.mod_time taskLogModTime,task_log.user_name taskLogUserName,task_log.content taskLogContent
+               contacts.name contacts_name ,contacts.phone
         from task
             left join  custom on task.custom_id=custom.id
             left join  business_opportunity on task.business_opportunity_id=business_opportunity.id
             left join  `order` on task.order_id=`order`.id
             left join  clue on task.clue_id=clue.id
             left join  contacts on task.contacts_id=contacts.id
-            left join  task_executor on task.id=task_executor.task_id
-            left join  task_log on task.id=task_log.task_id
+
         <where>
             and 1=1 and task.is_delete=0
 
@@ -183,22 +154,14 @@
 
 
     <select id="getPageListTotalTask" resultMap="TaskVoMap">
-        select task.* ,
-        custom.custom_name,
-        business_opportunity.name businessName,
-        `order`.order_name ,
-        clue.clue_name,
-        contacts.name contacts_name ,contacts.phone,
-        task_executor.task_executor_id, task_executor.executor_id taskExecutor_executor_id,task_executor.executor_name,
-        task_log.id taskLogId,task_log.mod_time taskLogModTime,task_log.user_name taskLogUserName,task_log.content taskLogContent
+        select task.id
         from task
         left join  custom on task.custom_id=custom.id
         left join  business_opportunity on task.business_opportunity_id=business_opportunity.id
         left join  `order` on task.order_id=`order`.id
         left join  clue on task.clue_id=clue.id
         left join  contacts on task.contacts_id=contacts.id
-        left join  task_executor on task.id=task_executor.task_id
-        left join  task_log on task.id=task_log.task_id
+
         <where>
             and 1=1 and task.is_delete=0
 

+ 17 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/GroupBudgetReviewController.java

@@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
@@ -60,6 +61,8 @@ public class GroupBudgetReviewController {
     private WxCorpInfoService wxCorpInfoService;
     @Resource
     private TimeTypeMapper timeTypeMapper;
+    @Resource
+    private InformationMapper informationMapper;
 
     @RequestMapping("/add")
     public HttpRespMsg add(Integer groupId,Integer oldManDay,Integer changeManDay,Integer nowManDay,String remark){
@@ -108,17 +111,17 @@ public class GroupBudgetReviewController {
             httpRespMsg.setError("验证失败");
             return httpRespMsg;
         }
+        Integer groupId = groupBudgetReview.getGroupId();
+        TaskGroup taskGroup = taskGroupMapper.selectById(groupId);
+        Project project = projectMapper.selectById(taskGroup.getProjectId());
         if(checkType==1){
             //审核通过计算到任务分组的项目人天
-            Integer groupId = groupBudgetReview.getGroupId();
-            TaskGroup taskGroup = taskGroupMapper.selectById(groupId);
             Double manDay = taskGroup.getManDay();
             BigDecimal bigDecimal = new BigDecimal(manDay==null?0:manDay);
             bigDecimal=bigDecimal.add(new BigDecimal(groupBudgetReview.getChangeManDay()==null?0:groupBudgetReview.getChangeManDay()));
             taskGroup.setManDay(bigDecimal.doubleValue());
             taskGroupMapper.updateById(taskGroup);
             //项目人天按照分组工时增加
-            Project project = projectMapper.selectById(taskGroup.getProjectId());
             double day = project.getManDay();
             day=day+groupBudgetReview.getChangeManDay();
             project.setManDay(day);
@@ -130,17 +133,25 @@ public class GroupBudgetReviewController {
             sb.append("$userName="+checker.getCorpwxUserid()+"$");
             if(checkType==1){
                 sb.append("通过了");
-                sb.append("您预估工时修改申请");
+                sb.append("您项目["+project.getProjectName()+"]下分组["+taskGroup.getName()+"]的预估工时修改申请");
             }else{
                 sb.append("驳回了");
-                sb.append("您预估工时修改申请");
+                sb.append("您项目["+project.getProjectName()+"]下分组["+taskGroup.getName()+"]的预估工时修改申请");
                 sb.append(",驳回理由:"+groupBudgetReview.getRejectReason());
             }
             String corpwxUserid = user.getCorpwxUserid();
             if (corpwxUserid != null) {
                 WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id",checker.getCompanyId()));
-                wxCorpInfoService.sendWXCorpMsg(info, corpwxUserid,sb.toString(), "budgetReview", WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_DENY);
+                wxCorpInfoService.sendWXCorpMsg(info, corpwxUserid,sb.toString(), "projectInside/"+taskGroup.getProjectId()+"@adjustBudget-"+taskGroup.getId(), WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_DENY);
             }
+            //系统消息
+            Information information=new Information();
+            information.setType(10);
+            information.setUserId(groupBudgetReview.getCreatorId());
+            information.setTime(LocalDateTime.now());
+            information.setContent(String.valueOf(groupBudgetReview.getProjectId())+"@adjustBudget-"+groupBudgetReview.getGroupId());
+            information.setMsg("您在项目["+project.getProjectName()+"]下分组["+taskGroup.getName()+"]的预估工时修改申请被"+(checkType==1?"通过了":("驳回了,驳回理由")+groupBudgetReview.getRejectReason()));
+            informationMapper.insert(information);
         }
         return  httpRespMsg;
     }

+ 77 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalController.java

@@ -15,10 +15,12 @@ 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;
 import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -64,6 +66,10 @@ public class ProjectApprovalController {
     private ParticipationService participationService;
     @Resource
     private SysFunctionService sysFunctionService;
+    @Resource
+    private ProjectApprovalLogService projectApprovalLogService;
+    @Resource
+    private ReportService reportService;
 
     /**
      * 分页获取项目列表
@@ -117,7 +123,9 @@ public class ProjectApprovalController {
                     p.setUserName(first.get().getName());
                 }
             });
-            participationApprovalService.saveBatch(participationApprovalList);
+            if(participationApprovalList.size()>0){
+                participationApprovalService.saveBatch(participationApprovalList);
+            }
         }
         if(projectApproval.getProjectApprovalBasecostList()!=null&&projectApproval.getProjectApprovalBasecostList().size()>0){
             List<ProjectApprovalBasecost> projectApprovalBasecostList = projectApproval.getProjectApprovalBasecostList();
@@ -135,7 +143,9 @@ public class ProjectApprovalController {
                     p.setAuditorName(first.get().getName());
                 }
             });
-            projectApprovalAuditorService.saveBatch(projectApprovalAuditorList);
+            if(projectApprovalAuditorList.size()>0){
+                projectApprovalAuditorService.saveBatch(projectApprovalAuditorList);
+            }
         }
         if(projectApproval.getProjectApprovalCheckList()!=null&&projectApproval.getProjectApprovalCheckList().size()>0){
             List<ProjectApprovalCheck> projectApprovalCheckList = projectApproval.getProjectApprovalCheckList();
@@ -146,8 +156,22 @@ public class ProjectApprovalController {
             for (int i = 0; i < projectApprovalCheckList.size(); i++) {
                 projectApprovalCheckList.get(i).setSeq(i+1);
             }
-            projectApprovalCheckService.saveOrUpdateBatch(projectApprovalCheckList);
+            if(projectApprovalCheckList.size()>0){
+                projectApprovalCheckService.saveOrUpdateBatch(projectApprovalCheckList);
+            }
+        }
+        //新增操作记录数据
+        ProjectApprovalLog projectApprovalLog=new ProjectApprovalLog();
+        projectApprovalLog.setCreateTime(LocalDateTime.now());
+        projectApprovalLog.setProjectApprovalId(projectApproval.getId());
+        if(projectApproval.getId()==null){
+            //提交
+            projectApprovalLog.setType(0);
+        }else {
+            //编辑
+            projectApprovalLog.setType(1);
         }
+        projectApprovalLogService.save(projectApprovalLog);
         return msg;
     }
 
@@ -203,12 +227,15 @@ public class ProjectApprovalController {
         Optional<ProjectApprovalCheck> first = projectApprovalChecks.stream().filter(p -> p.getUserId().equals(user.getId())&&p.getStatus()==0).findFirst();
 //        boolean hasAllPriviledge = sysFunctionService.hasPriviledge(user.getRoleId(), "管理全部立项申请");
 //        boolean hasCheckPriviledge = sysFunctionService.hasPriviledge(user.getRoleId(), "审核立项申请");
-        if(projectApprovalChecks.size()>0){
-            if(!first.isPresent()){
-                msg.setError("审核人验证失败,请按照审核人顺序审核");
-                return msg;
+        if(checkType!=3){
+            if(projectApprovalChecks.size()>0){
+                if(!first.isPresent()){
+                    msg.setError("审核人验证失败,请按照审核人顺序审核");
+                    return msg;
+                }
             }
         }
+        ProjectApprovalLog projectApprovalLog=new ProjectApprovalLog();
         switch (checkType){
             /*审核通过*/
             case 1:
@@ -257,18 +284,61 @@ public class ProjectApprovalController {
                     projectBasecostService.saveBatch(projectBasecostList);
                     participationService.saveBatch(participationList);
                     projectAuditorService.saveBatch(projectAuditorList);
+                    //新增操作记录数据
+                    projectApprovalLog.setCreateTime(LocalDateTime.now());
+                    projectApprovalLog.setProjectApprovalId(projectApproval.getId());
+                    //通过
+                    projectApprovalLog.setType(2);
+                    projectApprovalLogService.save(projectApprovalLog);
                 }
                 break;
             /*驳回*/
             case 2:
                 projectApproval.setStatus(2);
                 projectApprovalService.updateById(projectApproval);
+                //新增操作记录数据
+                projectApprovalLog.setCreateTime(LocalDateTime.now());
+                projectApprovalLog.setProjectApprovalId(projectApproval.getId());
+                //驳回
+                projectApprovalLog.setType(3);
+                projectApprovalLogService.save(projectApprovalLog);
                 break;
             /*撤销*/
             case 3:
+                Project project = projectService.getOne(new LambdaQueryWrapper<Project>().eq(Project::getApproveId,projectApproval.getId()));
+                if(project!=null){
+                    int count = reportService.count(new LambdaQueryWrapper<Report>().eq(Report::getProjectId, project.getId()));
+                    if(count>0){
+                        msg.setError("已存在填报数据无法撤销立项");
+                        return msg;
+                    }
+                    projectAuditorService.remove(new LambdaQueryWrapper<ProjectAuditor>().eq(ProjectAuditor::getProjectId,project.getId()));
+                    projectBasecostService.remove(new LambdaQueryWrapper<ProjectBasecost>().eq(ProjectBasecost::getProjectId,project.getId()));
+                    participationService.remove(new LambdaQueryWrapper<Participation>().eq(Participation::getProjectId,project.getId()));
+                    project.deleteById(project.getId());
+                }
+                projectApproval.setStatus(3);
+                projectApprovalService.updateById(projectApproval);
+                //新增操作记录数据
+                projectApprovalLog.setCreateTime(LocalDateTime.now());
+                projectApprovalLog.setProjectApprovalId(projectApproval.getId());
+                //驳回
+                projectApprovalLog.setType(4);
+                projectApprovalLogService.save(projectApprovalLog);
                 break;
         }
         return msg;
     }
+
+    @RequestMapping("/importData")
+    public HttpRespMsg importData(MultipartFile multipartFile){
+        return projectApprovalService.importData(multipartFile);
+    }
+
+    @RequestMapping("/exportData")
+    public HttpRespMsg exportData( String keyword,
+                                  @RequestParam(required = false, defaultValue = "1") Integer searchField,@RequestParam(defaultValue = "3") String statuses,Integer category,Integer viewId){
+        return projectApprovalService.exportData(keyword,searchField,statuses,category,viewId);
+    }
 }
 

+ 38 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectApprovalLogController.java

@@ -0,0 +1,38 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.management.platform.entity.ProjectApprovalLog;
+import com.management.platform.service.ProjectApprovalLogService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-21
+ */
+@RestController
+@RequestMapping("/project-approval-log")
+public class ProjectApprovalLogController {
+    @Resource
+    private ProjectApprovalLogService projectApprovalLogService;
+
+    @RequestMapping("/listById")
+    public HttpRespMsg listById(Integer id){
+        HttpRespMsg msg=new HttpRespMsg();
+        List<ProjectApprovalLog> list = projectApprovalLogService.list(new LambdaQueryWrapper<ProjectApprovalLog>().eq(ProjectApprovalLog::getProjectApprovalId, id).orderByAsc(ProjectApprovalLog::getCreateTime));
+        msg.setData(list);
+        return msg;
+    }
+
+}
+

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

@@ -450,8 +450,8 @@ public class ProjectController {
     }
     //导出项目任务报表
     @RequestMapping("/exportProjectTask")
-    public HttpRespMsg exportProjectTask(Integer taskType) {
-        return projectService.exportProjectTask(request,taskType);
+    public HttpRespMsg exportProjectTask(Integer projectId,Integer groupId,Integer taskType) {
+        return projectService.exportProjectTask(request,projectId,groupId,taskType);
     }
 
     //分页查询项目成本报表

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

@@ -459,6 +459,12 @@ public class Project extends Model<Project> {
     @TableField("from_outside")
     private Integer fromOutside;
 
+    /**
+     * 立项id
+     */
+    @TableField("approve_id")
+    private Integer approveId;
+
     /**
      * 预估工时表:任务分组
      */

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

@@ -0,0 +1,55 @@
+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 lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-21
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectApprovalLog extends Model<ProjectApprovalLog> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 操作人姓名
+     */
+    @TableField("user_name")
+    private String userName;
+
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    /**
+     * 操作类型 0-提交 1-编辑 2-通过 3-驳回 4-撤销
+     */
+    @TableField("type")
+    private Integer type;
+
+    @TableField("project_approval_id")
+    private Integer projectApprovalId;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 23 - 17
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -17,12 +17,13 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-01-26
+ * @since 2024-03-23
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
 public class TimeType extends Model<TimeType> {
+
     private static final long serialVersionUID=1L;
 
     /**
@@ -49,18 +50,6 @@ public class TimeType extends Model<TimeType> {
     @TableField("pm")
     private Float pm;
 
-    /**
-     * 休息时间
-     */
-    @TableField(exist = false)
-    private List<TimeAutoExclude> excludeTimeList;
-
-    /**
-     * 是否自动同步企业微信通讯录
-     */
-    @TableField(exist = false)
-    private Integer saasSyncContact;
-
     /**
      * 每月工作天数
      */
@@ -379,9 +368,6 @@ public class TimeType extends Model<TimeType> {
     @TableField("report_auto_approve_days")
     private Integer reportAutoApproveDays;
 
-    @TableField(exist = false)
-    private List<User> userList;
-
     /**
      * 日报填报时长上限,默认12小时
      */
@@ -442,7 +428,6 @@ public class TimeType extends Model<TimeType> {
     @TableField("restart_task_need_reason")
     private Integer restartTaskNeedReason;
 
-
     /**
      * 0-否 1-是  子项目是否必填
      */
@@ -538,6 +523,7 @@ public class TimeType extends Model<TimeType> {
      */
     @TableField("time_can_overlap")
     private Integer timeCanOverlap;
+
     /**
      * 自定义维度是否填报是可多选
      */
@@ -550,6 +536,26 @@ public class TimeType extends Model<TimeType> {
     @TableField("task_required")
     private Integer taskRequired;
 
+    /**
+     * 日报填报隐藏阶段项
+     */
+    @TableField("hide_stages")
+    private Integer hideStages;
+
+    /**
+     * 日报填报隐藏任务项
+     */
+    @TableField("hide_task")
+    private Integer hideTask;
+
+
+    @TableField(exist = false)
+    private List<User> userList;
+    @TableField(exist = false)
+    private List<TimeAutoExclude> excludeTimeList;
+    @TableField(exist = false)
+    private Integer saasSyncContact;
+
 
     @Override
     protected Serializable pkVal() {

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectApprovalLogMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ProjectApprovalLog;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-21
+ */
+public interface ProjectApprovalLogMapper extends BaseMapper<ProjectApprovalLog> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectApprovalLogService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectApprovalLog;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-21
+ */
+public interface ProjectApprovalLogService extends IService<ProjectApprovalLog> {
+
+}

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

@@ -4,6 +4,7 @@ import com.management.platform.entity.ProjectApproval;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
 
@@ -19,4 +20,7 @@ public interface ProjectApprovalService extends IService<ProjectApproval> {
 
     HttpRespMsg listByPage(Integer pageIndex, Integer pageSize, String keyword, Integer searchField, String statuses, Integer category,Integer viewId, HttpServletRequest request);
 
+    HttpRespMsg importData(MultipartFile multipartFile);
+
+    HttpRespMsg exportData(String keyword, Integer searchField, String statuses, Integer category, Integer viewId);
 }

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

@@ -94,7 +94,7 @@ public interface ProjectService extends IService<Project> {
 
     HttpRespMsg getProjectTask(Integer pageIndex, Integer pageSize, Integer projectId,Integer groupId, HttpServletRequest request,Integer taskType);
 
-    HttpRespMsg exportProjectTask(HttpServletRequest request,Integer taskType);
+    HttpRespMsg exportProjectTask(HttpServletRequest request,Integer projectId,Integer groupId,Integer taskType);
 
     HttpRespMsg getAllProjectCost(Integer pageIndex, Integer pageSize, Integer projectId,HttpServletRequest request);
 

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ProjectApprovalLog;
+import com.management.platform.mapper.ProjectApprovalLogMapper;
+import com.management.platform.service.ProjectApprovalLogService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-21
+ */
+@Service
+public class ProjectApprovalLogServiceImpl extends ServiceImpl<ProjectApprovalLogMapper, ProjectApprovalLog> implements ProjectApprovalLogService {
+
+}

+ 554 - 14
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectApprovalServiceImpl.java

@@ -1,21 +1,35 @@
 package com.management.platform.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.management.platform.entity.*;
-import com.management.platform.mapper.ProjectApprovalMapper;
-import com.management.platform.mapper.ProjectMapper;
-import com.management.platform.mapper.UserMapper;
+import com.management.platform.mapper.*;
 import com.management.platform.service.*;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
+import com.management.platform.util.MessageUtils;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.multipart.MultipartFile;
+import sun.misc.Request;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -38,6 +52,30 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
     private SysFunctionService sysFunctionService;
     @Resource
     private ProjectApprovalCheckService projectApprovalCheckService;
+    @Resource
+    private WxCorpInfoService wxCorpInfoService;
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
+    @Resource
+    private CompanyMapper companyMapper;
+    @Resource
+    private HttpServletRequest request;
+    @Resource
+    private ProjectCategoryMapper projectCategoryMapper;
+    @Resource
+    private ParticipationApprovalMapper participationApprovalMapper;
+    @Resource
+    private ParticipationApprovalService participationApprovalService;
+    @Resource
+    private ProjectApprovalAuditorMapper projectApprovalAuditorMapper;
+    @Resource
+    private ProjectApprovalAuditorService projectApprovalAuditorService;
+    @Resource
+    private ProjectMapper projectMapper;
+    @Resource
+    private ExcelExportService excelExportService;
+    @Value(value = "${upload.path}")
+    private String path;
 
 
 
@@ -95,23 +133,525 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
             if(optional.isPresent()){
                 r.setInchargerName(optional.get().getName());
             }
-            Integer canCheck=0;
+//            Integer canCheck=0;
             List<ProjectApprovalCheck> checkList = approvalCheckList.stream().filter(a -> a.getProjectApprovalId().equals(r.getId())).collect(Collectors.toList());
             Optional<ProjectApprovalCheck> first = checkList.stream().filter(c -> c.getUserId().equals(user.getId())&&c.getStatus()==0).findFirst();
-            if(first.isPresent()){
-                boolean anyMatch = checkList.stream().anyMatch(c -> c.getSeq() < first.get().getSeq() && c.getStatus() == 0);
-                if(!anyMatch){
-                    canCheck=1;
-                }
-            }
-            if(hasAllPriviledge||hasCheckPriviledge){
-                canCheck=1;
-            }
-            r.setCanChecked(canCheck);
+//            if(first.isPresent()){
+//                //针对当前登录人员 作为审核人检查是否有审核权限
+//                boolean anyMatch = checkList.stream().anyMatch(c -> c.getSeq() < first.get().getSeq() && c.getStatus() == 0);
+//                if(!anyMatch){
+//                    canCheck=1;
+//                }
+//            }
+            //有权限可编辑
+//            if(hasAllPriviledge||hasCheckPriviledge){
+//                canCheck=1;
+//            }
+//            //全部审核通过的情况不可编辑
+//            boolean b = checkList.stream().allMatch(c -> c.getStatus().equals(1));
+//            if(b){
+//                canCheck=0;
+//            }
+//            r.setCanChecked(canCheck);
         });
         result.put("records",records);
         result.put("total",iPage.getTotal());
         msg.setData(result);
         return msg;
     }
+
+    @Override
+    public HttpRespMsg importData(MultipartFile multipartFile) {
+        HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        String fileName = multipartFile.getOriginalFilename();
+        File file = new File(fileName == null ? "file" : fileName);
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            //读取文件输入流
+            inputStream = multipartFile.getInputStream();
+            outputStream = new FileOutputStream(file);
+            byte[] buffer = new byte[4096];
+            int temp = 0;
+            while ((temp = inputStream.read(buffer, 0, 4096)) != -1) {
+                //输入流数据写到输出流
+                outputStream.write(buffer, 0, temp);
+            }
+            inputStream.close();
+            outputStream.close();
+            //然后解析表格
+            XSSFWorkbook workbook = new XSSFWorkbook(file);
+            //我们只需要第一个sheet
+            XSSFSheet sheet = workbook.getSheetAt(0);
+            //获取全部人员
+            List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", user.getCompanyId()).eq("is_active", 1));
+            Company company = companyMapper.selectById(user.getCompanyId());
+            List<Project> projectList = new ArrayList<Project>();
+            //由于第一行需要指明列对应的标题
+            int rowNum = sheet.getLastRowNum();
+            HashMap<String, Integer> projectLevelMap = new HashMap<>();
+            projectLevelMap.put(MessageUtils.message("excel.normal"), 1);
+            projectLevelMap.put(MessageUtils.message("excel.urgent"), 2);
+            projectLevelMap.put(MessageUtils.message("excel.important"), 3);
+            projectLevelMap.put(MessageUtils.message("excel.impAndUrg"), 4);
+            projectLevelMap.put(MessageUtils.message("excel.lowRisk"), 5);
+            projectLevelMap.put(MessageUtils.message("excel.MedRisk"), 6);
+            projectLevelMap.put(MessageUtils.message("excel.highRisk"), 7);
+            List<String> existCodeList = new ArrayList<>();
+            List<String> userNameList=new ArrayList<>();
+            for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                XSSFCell participatorCell = row.getCell(6);
+                XSSFCell inchargerCell = row.getCell(7);
+
+                String part = "";
+                String inchargerName = "";
+                if (participatorCell!=null) {
+                    participatorCell.setCellType(CellType.STRING);
+                    part = participatorCell.getStringCellValue().trim();
+                }
+                if (inchargerCell != null) {
+                    inchargerCell.setCellType(CellType.STRING);
+                    inchargerName = inchargerCell.getStringCellValue().trim();
+                }
+                //兼容繁体
+                if(part.equals("参与人") || part.equals("參與人")){
+                    continue;
+                }
+                String[] partSplit = part.split("\\,|\\,");
+                for (String s : partSplit) {
+                    if(!userNameList.contains(s)&&!s.equals("")){
+                        userNameList.add(s);
+                    }
+                }
+                if(!userNameList.contains(inchargerName)&&!inchargerName.equals("")){
+                    userNameList.add(inchargerName);
+                }
+            }
+            HttpRespMsg respMsg=new HttpRespMsg();
+            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1&&userNameList.size()>0){
+                System.out.println("参与搜素的人员列表"+userNameList + userNameList.size());
+                respMsg = wxCorpInfoService.getBatchSearchUserInfo(wxCorpInfo, userNameList,null);
+                if(respMsg.code.equals("0")){
+                    msg.setError("姓名为["+String.valueOf(respMsg.data)+"]的人员存在重复,请使用工号!");
+                    return msg;
+                }
+            }
+            List<User> targetUserList= (List<User>) respMsg.data;
+            int importCount = 0;
+            for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                //项目编号 项目分类	项目名称 参与人 负责人 级别 开始日期 截止日期 合同金额
+                XSSFCell codeCell = row.getCell(0);
+                XSSFCell categoryCell = row.getCell(1);
+                XSSFCell isPublicCell = row.getCell(2);
+                XSSFCell nameCell = row.getCell(3);
+                XSSFCell descCell = row.getCell(4);
+                XSSFCell participatorCell = row.getCell(5);
+                XSSFCell inchargerCell = row.getCell(6);
+                XSSFCell reportAuditorsCell=null;
+                XSSFCell reportCcCell=null;
+                int i=0;
+                if(timeType.getReportAuditType()==0||timeType.getReportAuditType()==4||timeType.getReportAuditType()==6){
+                    i++;
+                    reportAuditorsCell = row.getCell(6+i);
+                }
+                if(timeType.getReportAuditType()==6){
+                    i++;
+                    reportCcCell=row.getCell(6+i);
+                }
+                XSSFCell levelCell = row.getCell(7+i);
+                XSSFCell startDateCell = row.getCell(8+i);
+                XSSFCell endDateCell = row.getCell(9+i);
+                XSSFCell amountCell = row.getCell(10+i);
+
+
+                if (codeCell != null)codeCell.setCellType(CellType.STRING);
+                if (nameCell != null)nameCell.setCellType(CellType.STRING);
+                if (categoryCell != null)categoryCell.setCellType(CellType.STRING);
+                if (isPublicCell != null)isPublicCell.setCellType(CellType.STRING);
+                if (descCell != null)descCell.setCellType(CellType.STRING);
+                if (participatorCell != null)participatorCell.setCellType(CellType.STRING);
+                if (inchargerCell != null)inchargerCell.setCellType(CellType.STRING);
+                if (reportAuditorsCell != null)reportAuditorsCell.setCellType(CellType.STRING);
+                if (reportCcCell != null)reportCcCell.setCellType(CellType.STRING);
+                if (levelCell != null)levelCell.setCellType(CellType.STRING);
+                if (startDateCell != null)startDateCell.setCellType(CellType.NUMERIC);
+                if (endDateCell != null)endDateCell.setCellType(CellType.NUMERIC);
+                if (amountCell != null)amountCell.setCellType(CellType.STRING);
+                if (descCell != null)descCell.setCellType(CellType.STRING);
+                if (nameCell == null) {//项目名称为空的直接跳过
+                    throw new Exception("项目名称不能为空");
+                }
+                ProjectApproval project = new ProjectApproval();
+                boolean exists=false;
+                if (codeCell != null) {
+                    String code = codeCell.getStringCellValue().trim().replaceAll("\\u00a0", "");
+                    if ((code.equals("项目编号") || code.equals("項目編號") || code.equals("project No")) && rowIndex == 0) {
+                        //跳过第一行标题
+                        continue;
+                    }
+                    //检查编号是否已经存在
+                    if (!StringUtils.isEmpty(code)) {
+                        int cnt = projectMapper.selectCount(new QueryWrapper<Project>().eq("project_code", code).eq("company_id", user.getCompanyId()));
+                        if (cnt > 0) {
+//                            throw new Exception("项目编号存在重复: " + code);
+                            existCodeList.add(code);
+                            //跳过编号重复的数据
+                            exists=true;
+                            continue;
+                        }
+                    }
+                    project.setProjectCode(code);
+                }
+                //检查项目分类是否存在
+                List<ProjectCategory> projectCategoryList = projectCategoryMapper.selectList(new QueryWrapper<ProjectCategory>().eq("company_id", user.getCompanyId()));
+                if (categoryCell != null && !StringUtils.isEmpty(categoryCell.getStringCellValue())) {
+                    Optional<ProjectCategory> category = projectCategoryList.stream().filter(pc -> pc.getName().equals(categoryCell.getStringCellValue())).findFirst();
+                    if(!category.isPresent()){
+                        throw  new Exception("项目分类["+categoryCell.getStringCellValue()+"]不存在");
+                    }
+                    project.setCategory(category.get().getId());
+                    project.setCategoryName(categoryCell.getStringCellValue());
+                }
+                String name = nameCell.getStringCellValue().trim().replaceAll("\\u00a0", "");
+                project.setCompanyId(user.getCompanyId());
+                project.setCreatorId(user.getId());
+                project.setCreatorName(user.getName());
+                project.setProjectName(name);
+                project.setCreateDate(LocalDate.now());
+                //处理人员
+                if (inchargerCell != null) {
+                    String inchargerName = inchargerCell.getStringCellValue().trim();
+                    if (!StringUtils.isEmpty(inchargerName)) {
+                        String s1;
+                        if(inchargerName.startsWith("/")){
+                            s1=inchargerName.substring(1,inchargerName.length());
+                        }else s1=inchargerName;
+                        String s2;
+                        if(s1.endsWith("/")){
+                            s2=s1.substring(0,s1.length()-1);
+                        }else s2=s1;
+                        String[] split = s2.split("/");
+                        Optional<User> first;
+                        Integer exception=null;
+                        if(split.length==1){
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                            }
+                            exception=0;
+                        }else {
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                exception=1;
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                            }
+                        }
+                        if (first.isPresent()) {
+                            project.setInchargerId(first.get().getId());
+                            project.setInchargerName(first.get().getName());
+                        } else {
+                            switch (exception){
+                                case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                            }
+                        }
+                    }
+                }
+
+
+                if (levelCell != null) {
+                    String levelStr = levelCell.getStringCellValue();
+                    if (!StringUtils.isEmpty(levelStr)) {
+                        project.setLevel(projectLevelMap.get(levelStr));
+                    }
+                }
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                if (startDateCell !=null && !StringUtils.isEmpty(startDateCell.getDateCellValue())) {
+                    project.setPlanStartDate(LocalDate.parse(sdf.format(startDateCell.getDateCellValue()), DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                }
+                if (endDateCell !=null && !StringUtils.isEmpty(endDateCell.getDateCellValue())) {
+                    project.setPlanEndDate(LocalDate.parse(sdf.format(endDateCell.getDateCellValue()), DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                }
+                if (amountCell !=null && !StringUtils.isEmpty(amountCell.getStringCellValue())) {
+                    project.setContractAmount(Double.parseDouble(amountCell.getStringCellValue()));
+                }
+                if(descCell!=null&&!StringUtils.isEmpty(descCell.getStringCellValue())){
+                    project.setProjectDesc(descCell.getStringCellValue());
+                }
+                if (isPublicCell != null && !StringUtils.isEmpty(isPublicCell.getStringCellValue())) {
+                    project.setIsPublic(MessageUtils.message("excel.yes").equals(isPublicCell.getStringCellValue())?1:0);
+                }
+                if(exists){
+                    ProjectApproval updateProject = projectApprovalMapper.selectList(new QueryWrapper<ProjectApproval>().eq("project_code", project.getProjectCode()).eq("company_id", user.getCompanyId())).get(0);
+                    project.setId(updateProject.getId());
+                    projectApprovalMapper.updateById(project);
+                }else {
+                    projectApprovalMapper.insert(project);
+                }
+                importCount++;
+                //导入项目参与人,遵守只增不减的原则, 避免误删
+                List<ParticipationApproval> oldPartList = new ArrayList<>();
+                List<ProjectApprovalAuditor> oldAuditorList = new ArrayList<>();
+                if (exists) {
+                    //更新的项目,检查已经存在的项目参与人
+                    oldPartList = participationApprovalMapper.selectList(new QueryWrapper<ParticipationApproval>().eq("project_id", project.getId()));
+                    oldAuditorList = projectApprovalAuditorMapper.selectList(new QueryWrapper<ProjectApprovalAuditor>().eq("project_id", project.getId()));
+                }
+                List<ParticipationApproval> participationList = new ArrayList<>();
+                if(inchargerCell!=null){
+                    String value = inchargerCell.getStringCellValue().trim();
+                    if (!StringUtils.isEmpty(value)) {
+                        //有项目经理
+                        String s1;
+                        if(value.startsWith("/")){
+                            s1=value.substring(1,value.length());
+                        }else {
+                            s1=value;
+                        }
+                        String s2;
+                        if(s1.endsWith("/")){
+                            s2=s1.substring(0,s1.length()-1);
+                        }else {
+                            s2=s1;
+                        }
+                        String[] split = s2.split("/");
+                        Optional<User> first;
+                        Integer exception=null;
+                        if(split.length==1){
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                            }
+                            exception=0;
+                        }else {
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                exception=1;
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                            }
+                        }
+                        ParticipationApproval p = new ParticipationApproval();
+                        if (first.isPresent()) {
+                            p.setUserId(first.get().getId());
+                            p.setProjectApprovalId(project.getId());
+                            participationList.add(p);
+                        } else {
+                            switch (exception){
+                                case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                            }
+                        }
+                    }
+                }
+                if (participatorCell != null) {
+                    String part = participatorCell.getStringCellValue().trim();
+                    if (!StringUtils.isEmpty(part)) {
+                        String[] partSplit = part.split("\\,|\\,");
+                        for (String str : partSplit) {
+                            ParticipationApproval p = new ParticipationApproval();
+                            String s1;
+                            if(str.startsWith("/")){
+                                s1=str.substring(1,str.length());
+                            }else s1=str;
+                            String s2;
+                            if(s1.endsWith("/")){
+                                s2=s1.substring(0,s1.length()-1);
+                            }else s2=s1;
+                            String[] split = str.split("/");
+                            Optional<User> first;
+                            Integer exception=null;
+                            if(split.length==1){
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                    first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                }else {
+                                    first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                }
+                                exception=0;
+                            }else {
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                    first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                    exception=1;
+                                }else {
+                                    first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                }
+                            }
+                            if (first.isPresent()) {
+                                User partMemb = first.get();
+//                                    System.out.println("参与人:"+partMemb.getName());
+                                if (!participationList.stream().anyMatch(partOne->partOne.getUserId().equals(partMemb.getId()))) {
+                                    p.setUserId(partMemb.getId());
+                                    p.setProjectApprovalId(project.getId());
+                                    participationList.add(p);
+                                }
+                            } else {
+                                switch (exception){
+                                    case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                    case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                }
+                            }
+                        }
+                    }
+                }
+                if (participationList.size() > 0) {
+                    //批量保存
+                    List<ParticipationApproval> finalOldPartList = oldPartList;
+                    List<ParticipationApproval> addPartList = participationList.stream().filter(newP-> !finalOldPartList.stream().anyMatch(oldP->oldP.getUserId().equals(newP.getUserId()))).collect(Collectors.toList());
+                    if (addPartList.size() > 0) {
+                        participationApprovalService.saveBatch(addPartList);
+                    }
+                }
+                List<ProjectApprovalAuditor> projectAuditorList = new ArrayList<>();
+                if (reportAuditorsCell != null) {
+                    String part = reportAuditorsCell.getStringCellValue().trim();
+                    if (!StringUtils.isEmpty(part)) {
+                        String[] partSplit = part.split("\\,|\\,");
+                        for (String str : partSplit) {
+                            if(str.equals(inchargerCell.getStringCellValue())){
+                                continue;
+                            }
+                            ProjectApprovalAuditor projectAuditor = new ProjectApprovalAuditor();
+                            String s1;
+                            if(str.startsWith("/")){
+                                s1=str.substring(1,str.length());
+                            }else s1=str;
+                            String s2;
+                            if(s1.endsWith("/")){
+                                s2=s1.substring(0,s1.length()-1);
+                            }else s2=s1;
+                            String[] split = str.split("/");
+                            Optional<User> first;
+                            Integer exception=null;
+                            if(split.length==1){
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                    first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                }else {
+                                    first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                }
+                                exception=0;
+                            }else {
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                    first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                                    exception=1;
+                                }else {
+                                    first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                }
+                            }
+                            if (first.isPresent()) {
+                                User partMemb = first.get();
+//                                    System.out.println("参与人:"+partMemb.getName());
+                                if (!projectAuditorList.stream().anyMatch(partOne->partOne.getAuditorId().equals(partMemb.getId()))) {
+                                    projectAuditor.setAuditorId(partMemb.getId());
+                                    projectAuditor.setAuditorName(partMemb.getName());
+                                    projectAuditor.setProjectApprovalId(project.getId());
+                                    projectAuditorList.add(projectAuditor);
+                                }
+                            } else {
+                                switch (exception){
+                                    case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                    case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    ProjectApprovalAuditor projectAuditor=new ProjectApprovalAuditor();
+                    projectAuditor.setAuditorId(project.getInchargerId());
+                    projectAuditor.setAuditorName(project.getInchargerName());
+                    projectAuditor.setProjectApprovalId(project.getId());
+                    projectApprovalAuditorMapper.insert(projectAuditor);
+                }
+                if (projectAuditorList.size() > 0) {
+                    //批量保存
+                    List<ProjectApprovalAuditor> finalOldAuditorList = oldAuditorList;
+                    List<ProjectApprovalAuditor> addAuditorList = projectAuditorList.stream().filter(newP-> !finalOldAuditorList.stream().anyMatch(oldP->oldP.getAuditorId().equals(newP.getAuditorId()))).collect(Collectors.toList());
+                    if (addAuditorList.size() > 0) {
+                        projectApprovalAuditorService.saveBatch(addAuditorList);
+                    }
+                }
+            }
+            //msg.data = "成功导入"+importCount+"条数据。";
+            msg.data = MessageUtils.message("data.importSucRow",importCount);
+            if (existCodeList.size() > 0) {
+                String collect = existCodeList.stream().collect(Collectors.joining(","));
+                msg.data += MessageUtils.message("data.upSkip",existCodeList.size(),collect);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg exportData(String keyword, Integer searchField, String statuses, Integer category, Integer viewId) {
+        HttpRespMsg msg=new HttpRespMsg();
+        List<String> titleList=new ArrayList<>();
+        String[] arr=new String[]{"正常","紧急","重要","重要且紧急","低风险","中风险","高风险"};
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
+        titleList.add("项目编号");
+        titleList.add("项目名称");
+        titleList.add("项目分类");
+        titleList.add("项目类型");
+        titleList.add("项目经理");
+        titleList.add("级别");
+        titleList.add("审核状态");
+        List<List<String>> dataList=new ArrayList<>();
+        dataList.add(titleList);
+        HttpRespMsg respMsg = listByPage(-1, -1, keyword, searchField, statuses, category, viewId, request);
+        Map<String, Object> msgData = (Map<String, Object>) respMsg.getData();
+        List<ProjectApproval> records = (List<ProjectApproval>) msgData.get("records");
+        for (ProjectApproval record : records) {
+            List<String> item=new ArrayList<>();
+            item.add(record.getProjectCode()==null?"":record.getProjectCode());
+            item.add(record.getProjectName());
+            item.add(record.getCategoryName());
+            item.add(record.getIsPublic()==0?"正式项目":"非项目");
+            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                item.add("$userName="+(record.getInchargerName()==null?"":record.getInchargerName())+"$");
+            }else {
+                item.add(record.getInchargerName()==null?"":record.getInchargerName());
+            }
+            item.add(arr[record.getLevel()-1]);
+            item.add(record.getStatus()==0?"待审核":record.getStatus()==1?"已通过":"已驳回");
+            dataList.add(item);
+        }
+        String title="立项导出_"+System.currentTimeMillis();
+        try {
+            return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,title,dataList,path);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return msg;
+    }
 }

+ 31 - 21
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -126,6 +126,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     @Resource
     private TaskMapper taskMapper;
     @Resource
+    private ReportAuditLogMapper reportAuditLogMapper;
+    @Resource
     private EarningSnapshotMapper earningSnapshotMapper;
     @Resource
     private ExpenseItemMapper expenseItemMapper;
@@ -1365,7 +1367,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     }
 
     private void deleteProData(Integer id) {
-        reportMapper.delete(new QueryWrapper<Report>().eq("project_id", id));
+        List<Report> rList = reportMapper.selectList(new QueryWrapper<Report>().select("id").eq("project_id", id));
+        if (rList.size() > 0) {
+            //删除日报的审核日志
+            reportAuditLogMapper.delete(new QueryWrapper<ReportAuditLog>().in("report_id", rList.stream().map(Report::getId).collect(Collectors.toList())));
+            reportMapper.delete(new QueryWrapper<Report>().eq("project_id", id));
+        }
+
         subProjectMapper.delete(new QueryWrapper<SubProject>().eq("project_id", id));
         participationMapper.delete(new QueryWrapper<Participation>().eq("project_id", id));
         taskMapper.delete(new QueryWrapper<Task>().eq("project_id", id));
@@ -2823,7 +2831,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     }
 
     @Override
-    public HttpRespMsg exportProjectTask(HttpServletRequest request,Integer taskType) {
+    public HttpRespMsg exportProjectTask(HttpServletRequest request,Integer projectId,Integer groupId,Integer taskType) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
 
         User user = userMapper.selectById(request.getHeader("Token"));
@@ -2849,7 +2857,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 inchagerIds.add(-1);
             }
         }
-        List<Map<String,Object>> projectList = taskMapper.getProjectTask(companyId, null, null, null,null,taskType,inchagerIds);
+        List<Map<String,Object>> projectList = taskMapper.getProjectTask(companyId, null, null, projectId,groupId,taskType,inchagerIds);
         if(companyId==3092){
             List<Integer> groupIds = projectList.stream().map(p -> Integer.valueOf(String.valueOf(p.get("groupId")))).distinct().collect(Collectors.toList());
             groupIds.add(-1);
@@ -11716,6 +11724,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                         break;
                     case "Z09":categoryName="售后工程项目";
                         break;
+                    case "Z10":categoryName="运维项目";
+                        break;
                     default:categoryName="未知";
                 }
                 String finalCategoryName = categoryName;
@@ -12819,13 +12829,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                                 List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
                                         && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId())).collect(Collectors.toList());
                                 if(mapList!=null&&mapList.size()>0){
-                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
-                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
-                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum());
-                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
-                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    item.put("planHour",mapList.get(0).get("planHour"));
+                                    item.put("realHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("realCost",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("overHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("normalHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
                                     double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
-                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    double planHour =  Double.valueOf(String.valueOf( mapList.get(0).get("planHour"))).doubleValue();
                                     item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);
@@ -12866,13 +12876,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                                 List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
                                         && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId())).collect(Collectors.toList());
                                 if(mapList!=null&&mapList.size()>0){
-                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
-                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
-                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum());
-                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
-                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    item.put("planHour",mapList.get(0).get("planHour"));
+                                    item.put("realHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("realCost",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("overHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("normalHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
                                     double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
-                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    double planHour =  Double.valueOf(String.valueOf( mapList.get(0).get("planHour"))).doubleValue();
                                     item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);
@@ -12917,13 +12927,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                                 List<Map<String, Object>> mapList = resultList.stream().filter(r -> Integer.valueOf(String.valueOf(r.get("projectId"))).equals(project.getId())
                                         && Integer.valueOf(String.valueOf(r.get("groupId"))).equals(taskGroup.get().getId())).collect(Collectors.toList());
                                 if(mapList!=null&&mapList.size()>0){
-                                    item.put("planHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("planHour")))).sum());
-                                    item.put("realHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum());
-                                    item.put("realCost",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum());
-                                    item.put("overHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum());
-                                    item.put("normalHour",mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum());
+                                    item.put("planHour",mapList.get(0).get("planHour"));
+                                    item.put("realHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("realCost",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("realCost")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("overHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("overHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
+                                    item.put("normalHour",new BigDecimal(mapList.stream().mapToDouble(m-> Double.valueOf(String.valueOf(m.get("normalHour")))).sum()).setScale(2,RoundingMode.HALF_UP));
                                     double realHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("realHour")))).sum();
-                                    double planHour = mapList.stream().mapToDouble(m -> Double.valueOf(String.valueOf(m.get("planHour")))).sum();
+                                    double planHour =  Double.valueOf(String.valueOf( mapList.get(0).get("planHour"))).doubleValue();
                                     item.put("process",percentFormat.format(realHour/planHour));
                                 }else {
                                     item.put("planHour",0);

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

@@ -310,7 +310,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     //费用报销
                     title = "工时管家:任务到期通知";
                 }
-                else if ("budgetReview".equals(pageRouter)) {
+                else if (pageRouter.contains("projectInside")) {
                     //预估工时审核
                     title = "预估工时审核通知";
                 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml

@@ -15,7 +15,7 @@ spring:
       location: C:/upload/
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://47.101.180.183:17089/man_hour_manager?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useSSL=false
+    url: jdbc:mysql://47.101.180.183:17089/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useSSL=false
     username: root
     password: P011430@Huoshi*
 

+ 19 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectApprovalLogMapper.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectApprovalLogMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectApprovalLog">
+        <id column="id" property="id" />
+        <result column="user_name" property="userName" />
+        <result column="create_time" property="createTime" />
+        <result column="type" property="type" />
+        <result column="project_approval_id" property="projectApprovalId" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, user_name, create_time, type, project_approval_id
+    </sql>
+
+</mapper>

ファイルの差分が大きいため隠しています
+ 6 - 17
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml


ファイルの差分が大きいため隠しています
+ 3 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/立项导入模板.xlsx


+ 13 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue

@@ -725,6 +725,19 @@
                             // 6、7 合同通过、驳回
                             this.$router.push("/contract");
                             this.drawer = false;
+                        } else if (type == 10) {
+                            // 10 预估工时审核 通过 驳回
+                            var action = '';
+                            //跳转进入项目的任务看板
+                            date = decodeURIComponent(date);
+                            if (date.includes('@adjustBudget-')) {
+                                //需要打开分组预估工时设置
+                                action = date.split('@')[1];
+                                date = date.split('@')[0];
+                            }
+                            sessionStorage.action = action
+                            this.$router.push("/projectInside/"+date);
+                            this.drawer = false;
                         }
                         
                         

+ 21 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue

@@ -137,6 +137,9 @@
                     if (loginUserId.includes('#/')) {
                         loginUserId = loginUserId.substring(0, loginUserId.indexOf('#/'));
                     }
+                    if (loginUserId.includes('&')) {
+                        loginUserId = loginUserId.substring(0, loginUserId.indexOf('&'));
+                    }
                     var path = null;
                     if (href.indexOf('path') > 0) {
                         path = href.split('path=')[1].split('&')[0];
@@ -173,6 +176,9 @@
                     if (loginUserId.includes('#/')) {
                         loginUserId = loginUserId.substring(0, loginUserId.indexOf('#/'));
                     }
+                    if (loginUserId.includes('&')) {
+                        loginUserId = loginUserId.substring(0, loginUserId.indexOf('&'));
+                    }
                     var path = null;
                     if (href.indexOf('path') > 0) {
                         path = href.split('path=')[1].split('&')[0];
@@ -362,7 +368,21 @@
                                 this.permissionsList(res.data)
                                 if(user.moduleList.length > 0) {
                                     if (path) {
-                                        this.$router.push({ path: path })
+                                        var action = '';
+                                        //url上传来的格式
+                                        if (path.includes('projectInside')) {
+                                            //跳转进入项目的任务看板
+                                            path = decodeURIComponent(path);
+                                            console.log('pppp='+path);
+                                            if (path.includes('@adjustBudget-')) {
+                                                //需要打开分组预估工时设置
+                                                action = path.split('@')[1];
+                                                path = path.split('@')[0];
+                                            }
+                                        }
+                                        console.log('action==='+action);
+                                        sessionStorage.action = action
+                                        this.$router.push({ path: path})
                                     } else {
                                         this.$router.push({ path: user.moduleList[0].path })
                                     }

+ 2 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -2173,6 +2173,8 @@ export default {
           if(this.taskTypeId != 'null' && this.taskTypeId != null && this.taskTypeId != '') {
             sl.taskType = this.taskTypeId
           }
+          sl.projectId = this.proJuctId
+          sl.groupId=this.projectGroupId
         } else if (this.ins == 2) {
           fName = this.$t('projectcoststatement') + '.xlsx';
           url += "/exportAllProjectCost";

+ 18 - 11
fhKeeper/formulahousekeeper/timesheet/src/views/project/projectInside.vue

@@ -57,7 +57,7 @@
                                         <el-dropdown-item divided @click.native="renameGroup(item)" :disabled="!(projectCreatorId == user.id || projectInchargerId == user.id || permissions.projectManagement || isManageDept)">
                                             <i class="el-icon-edit"></i>
                                             {{ $t('changegroups') }}</el-dropdown-item>
-                                        <el-dropdown-item divided @click.native="setManDay(item)" v-if="user.timeType.projectManDay==1" :disabled="!(projectCreatorId == user.id || projectInchargerId == user.id || permissions.projectManagement || isManageDept)">
+                                        <el-dropdown-item divided @click.native="setManDay()" v-if="user.timeType.projectManDay==1" :disabled="!(projectCreatorId == user.id || projectInchargerId == user.id || permissions.projectManagement || isManageDept)">
                                             <i class="el-icon-time"></i>
                                             预估工时</el-dropdown-item>
                                         <el-dropdown-item divided @click.native="addToTemplate(item)" :disabled="!(projectCreatorId == user.id || projectInchargerId == user.id || permissions.projectManagement || isManageDept)">
@@ -3202,9 +3202,9 @@
                 this.groupForm = JSON.parse(JSON.stringify(item));
                 this.setInchargerDialog = true;
             },
-            setManDay(item) {
+            setManDay() {
                 this.modGroupManDayKey++
-                this.groupForm = JSON.parse(JSON.stringify(item));
+                // this.groupForm = JSON.parse(JSON.stringify(item));
                 this.modGroupManDayDialog = true;
             },
             renameGroup(item) {
@@ -3421,11 +3421,15 @@
                 this.addGroupDialog = true;
                 this.groupForm = {projectId: this.curProjectId};
             },
-            getInsideData() {
-                this.getTaskGroups();
+            getInsideData(action) {
+                var targetGpId = null;
+                if (action) {
+                    targetGpId = action.split('-')[1];
+                }
+                this.getTaskGroups(targetGpId);
             },
             //加载项目内的任务分组
-            getTaskGroups() {
+            getTaskGroups(errorGroupId) {
                 this.http.post('/task-group/list',{projectId: this.curProjectId},
                 res => {
                     if (res.code == "ok") {
@@ -3439,6 +3443,10 @@
                         } else {
                             this.groupList = res.data;
                         }
+                        if (errorGroupId != null) {
+                            //需要修正分组工时
+                            this.setManDay();
+                        }
                         console.log(this.groupList, '返回的数据')
                         console.log(this.groupList,'grouplist');
                         if (this.groupList.length > 0) {
@@ -3865,7 +3873,6 @@
             },
             // 企业微信搜索
             echartDepartment() {
-                console.log('我点击了搜索')
                 if(this.participantsFilterText != '') {
                     this.filterNodeFlag = true
                     this.http.post("/user/getEmployeeList", {
@@ -3935,15 +3942,15 @@
             }, 1000)
         },
         mounted() {
-
             this.curProjectId = parseInt(this.$route.params.id);
             this.activeName = this.$route.path.split("/")[1];
             this.getDetail();
-
             this.getUsers();
             this.getMyProjectList();
-            
-            this.getInsideData();
+            console.log('action==params='+sessionStorage.action);
+            var action = sessionStorage.action
+            this.getInsideData(action);
+            sessionStorage.removeItem('action');
             this.getGroupTemplate();
         }
     };

+ 152 - 7
fhKeeper/formulahousekeeper/timesheet/src/views/projectApproval/projectApproval.vue

@@ -24,6 +24,8 @@
                 </el-form-item>
                 <el-form-item style="float:right;">
                     <el-link type="primary" :underline="false" @click="handleAdd(-1,null)">{{'新增'}}</el-link>
+                    <el-link type="primary" v-if="permissions.projectApprovalEdit||permissions.projectApprocalImport" style="margin-right:10px;" :underline="false" @click="importDialog=true">{{'导入'}}</el-link>
+                    <el-link type="primary" v-if="permissions.projectApprovalEdit||permissions.projectApprocalExport" style="margin-right:10px;" :underline="false" @click="exportData()">{{'导出'}}</el-link>
                 </el-form-item>
             </el-form>
         </el-col>
@@ -40,7 +42,14 @@
                         <el-table-column type="selection" width="60" :selectable="isSelectable">
                         </el-table-column>
                         <el-table-column prop="projectCode" :label="$t('Itemno')"  width="150"></el-table-column>
-                        <el-table-column prop="projectName" :label="$t('headerTop.projectName')" min-width="250" ></el-table-column>
+                        <el-table-column prop="projectName" :label="$t('headerTop.projectName')" min-width="250" sortable="custom">
+                        <template slot-scope="scope">
+                            <div> 
+                                <el-button type="text" v-if="scope.row.isPublic==0" @click="showProjectApprovalDeatil(scope.row)">{{scope.row.projectName}}</el-button>
+                                <span v-else>{{scope.row.projectName}}</span>
+                            </div>
+                        </template>
+                    </el-table-column>
                         <el-table-column prop="categoryName" :label="$t('projectclassification')"  width="140"></el-table-column>
                         <el-table-column prop="isPublic" :label="'项目类型'"  width="140">
                             <template slot-scope="scope">{{scope.row.isPublic==0?"正式项目":"非项目"}}</template>
@@ -71,10 +80,10 @@
                         </el-table-column>
                         <el-table-column :label="$t('operation')" :width="300" align="left" fixed="right">
                             <template slot-scope="scope">
-                                <el-button size="mini" type="primary" @click="handleAdd(scope.$index, scope.row)">{{'编辑'}}</el-button>
-                                <el-button size="mini" v-if="(permissions.projectApprovalEdit||permissions.projectApprovalCheck) && scope.row.status==0 &&scope.row.canChecked==1 "  @click="check(scope.row,1)">{{'通过'}}</el-button>
-                                <el-button size="mini" v-if="(permissions.projectApprovalEdit||permissions.projectApprovalCheck) && scope.row.status!=2 &&scope.row.canChecked==1" @click="check(scope.row,2)">{{'驳回'}}</el-button>
-                                <el-button size="mini" v-if="(permissions.projectApprovalEdit ||permissions.projectApprovalCheck)&&scope.row.canChecked==1 && scope.row.status==1" @click="check(scope.row,3)">{{'撤销'}}</el-button>
+                                <el-button size="mini" type="primary" v-if="(permissions.projectApprovalEdit||permissions.projectApprovalCheck)&&scope.row.status!=1" @click="handleAdd(scope.$index, scope.row)">{{'编辑'}}</el-button>
+                                <el-button size="mini" v-if="(permissions.projectApprovalEdit||permissions.projectApprovalCheck) && scope.row.status==0"  @click="check(scope.row,1)">{{'通过'}}</el-button>
+                                <el-button size="mini" v-if="(permissions.projectApprovalEdit||permissions.projectApprovalCheck) && scope.row.status==0" @click="check(scope.row,2)">{{'驳回'}}</el-button>
+                                <el-button size="mini" v-if="(permissions.projectApprovalEdit ||permissions.projectApprovalCheck) && scope.row.status==1" @click="check(scope.row)">{{'撤销'}}</el-button>
                             </template>
                         </el-table-column>
                     </el-table>
@@ -283,6 +292,54 @@
                             <el-button type="primary" @click="userDetailVisible = false" >{{ $t('btn.determine') }}</el-button>
                         </div>
                     </el-dialog>
+
+                    <!--查看立项-->
+                    <el-dialog :title="$t('Checkthedetails')" v-if="projectApprovalDetailVisible" :visible.sync="projectApprovalDetailVisible" :close-on-click-modal="false" customClass="customWidth" width="400px">
+                        <div class="line"><span>{{'项目编号:'}}</span><span>{{projectApprocalDetail.projectCode}}</span></div>
+                        <div class="line"><span>{{'项目名称:'}}</span><span>{{projectApprocalDetail.projectName}}</span></div>
+                        <div class="line"><span>{{'项目分类:'}}</span><span>{{projectApprocalDetail.categoryName}}</span></div>
+                        <div class="line"><span>{{'项目类型:'}}</span><span>{{projectApprocalDetail.isPublic==0?"正式项目":"非项目"}}</span></div>
+                        <div class="line"><span>{{'项目描述:'}}</span><span>{{projectApprocalDetail.projectDesc}}</span></div>
+                        <div class="line"><span>{{'全部参与人:'}}</span>
+                            <span v-if="user.userNameNeedTranslate != 1">{{projectApprocalDetail.participationApprovalList.map(v=>v.userName).join(",")}}</span>
+                            <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName' :openid='projectApprocalDetail.participationApprovalList.map(v=>v.userName).join(",")'></ww-open-data></span>
+                        </div>
+                        <div class="line"><span>{{'项目经理:'}}</span>
+                            <span v-if="user.userNameNeedTranslate != 1">{{projectApprocalDetail.inchargerName}}</span>
+                            <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName' :openid='projectApprocalDetail.inchargerName'></ww-open-data></span>
+                        </div>
+                        <div class="line"><span>{{'日报审核人:'}}</span>
+                            <span v-if="user.userNameNeedTranslate != 1">{{projectApprocalDetail.projectApprovalAuditorList.map(v=>v.auditorName).join(",")}}</span>
+                            <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName' :openid='projectApprocalDetail.projectApprovalAuditorList.map(v=>v.auditorName).join(",")'></ww-open-data></span>
+                        </div>
+                        <div class="line"><span>{{'级别:'}}</span><span>{{importanceListLable[projectApprocalDetail.level-1]}}</span></div>
+                        <div class="line"><span>{{'合同金额:'}}</span><span>{{projectApprocalDetail.contractAmount}}</span></div>
+                        <el-divider></el-divider>
+                        <div class="line">
+                            <span>成本基线</span>
+                        </div>
+                        <div class="line">
+                            <span>{{'计划开始日期:'}}</span><span>{{projectApprocalDetail.planStartDate}}</span>
+                            <span>{{'计划结束日期:'}}</span><span>{{projectApprocalDetail.planEndDate}}</span>
+                        </div>
+                        <div class="line" v-for="(item,index) in projectApprocalDetail.projectApprovalBasecostList">
+                            <span>{{item.baseName+":"}}</span><span>{{item.baseAmount}}</span>
+                        </div>
+                        <div slot="footer" class="dialog-footer">
+                            <el-button type="primary" @click="projectApprovalDetailVisible = false" >{{'关闭'}}</el-button>
+                        </div>
+                    </el-dialog>
+                    <el-dialog :title="$t('other.Batchimportofworkinghours')" v-if="importDialog" :visible.sync="importDialog" customClass="customWidth" width="500px">
+                    <p>1. {{$t('other.download')}}
+                    <el-link type="primary" style="margin-left:5px;" :underline="false" :href="'./upload/立项导入模板.xlsx'" :download="'立项导入模板.xlsx'">{{'立项导入模板.xlsx'}}</el-link>
+                    </p>
+                    <p>2. {{$t('other.projectsAndPeopleInThetemplate')}}。</p>
+                    <p style="display: flex;justify-content: center;">
+                        <el-upload ref="upload"  action="#" :limit="1" :http-request="batchImportData" :show-file-list="false">
+                        <el-button type="primary" :underline="false" :loading="importingData">{{$t("other.startImporting")}}</el-button>
+                    </el-upload>
+                    </p>
+                    </el-dialog>
             </div>
         </div>
     </section>
@@ -350,6 +407,9 @@ return {
     }],
     idx: 1,
     tableHeight: 0,
+    projectApprovalDetailVisible:false,
+    projectApprocalDetail:{},
+    importDialog:false,
 }
 },
 computed: {},
@@ -622,6 +682,7 @@ methods: {
             this.participator=theData.participationApprovalList
             this.changeParticipator();
             this.getProjectBaseData(item.id);
+            console.log("===========",this.projectBaseCostData)
             this.getProjectCheckerData(item.id);
             });
         }
@@ -953,10 +1014,18 @@ methods: {
         }
     },
     getProjectBaseData(projectId) {
-        this.http.post('//project-approval-basecost/get',{projectId: projectId},
+        this.http.post('/project-approval-basecost/get',{projectId: projectId},
                 res => {
                     if (res.code == "ok") {
-                        this.projectBaseCostData = res.data;
+                        if(res.data.length>0){
+                            this.projectBaseCostData = res.data;
+                        }else{
+                            this.projectBaseCostData = [];
+                            for (var m=0;m<this.baseCostItemList.length; m++) {
+                                this.projectBaseCostData.push({baseId: this.baseCostItemList[m].id, baseName:this.baseCostItemList[m].name, baseAmount:0});
+                            }
+                        }
+                        
                     } else {
                         this.$message({
                             message: res.msg,
@@ -1133,7 +1202,83 @@ methods: {
             });
             }
         );
+    },
+    showProjectApprovalDeatil(item){
+        this.projectApprovalDetailVisible=true
+        this.http.post('/project-approval/getDetail', {id: item.id},
+        res => {
+        if (res.code == "ok") {
+            this.projectApprocalDetail=res.data
+        }
+        });
+    },
+    batchImportData(item) {
+    //首先判断文件类型
+    let str = item.file.name.split(".");
+    let format = str[str.length - 1];
+    if (format != "xls" && format != "xlsx") {
+        this.$message({
+            message: this.$t('other.PleaseselecttheXLSorXLSXfile'),
+            type: "error"
+        });
+    } else {
+        this.importingData = true;
+        let formData = new FormData();
+        formData.append("multipartFile", item.file);
+        formData.append("companyId", this.user.companyId)
+        this.http.uploadFile('/project-approval/importData', formData,
+        res => {
+            this.$refs.upload.clearFiles();
+            this.importingData = false;
+            if (res.code == "ok") {
+                //换成弹出框,以免有人等了半天回来啥也没看到
+                this.importResultMsg = "成功导入"+res.data+'立项数据'+(res.msg?res.msg:"");;
+                this.getList();
+                this.importDialog = false;
+            } else {
+                this.importResultMsg = this.$t('export.Importfailure')+":"+res.msg;
+            }
+        },
+        error => {
+            this.$refs.upload.clearFiles();
+            this.importingData = false;
+            this.$message({
+                message: error,
+                type: "error"
+            });
+        });
     }
+    },
+    exportData(){
+        let parameter = {
+            keyword:this.keyword,
+            searchField: this.searchField,
+            category:this.statusClf,
+            status: this.status,
+            viewId:this.idx
+        }
+        this.http.post("/project-approval/exportData", parameter,
+        res => {
+            if (res.code == "ok") {
+                const a = document.createElement("a"); // 创建a标签
+                a.setAttribute("download", "立项导出.xlsx"); // download属性
+                a.setAttribute("href", res.data); // href链接
+                a.click(); // 自执行点击事件
+                a.remove();
+            } else {
+                this.$message({
+                message: res.msg,
+                type: "error"
+                });
+            }
+        },
+        error => {
+            this.$message({
+                message: error,
+                type: "error"
+            });
+        });
+    },
 },
 }
 </script>

+ 20 - 22
fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue

@@ -326,27 +326,6 @@
             <el-switch style="margin-left: 40px" v-model="timeType.notAllowedExpiredProject" active-color="#20a0ff" > </el-switch>
             <span style="margin-left:10px;color:#999;">开启后超过项目计划结束日期的日报将不可填报</span>
         </div>
-        <!-- <p style="margin-left:10px;color:#666;" v-if="user.timeType.pushReportData == 1">推送工时设置</p>
-        <el-row :gutter="20" style="padding-top:10px;width:100%;margin:0 auto;padding-left:10px;padding-right:10px;" v-if="user.timeType.pushReportData == 1">
-            <el-col :span="24" >
-                <div class="panel" style="height:70px;color:#606266">
-                    <el-form :inline="true" style="margin-top:10px;">
-                    <el-form-item :label="'每月'">
-                        <el-select v-model="pushParam.day" style="width:80px">
-                            <el-option v-for="item in pushParam.dayList" :key="item" :label="item" :value="item">
-                            </el-option>
-                        </el-select>
-                        <span>日</span>
-                        <el-select v-model="pushParam.hour" style="width:80px;margin-left:20px">
-                            <el-option v-for="item in pushParam.hourList" :key="item" :label="item" :value="item">
-                            </el-option>
-                        </el-select>
-                        <span>时推送一次工时</span>
-                    </el-form-item>
-                    </el-form>
-                </div>
-            </el-col>
-        </el-row> -->
         <!-- 设置研究中心 -->
         <div class="yanjiu">
             <p style="margin-left:10px;color:#666;">{{ $t('dailyfilling') }}</p>
@@ -424,6 +403,14 @@
             </el-col>
         </el-row>
         <div class="yanjiu" v-if="user.company.packageProject==1">
+            <span style="margin:0px 0 10px 10px;color:#666;">填报日报是否隐藏阶段</span>
+            <el-switch style="margin:0px 0 10px 81px;" v-model="timeType.hideStages" active-color="#20a0ff"></el-switch>
+        </div>
+        <div class="yanjiu" v-if="user.company.packageProject==1">
+            <span style="margin:0px 0 10px 10px;color:#666;">填报日报是否隐藏任务</span>
+            <el-switch style="margin:0px 0 10px 81px;" v-model="timeType.hideTask" active-color="#20a0ff" @change="onHideTask()"></el-switch>
+        </div>
+        <div class="yanjiu" v-if="user.company.packageProject==1 && !timeType.hideTask">
             <span style="margin:0px 0 10px 10px;color:#666;">任务是否必填</span>
             <el-switch style="margin:0px 0 10px 81px;" v-model="timeType.taskRequired" active-color="#20a0ff"></el-switch>
             <span style="margin:0px 0 10px 10px;color:#999;">{{ $t('parametermandatory') }}</span>
@@ -693,6 +680,8 @@
                     includeWeekends: false,
                     workOvertimeNeedCheck: false,
                     maxReportTime: 8,
+                    hideStages: false,
+                    hideTask: false,
                 },
                 customDegreeActive: false,
                 needDeptAudit: false,
@@ -764,6 +753,12 @@
             }, 1000)
         },
          methods: {
+            onHideTask() {
+                if (this.timeType.hideTask) {
+                    //任务必填属性变为false
+                    this.timeType.taskRequired = false;
+                }
+            },
             onNonWorkDaySet() {
                 if (this.timeType.notAllowedOnNonWorkday) {
                     this.timeType.includeWeekends = false;
@@ -1338,7 +1333,8 @@
                         param.customDataMaxStatus = param.customDataMaxStatus && param.customDataActive ? 1 : 0
 
                         param.workContentState = param.workContentState ? 1 : 0
-
+                        param.hideStages = param.hideStages ? 1 : 0
+                        param.hideTask = param.hideTask ? 1 : 0
                         param.taskRequired = param.taskRequired ? 1 : 0
 
                         var oneseet = 0
@@ -1406,6 +1402,8 @@
                             this.timeType.stopReport = this.timeType.stopReport? true: false;
                             this.timeType.notAllowedOnNonWorkday = this.timeType.notAllowedOnNonWorkday? true: false;
                             this.timeType.notAllowedExpiredProject = this.timeType.notAllowedExpiredProject? true: false;
+                            this.timeType.hideStages = this.timeType.hideStages? true: false;
+                            this.timeType.hideTask = this.timeType.hideTask? true: false;
                             this.timeType.taskRequired = this.timeType.taskRequired? true: false;
                             // this.timeType.alertNonWorkday = this.timeType.alertNonWorkday ? true : false
 

+ 16 - 18
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -480,9 +480,9 @@
                             </div>
                         </el-form-item>
                     
-                        <el-form-item :label="user.companyId==781? $t('screening.workTasks') : $t('screening.inputProject')" :prop="'domains.' + index + '.projectId'"
-                            :rules="{ required: true, message: user.companyId==781? $t('defaultText.pleaseSelectaJob'): $t('defaultText.pleaseSelectSnItem'), trigger: ['change','blur'] }">
-                            <el-select v-model="domain.projectId" :placeholder="user.companyId==781?$t('defaultText.pleaseSelectaJob'): $t('defaultText.pleaseSelectSnItem')" style="width:200px;" clearable="true"  filterable="true" value-key="id"
+                        <el-form-item :label="$t('screening.inputProject')" :prop="'domains.' + index + '.projectId'"
+                            :rules="{ required: true, message: $t('defaultText.pleaseSelectSnItem'), trigger: ['change','blur'] }">
+                            <el-select v-model="domain.projectId" :placeholder="$t('defaultText.pleaseSelectSnItem')" style="width:200px;" clearable="true"  filterable="true" value-key="id"
                             @change="selectProject(domain, index)"
                             :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)" popper-class="projectSelectPopperClass">
                                 <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
@@ -523,7 +523,7 @@
                             <!-- <el-link type="primary" v-if="canEdit"
                                 :underline="false" style="margin-left:10px;" @click="copyProject(index)">复制</el-link> -->
                         </el-form-item>
-                        <el-form-item :label="user.companyId==781? $t('other.describe'):$t('other.projectDescription')" v-if="user.company.packageProject==0&&domain.projectId&&fillProjectList.filter(p=>p.id == domain.projectId).length>0&&fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc">
+                        <el-form-item :label="$t('other.projectDescription')" v-if="user.company.packageProject==0&&domain.projectId&&fillProjectList.filter(p=>p.id == domain.projectId).length>0&&fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc">
                             <span>{{fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc}}</span>
                         </el-form-item>
                         <!--如果设置了工时成本预警的预算成本项-->
@@ -554,7 +554,7 @@
                                     </el-option>
                                 </el-select>
                             </template>
-                            <template v-else><!-- 项目的阶段 -->
+                            <template v-else-if="!user.timeType.hideStages"><!-- 项目的阶段 -->
                                 <span v-if="domain.stages != null && domain.stages.length> 0" style="margin-left:45px;">{{$t('other.inputStage')}}</span>
                                 <el-select v-model="domain.stage" :placeholder="$t('defaultText.pleaseChoose')" style="width:200px;margin-left:10px;" 
                                 clearable="true" v-if="domain.stages != null && domain.stages.length> 0"
@@ -568,7 +568,7 @@
                             <el-form-item v-if="user.timeType.reportAuditType != 3 && user.timeType.reportAuditType != 5 && domain.auditUserList.length > 0">
                                 <template slot="label">
                                     <span style="color:#FF0000;">*</span>
-                                    <span >{{user.companyId==781? $t('other.reviewer') : $t('other.projectAuditor')}}</span>
+                                    <span >{{$t('other.projectAuditor')}}</span>
                                 </template>
                                 
                                 <el-select v-model="domain.projectAuditorId" :disabled="!domain.canEdit" @change="$forceUpdate()" v-if="user.userNameNeedTranslate != '1'" style="width:200px;">
@@ -676,7 +676,7 @@
                             </span>
                         </el-form-item>
                         <!--项目管理专业版模式下,项目下的近期执行的任务 -->
-                        <el-form-item :label="$t('other.task')+' / '+$t('other.milestone')" :prop="'domains.' + index + '.taskId'" v-if="user.company.packageProject==1&& (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic!=1))" >
+                        <el-form-item :label="$t('other.task')+' / '+$t('other.milestone')" :prop="'domains.' + index + '.taskId'" v-if="user.company.packageProject==1&& !user.timeType.hideTask && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic!=1))" >
                             <template slot="label" v-if="user.timeType.taskRequired == 1">
                                 <span style="color:#FF0000;">*</span>
                                 <span >任务</span>
@@ -696,8 +696,8 @@
                             </el-select>
                         </el-form-item>
                         <!--工作事项-->
-                        <el-form-item :label="user.companyId==781? $t('other.specificContentAndResults') : $t('other.workMatters') " :prop="'domains.' + index + '.content'" 
-                        :rules="user.timeType.workContentState == 1 ? { required: true, message: user.companyId==781? $t('other.tianspecificContentAndResults'):$t('other.tianworkMatters'), trigger: 'blur' } : null">
+                        <el-form-item :label="$t('other.workMatters') " :prop="'domains.' + index + '.content'" 
+                        :rules="user.timeType.workContentState == 1 ? { required: true, message: $t('other.tianworkMatters'), trigger: 'blur' } : null">
                             <el-input v-model="domain.content" type="textarea" :rows="4" :placeholder="$t('defaultText.pleaseFillOut')" clearable style="width:75%;margin-right:7%"
                             :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"></el-input>
                         </el-form-item>
@@ -763,7 +763,7 @@
                                 <!-- <el-link type="primary" v-if="canEdit"
                                     :underline="false" style="margin-left:10px;" @click="copyProject(index)">复制</el-link> -->
                             </el-form-item>
-                            <el-form-item :label="user.companyId==781?$t('other.describe'):$t('other.projectDescription')" v-if="user.company.packageProject==0&&domain.projectId&&fillProjectList.filter(p=>p.id == domain.projectId).length>0&&fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc">
+                            <el-form-item :label="$t('other.projectDescription')" v-if="user.company.packageProject==0&&domain.projectId&&fillProjectList.filter(p=>p.id == domain.projectId).length>0&&fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc">
                                 <span>{{fillProjectList.filter(p=>p.id == domain.projectId)[0].projectDesc}}</span>
                             </el-form-item>
                             <!--如果设置了工时成本预警的预算成本项-->
@@ -1049,7 +1049,7 @@
                         <el-option v-for="item in zhoBao.taskGroups" :key="item.id" :label="item.name" :value="item.id"></el-option>
                     </el-select>
                 </div>
-                <div class="zhoFel" v-if="user.company.packageProject == 1 && zhoBao.stages != null && zhoBao.stages.length> 0">
+                <div class="zhoFel" v-if="user.company.packageProject == 1 && !user.timeType.hideStages && zhoBao.stages != null && zhoBao.stages.length> 0">
                     <p>{{$t('other.inputStage')}}</p>
                     <el-select v-model="zhoBao.stage" :placeholder="$t('defaultText.pleaseChoose')" style="width: 355px" clearable="true" @change="getWeeklyTaskList(zhoBao)">
                             <el-option v-for="item in zhoBao.stages" :key="item.id" :label="item.stagesName" :value="item.stagesName"></el-option>
@@ -1117,7 +1117,7 @@
                     <el-input v-model="zhoBao.customText" type="textarea" :rows="1" style="width:355px;" maxlength="1000" show-word-limit></el-input>
                 </div>
                 <!-- 选择任务 -->
-                <div class="zhoFel" v-if="user.company.packageProject==1 && user.company.nonProjectSimple==0">
+                <div class="zhoFel" v-if="user.company.packageProject==1 && !user.timeType.hideTask && user.company.nonProjectSimple==0">
                     <p>任务 / 里程碑</p>
                     <el-select v-model="zhoBao.taskId" :placeholder="'请选择'" clearable="true" style="width: 355px" filterable @change="$forceUpdate()">
                         <el-option v-for="item in zhoBao.taskList" :key="item.taskId" :label="item.taskName" :value="item.taskId"></el-option>
@@ -1160,11 +1160,8 @@
                     </el-select>
                 </el-form-item>
                 <el-form-item prop="departmentId" :label="$t('other.selectdepartment')" v-if="permissions.reportsCompany">
-
                     <el-cascader v-if="user.userNameNeedTranslate != 1" v-model="exportParam.departmentId" :placeholder="$t('other.allDepartments')" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 350px;"></el-cascader>
-
                     <vueCascader :size="'medium'" :widthStr="'350'" :clearable="true" :subjectId="exportParam.departmentId" :subject="option" :radios="true" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')"></vueCascader>
-
                 </el-form-item>
                 <el-form-item prop="projectId" :label="$t('time.dateRange')">
                     <el-date-picker
@@ -5424,8 +5421,9 @@
                 }
                 param.stateKey = this.stateKey
                 // param.departmentId = this.user.departmentId
-                console.log(param, '《=== 导出传的参数')
-                this.http.post( this.port.report.export, param,
+                console.log(param, '《=== 导出传的参数');
+                console.log('开始调用exportReport接口');
+                this.http.post(this.port.report.export, param,
                 res => {
                     this.exportingData = false;
                     if (res.code == "ok") {
@@ -6088,7 +6086,7 @@
                     errtips += this.user.timeType.customTextName + '、'
                 }
                 if(this.user.timeType.workContentState == 1 && !this.zhoBao.content){
-                    errtips +=  this.user.companyId==781?this.$t('other.specificcontentandresults')+'、': this.$t('other.workMatters') +'、'
+                    errtips +=  this.$t('other.workMatters') +'、'
                 }
                 if(errtips){
                     errtips = errtips.substring(0,errtips.length - 1)

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue

@@ -130,7 +130,7 @@
                     </van-popup>
                     <!--任务阶段 -->
                     <van-field readonly name="stage"
-                        v-if="user.companyId != yuzhongCompId && user.company.packageProject == 1 && item.stages != null && item.stages.length > 0"
+                        v-if="user.companyId != yuzhongCompId && user.company.packageProject == 1 && !user.timeType.hideStages && item.stages != null && item.stages.length > 0"
                         clickable :value="item.stage" label="投入阶段" placeholder="请选择投入阶段"
                         @click="clickPickStage(index, item)" />
                     <van-popup v-model="item.showPickerStage" position="bottom">
@@ -277,7 +277,7 @@
                             <van-stepper v-model="pItem.progress" integer min="0" max="100" />%
                         </template>
                     </van-field>
-                    <van-field v-if="user.company.packageProject == 1" readonly name="taskId" :value="item.taskName"
+                    <van-field v-if="user.company.packageProject == 1 && !user.timeType.hideTask && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic!=1))" readonly name="taskId" :value="item.taskName"
                         :rules="[{ required: reportTimeType.taskRequired? true : false, message: '请选择任务/里程碑' }]" label="任务/里程碑"
                         placeholder="请选择任务/里程碑" @click="clickPickerTask(index, item)"></van-field>
                     <van-field v-if="user.companyId == 3092" readonly name="sapServiceId" :value="item.sapServiceName"

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue

@@ -93,7 +93,7 @@
                             </div>
                         </van-popup>
                         <!--任务阶段 -->
-                        <van-field  readonly  name="stage" v-if="user.company.packageProject==1&&item.stages != null && item.stages.length > 0 && user.timeType.enableNewWeeklyfill != 1" clickable 
+                        <van-field  readonly  name="stage" v-if="user.company.packageProject==1 && !user.timeType.hideStages &&item.stages != null && item.stages.length > 0 && user.timeType.enableNewWeeklyfill != 1" clickable 
                             :value="item.stage" label="投入阶段" placeholder="请选择投入阶段"  :disabled="item.state<=1"
                         @click="clickPickStage(index, item)" />
                         <van-popup v-model="item.showPickerStage" position="bottom">
@@ -204,7 +204,7 @@
 
                         <!-- 任务里程碑 -->
                         <van-field :label="'任务/里程碑'" :value="item.taskName" :disabled="item.state<=1" readonly 
-                            clickable v-if="user.company.packageProject == 1 && user.company.nonProjectSimple==0" 
+                            clickable v-if="user.company.packageProject == 1 && !user.timeType.hideStages && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic!=1))" 
                             @click="clickTakKer(index, item)"
                         />
                         <van-popup v-model="item.showTaksDegree" position="bottom" @click-overlay="overlayPopup(index, 'showTaksDegree')">