Browse Source

Merge remote-tracking branch 'origin/master'

yusm 11 months ago
parent
commit
2ca992933c
46 changed files with 1585 additions and 548 deletions
  1. 8 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/components/relatedProducts/relatedProducts.vue
  2. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/api.ts
  3. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  4. 4 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/attachment.vue
  5. 28 12
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/stageSetting.vue
  6. 27 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  7. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts
  8. 120 44
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/attachment.vue
  9. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/deteleTables.vue
  10. 164 28
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/information.vue
  11. 13 33
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/operationRecord.vue
  12. 170 57
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedBusiness.vue
  13. 1 43
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedTasks.vue
  14. 9 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/detail/index.vue
  15. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/index.vue
  16. 7 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue
  17. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/api.ts
  18. 157 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue
  19. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/product/index.vue
  20. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/constant.ts
  21. 14 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue
  22. 122 16
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java
  23. 1 33
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SalesOrderController.java
  24. 5 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java
  25. 7 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/OrderProductDetail.java
  26. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Product.java
  27. 5 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Stage.java
  28. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/mapper/BusinessOpportunityMapper.java
  29. 6 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/BusinessOpportunityService.java
  30. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ClueService.java
  31. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/SalesOrderService.java
  32. 4 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/StageService.java
  33. 234 26
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/BusinessOpportunityServiceImpl.java
  34. 85 25
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ClueServiceImpl.java
  35. 5 10
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/CustomServiceImpl.java
  36. 48 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java
  37. 37 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/StageServiceImpl.java
  38. 2 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/OrderProductDetailMapper.xml
  39. 4 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  40. 129 91
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserWithBeisenController.java
  41. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  42. 30 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  43. 65 47
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  44. 1 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  45. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json
  46. 46 12
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

+ 8 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/components/relatedProducts/relatedProducts.vue

@@ -64,6 +64,7 @@ import { ref, reactive, onMounted, inject, watchEffect } from "vue";
 const props = defineProps<{
     productTableList: any,
     height?: string,
+    productTableListValue?: any
 }>()
 
 const productTable: any = ref([{}])
@@ -114,15 +115,20 @@ function deteleTableItem(index: number) {
 function returnData() {
     let jsonstr = JSON.stringify(productTable.value)
     let json = '[{"index":0}]'
-    if(jsonstr == json) {
+    if (jsonstr == json) {
         return false
     }
+    productTable.value.forEach((item: any) => {
+        delete item.index
+    });
     return productTable.value
 }
 
 watchEffect(() => {
-    const { productTableList, height } = props
+    const { productTableList, height, productTableListValue = [] } = props
+    console.log(productTableListValue, '<==== 数据')
     productArrar.value = productTableList || []
+    productTable.value = productTableListValue.length > 0 ? productTableListValue : [{ index: 0 }]
     heightClass.value = !height ? '200px' : height
 });
 

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

@@ -2,6 +2,9 @@ export const SENDVCODE = "/user/sendVcode";     //发送验证码
 export const REGISTER = "/user/insertCompany";  //注册
 export const LOGIN = "/user/loginAdmin";        //登录
 export const IMPORTTIMELIST = "/sys-form/getExportTemplate" // 下载模板
+export const URL_UPLOADFILE = `/contacts-document/fileUpload` // 上传文件
+export const URLFILEDETELE = `/contacts-document/fileDelete` // 删除文件
+export const URL_REFNAME = `/contacts-document/reNameFile` // 文件重命名
 
 export const SEX: sexTYpe[] = [
     { label: "男", value: '1' },

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

@@ -19,6 +19,7 @@ export const DETELEFILEFILE = `/business-opportunity/deleteFile`
 export const REFIENAMEFILE = `/business-opportunity/reFileName`
 export const UPLOADFILEFILE = `/business-opportunity/uploadFile`
 export const URL_IMPOERBUSINESS = `/business-opportunity/importData`
+export const URL_DETELESTAGE = `/business-opportunity/deleteStage`
 
 
 export const stageStatus = [

+ 4 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/attachment.vue

@@ -61,6 +61,7 @@ const attachmenttable = ref([])
 const information = ref<any>({})
 const renameDialogVisible = ref(false)
 const renameVal = ref('')
+const uploadRef = ref<any>()
 
 // 下载文件
 function fileDownload(item: any) {
@@ -106,7 +107,9 @@ async function httpUploadFile(param: UploadRequestOptions) {
     const formData = new FormData();
     formData.append('file', param.file)
     formData.append('id', bussinessId)
-    const res = await uploadFile(UPLOADFILEFILE, formData)
+    const res = await uploadFile(UPLOADFILEFILE, formData).finally(() => {
+        uploadRef.value.clearFiles()
+    })
     if (res.code == 'ok') {
         globalPopup?.showSuccess(res.msg || '')
         emits('refreshData');

+ 28 - 12
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/stageSetting.vue

@@ -6,7 +6,7 @@
                     <h4 :id="titleId">阶段设置</h4>
                     <div>
                         <el-button type="primary" @click="addStage(false)">新增</el-button>
-                        <el-button type="primary" @click="saveState()" v-loading="allLoading.saveLoading">保存</el-button>
+                        <el-button type="primary" @click="saveState()" :loading="allLoading.saveLoading">保存</el-button>
                         <el-button @click="cancel()">取消</el-button>
                     </div>
                 </div>
@@ -29,7 +29,7 @@
                         <el-table-column label="操作" fixed="right" width="200">
                             <template #default="scope">
                                 <el-button link type="primary" size="large" @click="addStage(scope.row)">编辑</el-button>
-                                <el-button link type="danger" size="large" @click="deteStage(+scope.$index)">删除</el-button>
+                                <el-button link type="danger" size="large" @click="deteStage(+scope.$index, scope.row)" :disabled="scope.row.isFinish == 1">删除</el-button>
                                 <el-button link type="primary" size="large" @click="moveStage(+scope.$index, 'up')"
                                     v-if="scope.$index != 0">上移</el-button>
                                 <el-button link type="primary" size="large" @click="moveStage(+scope.$index, 'down')"
@@ -75,7 +75,7 @@
 <script lang="ts" setup>
 import { post } from '@/utils/request';
 import { ref, reactive, onMounted, watch, inject } from 'vue'
-import { BUSIESS_GETSATE, BUSIESS_SAVESAIE } from '../api';
+import { BUSIESS_GETSATE, BUSIESS_SAVESAIE, URL_DETELESTAGE } from '../api';
 import { List } from 'echarts';
 
 type moveStageType = 'up' | 'down';
@@ -126,7 +126,7 @@ function saveState() {
         }
     })
     allLoading.saveLoading = true
-    post(BUSIESS_SAVESAIE, { list: JSON.stringify(data) }).then(() => {
+    post(BUSIESS_SAVESAIE, { stages: JSON.stringify(data) }).then(() => {
         globalPopup?.showSuccess('保存成功')
         cancel()
     }).finally(() => {
@@ -139,7 +139,7 @@ function editState(flag: boolean) {
         globalPopup?.showWarning('请输入阶段名称')
         return
     }
-    const listIndex = stageTableList.value.findIndex((item: stageFormType) => item.name == stageForm.name)
+    const listIndex = stageTableList.value.findIndex((item: stageFormType) => item.seq == stageForm.seq)
     const newStage = {
         ...stageForm,
         plan: stageForm.plan,
@@ -158,14 +158,19 @@ function editState(flag: boolean) {
 
 function addStage(item: any) {
     const row = JSON.parse(JSON.stringify(item))
+    console.log(item)
     if (!item) {
         resetStage()
     } else {
-        Object.assign(stageForm, {
+        let newData: any = {
             name: row.name,
             plan: row.plan,
-            seq: row.seq
-        })
+            seq: row.seq,
+            ...(row.id && { id: row.id }),
+            ...(row.companyId && { companyId: row.companyId }),
+            ...(row.isFinish && { isFinish: row.isFinish })
+        }
+        Object.assign(stageForm, newData)
     }
     allVisible.editVisible = true
 }
@@ -173,11 +178,16 @@ function addStage(item: any) {
 function resetStage() {
     let newData = JSON.parse(JSON.stringify(stageTableList.value))
     let maxnum = newData.sort((a: any, b: any) => { return b.seq - a.seq; })[0];
-    Object.assign(stageForm, {
+    console.log(maxnum)
+    let formVal = {
         name: '',
         plan: 0,
-        seq: +maxnum + 1
-    })
+        seq: +maxnum.seq + 1
+    }
+    delete stageForm.id
+    delete stageForm.isFinish
+    Object.assign(stageForm, formVal);
+    console.log(stageForm, '<=== 新增的数据', formVal)
 }
 
 function moveStage(index: number, stageType: moveStageType) {
@@ -191,7 +201,13 @@ function moveStage(index: number, stageType: moveStageType) {
     }
 }
 
-function deteStage(index: number) {
+function deteStage(index: number, row: any) {
+    if(row.id) {
+        post(URL_DETELESTAGE, { id: row.id }).then(() => {
+            stageTableList.value.splice(index, 1)
+        })
+        return
+    }
     stageTableList.value.splice(index, 1)
 }
 

+ 27 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -47,7 +47,7 @@
     <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" @click="showVisible('newBusinessisible')">新建商机</el-button>
+          <el-button type="primary" @click="editNewBusiness(false)">新建商机</el-button>
           <el-button type="primary" @click="showVisible('batchTransferVisible')"
             :disabled="batchTableData.length <= 0">批量转移</el-button>
           <el-button type="primary" @click="batchDeteleItem()" :disabled="batchTableData.length <= 0">批量删除</el-button>
@@ -69,7 +69,7 @@
             <el-table-column label="操作" fixed="right" width="200">
               <template #default="scope">
                 <el-button link type="primary" size="large"
-                  @click="editShowVisible('newBusinessisible', scope.row)">编辑</el-button>
+                  @click="editNewBusiness(scope.row)">编辑</el-button>
                 <el-button link type="primary" size="large" @click="newTask(scope.row)">新建任务</el-button>
                 <el-button link type="danger" size="large"
                   @click="businessDeteleItem(scope.row.id, scope.row.name)">删除</el-button>
@@ -98,8 +98,8 @@
           </div>
         </div>
       </template>
-      <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
-        <GenerateForm ref="generateForm" :data="generateFormData" />
+      <div class="h-[60vh] overflow-y-auto scroll-bar pt-3" v-loading="allLoading.generateFormLading">
+        <GenerateForm ref="businessTemplateRef" :data="businessTemplate" :value="businessTemplateValue" :key="businessTemplateKey" />
         <div>相关产品</div>
         <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" />
       </div>
@@ -182,9 +182,11 @@ const router = useRouter()
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const businessTableRef = ref<InstanceType<typeof ElTable>>() // 商机table dom
 const businessTotalTable = ref(0)
-const generateForm = ref<typeof GenerateForm>() // 自定义表单dom
+const businessTemplateRef = ref<typeof GenerateForm>() // 自定义表单dom
 const relatedProductsRef = ref<typeof RelatedProducts>()
-const generateFormData = ref({
+const businessTemplateValue = ref({})
+const businessTemplateKey = ref(1)
+const businessTemplate = ref({
   config: {},
   list: []
 }) // 自定义表单数据
@@ -196,6 +198,7 @@ const allLoading = reactive({
   transferLoading: false,
   importLoading: false,
   exoprtLoading: false,
+  generateFormLading: false
 })
 const allVisible = reactive({
   newBusinessisible: false,
@@ -236,7 +239,7 @@ const productTableList = ref([])
 
 
 function editBusiness(visibles: boolean) {
-  generateForm.value?.getData().then((res: any) => {
+  businessTemplateRef.value?.getData().then((res: any) => {
     let productTableListData = relatedProductsRef?.value?.returnData()
     let newForm = {
       ...res,
@@ -257,9 +260,23 @@ function editBusiness(visibles: boolean) {
   })
 }
 
-function editShowVisible(type: keyof typeof allVisible, item: any) {
+function editNewBusiness(item: any) {
   console.log(item, '选择数据')
-  showVisible(type)
+  showVisible('newBusinessisible')
+  allLoading.generateFormLading = true
+  if (item) {
+    businessTemplateValue.value = item
+    allText.newBusinessisibleText = '编辑商机'
+  }
+  if (!item) {
+    businessTemplateValue.value = {}
+    allText.newBusinessisibleText = '新建商机'
+  }
+  setTimeout(() => {
+    businessTemplateRef.value && businessTemplateRef.value.reset()
+    businessTemplateKey.value++
+    allLoading.generateFormLading = false
+  }, 500)
 }
 
 function newTask(item: any) {
@@ -419,7 +436,7 @@ async function getSystemField() {
   })
 
   const res = await get(GETGENERATEFOEM)
-  generateFormData.value = JSON.parse(res.data[0].config)
+  businessTemplate.value = JSON.parse(res.data[0].config)
 }
 
 function toBusinessTableDetail(row: any) {

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts

@@ -17,6 +17,7 @@ export const URL_DETELEITEM = `${URL}/confirmDeleteContacts`
 export const URL_RESTORE = `${URL}/returnContacts`
 export const URL_GETALL = `${URL}/getAllContacts`
 export const URL_GETDETAIL = `${URL}/getContactsDetail`
+export const URL_TRANSFERCONTACTS = `${URL}/transferContacts`
 
 export const actionButtons: any[] = [
     { text: '新建联系人' },

+ 120 - 44
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/attachment.vue

@@ -3,64 +3,140 @@
         <div class="flex justify-between">
             <div class="title">附件</div>
             <div>
-                <el-button type="primary">上传</el-button>
+                <el-upload ref="uploadRef" :http-request="httpUploadFile" :limit="1" :show-file-list="false"
+                    element-loading-text="正在上传" :loading="allLoading.uploadFileLoading">
+                    <template #trigger>
+                        <el-button type="primary">上传</el-button>
+                    </template>
+                </el-upload>
             </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 prop="documentName" label="附件名称" width="180" />
+                <el-table-column prop="size" label="附件大小" width="120" />
+                <el-table-column prop="creatorName" label="上传人" width="120" />
+                <el-table-column prop="indate" 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>
+                        <el-button link type="primary" size="large" @click="fileDownload(scope.row)">下载</el-button>
+                        <el-button link type="primary" size="large" @click="operation(scope.row)">重命名</el-button>
+                        <el-button link type="danger" size="large" @click="fileDetele(scope.row)">删除</el-button>
                     </template>
                 </el-table-column>
             </el-table>
         </div>
+
+        <!-- 弹窗 -->
+        <el-dialog v-model="allVisible.renameDialogVisible" width="800" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">{{ '文件重命名' }}</h4>
+                    <div>
+                        <el-button type="primary" @click="saveEditClue()" :loading="allLoading.saveLoading">保存</el-button>
+                        <el-button @click="allVisible.renameDialogVisible = false">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="pt-3">
+                <el-input v-model.trim="fileFormVal.name" style="width: 100%" class="pb-3" clearable />
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
+import { post, uploadFile } from '@/utils/request';
+import { UploadRequestOptions } from 'element-plus';
 import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { URLFILEDETELE, URL_REFNAME, URL_UPLOADFILE } from '@/pages/api';
+import { downloadFile } from '@/utils/tools';
+
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+const props = defineProps<{
+    data: any
+}>()
+
+type fileFormVal = {
+    id?: string,
+    name?: string
+}
+
+const uploadRef = ref<any>()
+const information = ref<any>({})
+const attachmenttable = ref([])
+const fileTypeStr = ref('') // 文件重命名的类型
+const fileFormVal = ref<fileFormVal>({})
+const allLoading = reactive({
+    uploadFileLoading: false,
+    saveLoading: false
+})
+const allVisible = reactive({
+    renameDialogVisible: false
+})
+
+function saveEditClue() {
+    if(!fileFormVal.value.name) {
+        globalPopup?.showWarning('请输入文件名称')
+        return
+    }
+    allLoading.saveLoading = true
+    post(URL_REFNAME, {
+        fileId: fileFormVal.value.id,
+        newName: fileFormVal.value.name + '.' + fileTypeStr.value
+    }).then(() => {
+        allVisible.renameDialogVisible = false
+        globalPopup?.showSuccess('重命名成功')
+        emits('refreshData');
+    }).finally(() => {
+        allLoading.saveLoading = false
+    })
+}
+
+function operation(item: any) {
+    fileTypeStr.value = item.documentName.split('.').pop()
+    fileFormVal.value = {
+        id: item.id,
+        name: item.documentName.replace(/\.[^/.]+$/, '')
+    }
+    allVisible.renameDialogVisible = true
+}
+
+function fileDownload(item: any) {
+    downloadFile(`${item.url}`, item.documentName)
+}
+
+function fileDetele(item: any) {
+    post(URLFILEDETELE, { fileIds: item.id }).then(() => {
+        globalPopup?.showSuccess('删除成功')
+        emits('refreshData');
+    })
+}
+
+// 上传附件
+async function httpUploadFile(param: UploadRequestOptions) {
+    const id = information.value.id
+    const formData = new FormData();
+    formData.append('file', param.file)
+    formData.append('contactsId', id)
+    allLoading.uploadFileLoading = true
+    const res = await uploadFile(URL_UPLOADFILE, formData).finally(() => {
+        allLoading.uploadFileLoading = false
+        uploadRef.value.clearFiles()
+    })
+    if (res.code == 'ok') {
+        globalPopup?.showSuccess(res.msg || '')
+        emits('refreshData');
+        return
+    }
+    globalPopup?.showError(res.msg || '')
+    return res
+}
 
-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',
-}])
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    attachmenttable.value = data.contactsDocumentList || []
+});
 </script>
 <style scoped lang="scss"></style>

+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/deteleTables.vue

@@ -3,7 +3,7 @@
         top="10vh">
         <template #header="{ close, titleId, titleClass }">
             <div class="flex justify-between items-center border-b pb-3 dialog-header">
-                <h4 :id="titleId">商机回收站</h4>
+                <h4 :id="titleId">联系人回收站</h4>
                 <div>
                     <el-button type="primary" v-loading="allLoading.batchRecoveryLoading" :disabled="batchTableData.length == 0"
                         @click="batchOperation('恢复')">批量恢复</el-button>
@@ -118,8 +118,8 @@ function getTableList() {
     allLoading.tableLoading = true
     post(URL_RECYCLELIST, { ...tableForm }).then((res) => {
         if (res.code == 'ok') {
-            const { data, total } = res.data
-            deteleBusinessTable.value = data.map((item: any) => {
+            const { records, total } = res.data
+            deteleBusinessTable.value = records.map((item: any) => {
                 return {
                     ...item,
                     expectedTransactionDate: formatDate(new Date(item.expectedTransactionDate))

+ 164 - 28
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/information.vue

@@ -3,8 +3,9 @@
         <div class="flex justify-between">
             <div class="title">基本信息</div>
             <div>
-                <el-button type="primary">转移</el-button>
-                <el-button type="primary">编辑</el-button>
+                <el-button type="primary" v-if="!info.ownerId">认领</el-button>
+                <el-button type="primary" v-else @click="operationCli(false)">转移</el-button>
+                <el-button type="primary" @click="editInfo(info)">编辑</el-button>
             </div>
         </div>
         <div class="form flex flex-wrap justify-between">
@@ -13,54 +14,189 @@
                 <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ item.value }}</div>
             </div>
         </div>
+
+        <el-dialog v-model="allVisible.editContactsVisible" width="1000" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">编辑联系人</h4>
+                    <div>
+                        <el-button type="primary" @click="editContactsSave()"
+                            :loading="allLoading.editContactsSaveLoading">保存</el-button>
+                        <el-button @click="closeVisible('editContactsVisible')">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
+                <div class="ml-4 mr-4">
+                    <GenerateForm ref="contactsTemplateRef" :data="contactsTemplate" :value="contactsTemplateValue"
+                        :key="contactsTemplateRefKey" v-loading="allLoading.contactsTemplateRefLoading" />
+                </div>
+            </div>
+        </el-dialog>
+
+        <el-dialog v-model="allVisible.transferVisible" width="600" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">{{ allText.operationText }}</h4>
+                    <div>
+                        <el-button type="primary" :loading="allLoading.transferLoading"
+                            @click="transferBusiness(true)">转移</el-button>
+                        <el-button @click="allVisible.transferVisible = false">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="scroll-bar m-6">
+                <div class="flex mb-4">
+                    <div class="w-20 flex items-center justify-end pr-4">转移至:</div>
+                    <el-select v-model="transferValue" placeholder="请选择" class="flex1">
+                        <el-option v-for="item in transferOptions" :key="item.value" :label="item.label"
+                            :value="item.value" />
+                    </el-select>
+                </div>
+                <div class="pl-3 text-[#e94a4a]">转移后,将看不到此联系人</div>
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
 import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { GenerateForm } from '@zmjs/form-design';
+import { getFromValue, getTemplateKey } from '@/utils/tools';
+import { GETGENERATEFOEM, GETPERSONNEL, URL_TRANSFERCONTACTS, URL_UPLOAD } from '../api';
+import { get, post } from '@/utils/request';
 
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
 const props = defineProps<{
     data: any
 }>()
-
+const allLoading = reactive({
+    contactsTemplateRefLoading: false,
+    editContactsSaveLoading: false,
+    transferLoading: false
+})
+const allVisible = reactive({
+    editContactsVisible: false,
+    transferVisible: false
+})
+const allText = reactive({
+    operationText: '认领联系人'
+})
+const contactsTemplate = ref({
+    list: [],
+    config: {}
+})
+const contactsTemplateValue = ref({})
+const contactsTemplateRef = ref<typeof GenerateForm>()
+const contactsTemplateRefKey = ref(1)
 const info: any = ref({})
+const transferValue = ref('')
+const transferOptions = ref<optionType[]>([])
+
+function operationCli(flag: boolean) {
+    transferValue.value = flag ? '' : info.value.ownerId
+    allText.operationText = flag ? '认领联系人' : '转移联系人'
+    showVisible('transferVisible')
+}
+
+function transferBusiness(flag: boolean) {
+    const url = flag ? URL_TRANSFERCONTACTS : ''
+    const formVal = flag ? { id: info.value.id, ownerId: transferValue.value } : {}
+    allLoading.transferLoading = true
+    post(url, { ...formVal }).then((_res) => {
+        closeVisible('transferVisible')
+        globalPopup?.showSuccess('转移成功')
+        emits('refreshData')
+    }).finally(() => {
+        allLoading.transferLoading = false
+    })
+}
+
+function editInfo(row: any) {
+    showVisible('editContactsVisible')
+    const templateKey = getTemplateKey(contactsTemplate.value.list)
+    const formVal: templateKey = { id: row.id }
+    for (let i in templateKey) {
+        if (row[templateKey[i]] || row[templateKey[i]] == 0) {
+            formVal[templateKey[i]] = templateKey[i] == 'sex' ? row[templateKey[i]] + '' : row[templateKey[i]]
+        }
+    }
+    setTemplateVal(formVal)
+}
+
+function editContactsSave(flag: boolean = false) {
+    contactsTemplateRef.value?.getData().then((res: any) => {
+        let formVal = getFromValue({ ...contactsTemplateValue.value, ...res })
+        allLoading.editContactsSaveLoading = true
+        post(URL_UPLOAD, { ...formVal }).then((_res) => {
+            allVisible.editContactsVisible = flag
+            globalPopup?.showSuccess('保存成功')
+            emits('refreshData')
+        }).finally(() => {
+            allLoading.editContactsSaveLoading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function setTemplateVal(val: any = {}) {
+    contactsTemplateValue.value = val
+    allLoading.contactsTemplateRefLoading = true
+    setTimeout(() => {
+        contactsTemplateRefKey.value++
+        allLoading.contactsTemplateRefLoading = false
+    }, 1000);
+}
+
+function showVisible(type: keyof typeof allVisible) {
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
 
 const formItems = reactive([
-    { label: '联系人', key: 'productCode', value: '', labelClass: 'w-20 text-right text-gray-500', width: '48%' },
+    { label: '联系人', key: 'name', value: '', labelClass: 'w-20 text-right text-gray-500', width: '48%' },
     { label: '客户', key: 'productName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '电话号码', key: 'typeName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '邮箱', key: 'unitName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '职务', key: 'price', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '性别', key: 'unit', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '地址', key: 'status', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '电话', key: 'phone', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '邮箱', key: 'email', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '职务', key: 'position', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '性别', key: 'sexValue', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '地址', key: 'address', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
     { label: '负责人', key: 'inchargerName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
     { label: '创建人', key: 'creatorName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
     { label: '创建时间', key: 'createTime', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
-    { label: '备注', key: 'descs', value: '', labelClass: 'w-22 text-right text-gray-500', width: '100%' },
+    { label: '备注', key: 'remark', value: '', labelClass: 'w-22 text-right text-gray-500', width: '100%' },
 ])
 
 watchEffect(() => {
-
+    const { data } = props
+    info.value = data
+    formItems.forEach(item => {
+        item.value = info.value[item.key];
+    });
 });
 
+async function getSystemField() {
+    const { data } = await post(GETPERSONNEL, {})
+    transferOptions.value = data.map((item: any) => {
+        const { id, name, phone, jobNumber } = item
+        return {
+            value: id,
+            label: name
+        }
+    })
+
+    const datas = await get(GETGENERATEFOEM)
+    contactsTemplate.value = JSON.parse(datas.data[0].config)
+}
+
 // 生命周期钩子
 onMounted(async () => {
-    info.value = {
-        productCode: '联系的人',
-        productName: '客户名称',
-        typeName: '电话号码',
-        unitName: '邮箱地址',
-        price: '职务',
-        unit: '性别',
-        status: 1,
-        inchargerName: '负责人姓名',
-        creatorName: '创建人姓名',
-        createTime: '创建时间',
-        descs: '备注信息'
-    };
-
-    formItems.forEach(item => {
-        item.value = info.value[item.key];
-    });
+    getSystemField()
 });
 </script>
 <style scoped lang="scss">

+ 13 - 33
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/operationRecord.vue

@@ -5,45 +5,25 @@
         </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-column prop="operateDate" label="操作时间" width="150" />
+                <el-table-column prop="operateName" label="操作人" width="120" />
+                <el-table-column prop="msg" label="操作内容" />
             </el-table>
         </div>
     </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+const props = defineProps<{
+    data: any
+}>()
 
-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',
-},])
+const operationRecordtable = ref([])
+
+watchEffect(() => {
+    const { data } = props
+    operationRecordtable.value = data.contactsLogList || []
+});
 // 生命周期钩子
 onMounted(() => {
 });

+ 170 - 57
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedBusiness.vue

@@ -3,78 +3,191 @@
         <div class="flex justify-between">
             <div class="title">相关商机</div>
             <div>
-                <el-button type="primary">新建商机</el-button>
+                <el-button type="primary" @click="addBusiness()">新建商机</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="商机名称">
+            <el-table :data="relatedTaskstable" border height="300" style="width: 100%;">
+                <el-table-column prop="name" label="商机名称">
                     <template #default="scope">
-                        <el-button link type="primary" size="large">{{
-                            scope.row.taskName
+                        <el-button link type="primary" size="large" @click="toBusDetal(scope.row)">{{
+                            scope.row.name
                         }}</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="customerName" label="客户名称" width="130" />
+                <el-table-column prop="inchargerName" label="负责人" width="130" />
+                <el-table-column prop="amountOfMoney" label="商机金额" width="130" />
+                <el-table-column prop="expectedTransactionDate" label="预计成交时间" width="170" />
+                <el-table-column prop="stageValue" label="商机阶段" width="130" />
+                <el-table-column prop="creatorName" label="创建人" width="130" />
+                <el-table-column prop="createTime" label="创建时间" width="130" />
+                <el-table-column prop="modifyTime" label="修改时间" width="130" />
             </el-table>
         </div>
+
+        <!-- 弹窗 -->
+        <el-dialog v-model="allVisible.newBusinessisible" width="1000" :show-close="false" top="10vh">
+            <template #header="{ close, titleId, titleClass }">
+                <div class="flex justify-between items-center border-b pb-3 dialog-header">
+                    <h4 :id="titleId">新建商机</h4>
+                    <div>
+                        <el-button type="primary" @click="editBusiness()"
+                            :loading="allLoading.businessSaveLading">保存</el-button>
+                        <el-button @click="closeVisible('newBusinessisible')">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="h-[60vh] overflow-y-auto scroll-bar pt-3" v-loading="allLoading.generateFormLading">
+                <GenerateForm ref="businessTemplateRef" :data="businessTemplate" :value="businessTemplateValue"
+                    :key="businessTemplateKey" />
+                <div>相关产品</div>
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" />
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { GenerateForm } from '@zmjs/form-design';
+import { get, post } from '@/utils/request';
+import { useRouter, useRoute } from "vue-router";
+
+import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import { formatDateTime } from '@/utils/times';
+import { GETGENERATEFOEM, UPDATEINSET } from '@/pages/business/api';
+import { GETTABLELIST } from '@/pages/product/api';
+
+const router = useRouter()
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+const props = defineProps<{
+    data: any
+}>()
+
+const information = ref<any>({})
+const relatedTaskstable = ref([])
+const businessTemplateRef = ref<typeof GenerateForm>() // 自定义表单dom
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const productTableList = ref([])
+const businessTemplateValue = ref({})
+const businessTemplateKey = ref(1)
+const businessTemplate = ref({
+    config: {},
+    list: []
+}) // 自定义表单数据
+const allLoading = reactive({
+    newBusinessSaveLading: false,
+    businessSaveLading: false,
+    generateFormLading: false
+})
+const allVisible = reactive({
+    newBusinessisible: false
+})
+
+function toBusDetal(row: any) {
+    router.push({
+        path: `/business/detail`,
+        query: { id: row.id }
+    })
+}
+
+function editBusiness() {
+    businessTemplateRef.value?.getData().then((res: any) => {
+        let productTableListData = relatedProductsRef?.value?.returnData()
+        let newForm = {
+            ...res,
+            expectedTransactionDate: res.expectedTransactionDate ? formatDateTime(new Date(res.expectedTransactionDate)) : '',
+            businessItemProductList: productTableListData ? JSON.stringify(productTableListData) : []
+        }
+        allLoading.businessSaveLading = true
+        post(UPDATEINSET, { ...businessTemplateValue.value, ...newForm }).then((_res) => {
+            allVisible.newBusinessisible = false
+            globalPopup?.showSuccess('保存成功')
+            emits('refreshData')
+        }).finally(() => {
+            allLoading.businessSaveLading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function addBusiness() {
+    showVisible('newBusinessisible')
+    allLoading.generateFormLading = true
+    businessTemplateValue.value = {
+        customerId: information.value.customId,
+        contactsId: information.value.id
+    }
+    setTimeout(() => {
+        businessTemplateRef.value && businessTemplateRef.value.reset()
+        businessTemplateKey.value++
+        allLoading.generateFormLading = false
+    }, 500)
+}
+
+function showVisible(type: keyof typeof allVisible) { // 显示弹窗
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
+
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    relatedTaskstable.value = data.businessOpportunityList
+});
+
+async function getSystemField() {
+    const res = await get(GETGENERATEFOEM)
+    let config = JSON.parse(res.data[0].config)
+    config.list.forEach((item: any) => {
+        if (item.type == "grid") {
+            item.columns.forEach((column: any) => {
+                column.list.forEach((newColumn: any) => {
+                    if (newColumn.model == 'customerId' || newColumn.model == "contactsId") {
+                        newColumn.options.disabled = true
+                    }
+                })
+            })
+        }
+    })
+    businessTemplate.value = config
+}
 
-const relatedTaskstable = ref([{
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '相关商机',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}])
+function getProductTableList() {
+    post(GETTABLELIST, { pageIndex: -1, pageSize: -1 }).then((res) => {
+        if (res.code == 'ok') {
+            const { record, total } = res.data
+            productTableList.value = record.map((item: any) => {
+                const { id, productName, productCode, unit, unitName, typeName, type, price, inventory } = item
+                return {
+                    id,
+                    productId: id,
+                    productName,
+                    productCode,
+                    unit,
+                    unitName,
+                    price,
+                    type,
+                    typeName,
+                    inventory,
+                    quantity: '',
+                    discount: '',
+                    totalPrice: ''
+                }
+            })
+        }
+    })
+}
 // 生命周期钩子
 onMounted(() => {
+    getSystemField()
+    getProductTableList()
 });
 </script>
 <style scoped lang="scss"></style>

+ 1 - 43
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedTasks.vue

@@ -27,49 +27,7 @@
 <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',
-}])
+const relatedTaskstable = ref([])
 // 生命周期钩子
 onMounted(() => {
 });

+ 9 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/detail/index.vue

@@ -7,7 +7,7 @@
                 </el-link>
             </div>
             <div class="mr-8">
-                <el-select v-model="values" placeholder="请选择" style="width: 150px">
+                <el-select v-model="values" placeholder="请选择" style="width: 300px" @change="getDetail">
                     <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
                 </el-select>
             </div>
@@ -16,10 +16,10 @@
         <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar" v-loading="pageLoading">
             <div class="w-full h-auto flex justify-between">
                 <div class="bg-white shadow-md rounded-md" style="width: 46%;">
-                    <Information :data="information" />
+                    <Information :data="information" @refreshData="getDetail" />
                 </div>
                 <div class="bg-white ml-2 shadow-md rounded-md flex-1">
-                    <Attachment :data="attachment" :information="information" />
+                    <Attachment :data="information" @refreshData="getDetail" />
                 </div>
             </div>
 
@@ -29,13 +29,13 @@
                     <Detailcompinents :data="relatedTasks" :information="information" :formTaskType="0" :filed="'contactsId'" :disabledList="['contactsId']" @refreshData="getDetail" />
                 </div>
                 <div class="bg-white ml-2 shadow-md rounded-md flex-1">
-                    <OperationRecord :data="operationRecord" />
+                    <OperationRecord :data="information" />
                 </div>
             </div>
 
             <div class="w-full h-auto flex justify-between mt-2">
                 <div class="bg-white shadow-md rounded-md w-full">
-                    <RelatedBusiness :data="relatedBusiness" />
+                    <RelatedBusiness :data="information" @refreshData="getDetail" />
                 </div>
             </div>
         </div>
@@ -60,10 +60,7 @@ import RelatedBusiness from '../component/relatedBusiness.vue'
 const route = useRoute()
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const information = ref({}) // 基本信息
-const attachment = ref([]) // 附件
 const relatedTasks = ref([]) // 相关任务
-const operationRecord = ref([]) // 操作记录
-const relatedBusiness = ref([]) // 相关商机
 const rowId = ref(+(route.query.id || ''))
 const values = ref<number | string>('')
 const options = ref<optionType[]>([])
@@ -71,8 +68,10 @@ const pageLoading = ref(false)
 
 function getDetail() {
     pageLoading.value = true
-    post(URL_GETDETAIL, {}).then((res) => {
-        console.log(res)
+    post(URL_GETDETAIL, { id: rowId.value }).then((res) => {
+        res.data.sexValue = res.data.sex === 1 ? '男' : '女'
+        information.value = res.data
+        relatedTasks.value = res.data.taskList
     }).finally(() => {
         pageLoading.value = false
     })

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

@@ -261,7 +261,7 @@ function contactsDeteleItem(value: string | number, label: string, batch: boolea
       changeBatch(false)
       getContactPerson()
     }).catch((err) => {
-      globalPopup?.showError(err.message)
+      globalPopup?.showError(err.msg)
     })
   })
 }

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

@@ -378,11 +378,11 @@ function editCustomerSave(flag: boolean) {
 
 function editCustomer(row: any) { // row 有数据代表编辑
   if (row) {
-    const { id, companyPhone, customName, inchargerId, createTime, customSourceId, customerIndustryId, customerLevelId, email } = row
+    const { id, companyPhone, customName, inchargerId, createTime, customSourceId, customerIndustryId, customerLevelId, email, telPhone } = row
     const formVal = {
       id, customName, inchargerId, customerIndustryId, customerLevelId, email, customSourceId,
       createTime: formatDate(new Date(createTime)),
-      telPhone: companyPhone,
+      telPhone, companyPhone
     }
     allLoading.customerTemplateRefLoading = true
     setTimeout(() => {
@@ -392,6 +392,11 @@ function editCustomer(row: any) { // row 有数据代表编辑
     customerTemplateValue.value = formVal
     allText.editCustomerText = '编辑客户'
   } else { // 没有数据代表新建
+    setTimeout(() => {
+      console.log(customerTemplateRef.value)
+      customerTemplateValue.value = {}
+      customerTemplateRefKey.value++
+    }, 200)
     allText.editCustomerText = '新增客户'
   }
   showVisible('editCustomerVisible')

+ 3 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/api.ts

@@ -5,7 +5,10 @@ export const GETSYSFILED = "/sys-dict/getListByCode";
 export const GETPERSONNEL = "/user/getSimpleActiveUserList";
 export const GETGENERATEFOEM = `/sys-form/getListByCode${MOD}`
 export const GETALLPRODUCT = `/sys-form/getListByCode/Order`
+export const GETTABLELISTPRODUCT = `/product/list`
 export const GETTABLELIST = `${MOD}/list`
+export const URL_OEDERUPDATE = `${MOD}/addOrUpdate`
+export const URL_PRODUTWITHORDER = `${MOD}/productWithOrder`
 
 export const tableColumns: TableColumn[] = [
     { prop: 'orderCode', label: '订单编号', event: 'toDetali', width: '150' },

+ 157 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue

@@ -36,7 +36,7 @@
       <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 v-permission="['aabbc']" type="primary">新建订单</el-button>
+          <el-button type="primary" @click="editOrder(false)">新建订单</el-button>
           <el-button type="primary">批量转移</el-button>
           <el-button type="primary">批量删除</el-button>
           <el-button type="primary">回收站</el-button>
@@ -58,7 +58,7 @@
             </el-table-column>
             <el-table-column :label="'操作'" :width="'200px'" fixed="right">
               <template #default="scope">
-                <el-button link type="primary" size="large">编辑</el-button>
+                <el-button link type="primary" size="large" @click="editOrder(scope.row)">编辑</el-button>
                 <el-button link type="primary" size="large">新建任务</el-button>
                 <el-button link type="danger" size="large">删除</el-button>
               </template>
@@ -72,19 +72,43 @@
         </div>
       </div>
     </div>
+
+    <!-- 弹窗 -->
+    <el-dialog v-model="allVisible.editOrderVisible" width="1000" :show-close="false" top="10vh">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">{{ allText.orderEditText }}</h4>
+          <div>
+            <el-button type="primary" :loading="allLoading.editSaveLading" @click="saveOrder(true)">保存并新建</el-button>
+            <el-button type="primary" :loading="allLoading.editSaveLading" @click="saveOrder(false)">保存</el-button>
+            <el-button @click="closeVisible('editOrderVisible')">取消</el-button>
+          </div>
+        </div>
+      </template>
+      <div class="h-[60vh] overflow-y-auto scroll-bar pt-3" v-loading="allLoading.orderTemplateLoadinng">
+        <GenerateForm ref="orderTemplateRef" :data="orderTemplate" :value="orderTemplateValue" />
+        <div>相关产品</div>
+        <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" :productTableListValue="productTableListValue" />
+      </div>
+    </el-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, inject, defineExpose } from "vue";
-import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, getTemplateKey } from '@/utils/tools'
 import { post, get } from "@/utils/request";
-import { tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, GETTABLELIST, GETALLPRODUCT } from "./api";
+import { tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, GETTABLELIST, GETALLPRODUCT, GETTABLELISTPRODUCT, URL_OEDERUPDATE, URL_PRODUTWITHORDER } from "./api";
 import { useRouter, useRoute } from "vue-router";
+import { GenerateForm } from '@zmjs/form-design';
 import { URL_FETALL } from "../customer/api";
 
-const router = useRouter()
+import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import TaskModal from '@/components/TaskModal/index.vue'
+import { formatDateTime } from "@/utils/times";
 
+const router = useRouter()
+const globalPopup = inject<GlobalPopup>('globalPopup')
 const filterForm = reactive<FilterForm>({ // 筛选条件 Value
   contactPerson: "",
   customerId: "",
@@ -102,19 +126,91 @@ const selectData = reactive({ // 下拉数据
   RemittanceStatus: [{ id: 0, name: '已回款' }, { id: 1, name: '未回款' }, { id: 2, name: '已完全回款' }] as any[], // 回款状态
   AllProduct: [] as any[] // 所有产品
 })
-const filterItems = ref<FilterItem[]>([]) // 渲染筛选条件
 const formTablePaging = reactive({ // 分页条件
   currentPage: 1,
   pageSize: 10,
   total: 0,
 })
-const formTable = ref([]) // 表格数据
 const allLoading = reactive({ // 按钮加载 Loading
   formTableLading: false,
+  editSaveLading: false,
+  orderTemplateLoadinng: false
 })
 const allVisible = reactive({
-
+  editOrderVisible: false
+})
+const allText = reactive({
+  orderEditText: '新建订单'
 })
+const orderTemplate = ref({
+  list: [],
+  config: {}
+})
+const orderTemplateValue = ref({})
+const orderTemplateKey = ref(1)
+const orderTemplateRef = ref<typeof GenerateForm>()
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const filterItems = ref<FilterItem[]>([]) // 渲染筛选条件
+const formTable = ref([]) // 表格数据
+const productTableList = ref([])
+const productTableListValue = ref([])
+
+function saveOrder(flag: boolean) {
+  orderTemplateRef.value?.getData().then((res: any) => {
+    let productTableListData = relatedProductsRef?.value?.returnData()
+    for(var i in productTableListData) {
+      productTableListData[i].sealPrice = productTableListData[i].sellingPrice,
+      productTableListData[i].discount = productTableListData[i].discount,
+      productTableListData[i].num = productTableListData[i].quantity
+    }
+    const produt = productTableListData ? JSON.stringify(productTableListData) : []
+    allLoading.editSaveLading = true
+    post(URL_OEDERUPDATE, {
+      ...orderTemplateValue.value,
+      ...res,
+      orderEndDate: res.orderEndDate ? formatDate(res.orderEndDate) : '',
+      orderStartDate: res.orderStartDate ? formatDate(res.orderStartDate) : '',
+      orderProductDetailString: produt
+    }).then((_res) => {
+      allVisible.editOrderVisible = flag
+      globalPopup?.showSuccess('保存成功')
+      if (flag) {
+        orderTemplateRef.value?.reset()
+      }
+      getTableList()
+    }).finally(() => {
+      allLoading.editSaveLading = false
+    })
+  }).catch((_err: any) => {
+    console.log(_err)
+    globalPopup?.showError('请填写完整')
+  })
+}
+
+function editOrder(item: any) {
+  showVisible('editOrderVisible')
+  allLoading.orderTemplateLoadinng = true
+  if (item) {
+    editProduct(item)
+    const templateKey = getTemplateKey(orderTemplate.value.list)
+    let formVal: templateKey = { id: item.id }
+    for (let i = 0; i < orderTemplate.value.list.length; i++) {
+      const key = templateKey[i]
+      formVal[key] = item[key]
+    }
+    orderTemplateValue.value = formVal
+    allText.orderEditText = '编辑订单'
+  }
+  if (!item) {
+    orderTemplateValue.value = {}
+    productTableListValue.value = []
+    allText.orderEditText = '新增订单'
+  }
+  setTimeout(() => {
+    orderTemplateKey.value++
+    allLoading.orderTemplateLoadinng = false
+  }, 500)
+}
 
 function toDetali(row: any) {
   router.push({
@@ -171,12 +267,20 @@ async function getSystemField() {
     const { id, customName } = item
     return { id, name: customName }
   })
-  // const res = await get(GETGENERATEFOEM)
-  // generateFormData.value = JSON.parse(res.data[0].config)
+  const res = await get(GETGENERATEFOEM)
+  orderTemplate.value = JSON.parse(res.data[0].config)
 
   setFilterItems()
 }
 
+function showVisible(type: keyof typeof allVisible) { // 显示弹窗
+  allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+  allVisible[type] = false
+}
+
 function setFilterItems() {
   filterItems.value = [
     { label: '订单编号', key: 'orderCode', type: 'input' },
@@ -190,10 +294,53 @@ function setFilterItems() {
   ]
 }
 
+function editProduct(row: any) {
+  post(URL_PRODUTWITHORDER, { id: row.id }).then(({ data }) => {
+    const list = data.map((item: any) => {
+      const { id, productName, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail } = item
+      return { id, productId: id, productName, productCode, unit, unitName, typeName, type, price, inventory, 
+        quantity: +orderProductDetail?.num, 
+        discount: +orderProductDetail?.discount, 
+        sellingPrice: +orderProductDetail?.sealPrice,
+        totalPrice: +orderProductDetail?.totalPrice
+      }
+    })
+    console.log('开始执行', list)
+    productTableListValue.value = list
+  })
+}
+
+function getProductTableList() {
+  post(GETTABLELISTPRODUCT, { pageIndex: -1, pageSize: -1 }).then((res) => {
+    if (res.code == 'ok') {
+      const { record, total } = res.data
+      productTableList.value = record.map((item: any) => {
+        const { id, productName, productCode, unit, unitName, typeName, type, price, inventory } = item
+        return {
+          id,
+          productId: id,
+          productName,
+          productCode,
+          unit,
+          unitName,
+          price,
+          type,
+          typeName,
+          inventory,
+          quantity: '',
+          discount: '',
+          totalPrice: ''
+        }
+      })
+    }
+  })
+}
+
 onMounted(() => {
   getSystemField()
   getAllProduct()
   getTableList()
+  getProductTableList()
 })
 </script>
 

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

@@ -250,6 +250,7 @@ function editProduct(item: any) {
   }
   if (!item) {
     genereditForm.value = {}
+    allText.editClueText = '新建产品'
   }
   setTimeout(() => {
     generateForm.value && generateForm.value.reset()

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/constant.ts

@@ -18,3 +18,4 @@ export const DEYELWCLUE = `${prefix}/listDeleterClue`
 export const DETERDETELE = `${prefix}/deleterDelete`
 export const ROLLBACK = `${prefix}/rollback`
 export const URL_IMPORTTHREAD = `${prefix}/importData`
+export const URL_EXPOERTHREAD = `${prefix}/exportData`

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

@@ -57,7 +57,7 @@
           <el-button type="primary" @click="batchDeletes()">批量删除</el-button>
           <el-button type="primary" @click="showDeteleClue(true)">回收站</el-button>
           <el-button type="primary" @click="dialogVisible.importVisible = true">导入</el-button>
-          <el-button type="primary">导出</el-button>
+          <el-button type="primary" @click="exportTheadTableList()" :loading="allLoading.exoprtLoading">导出</el-button>
         </div>
         <div class="flex-1 w-full overflow-hidden">
           <el-table ref="clueTableRef" :data="clueTable" border v-loading="allLoading.clueTableLading"
@@ -169,8 +169,8 @@
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, inject } from "vue";
-import { GETSYSFILED, MOD, IMPORMOD, GETPERSONNEL, GETTABLE, GETTEMPLATE, GETDETAIL, UNDATECLAIM, UNDATEFORM, DELTEROW, URL_IMPORTTHREAD } from './constant'
-import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, createTaskFromType, confirmAction, downloadTemplate } from '@/utils/tools'
+import { GETSYSFILED, MOD, IMPORMOD, GETPERSONNEL, GETTABLE, GETTEMPLATE, GETDETAIL, UNDATECLAIM, UNDATEFORM, DELTEROW, URL_IMPORTTHREAD, URL_EXPOERTHREAD } from './constant'
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, createTaskFromType, confirmAction, downloadTemplate, downloadFile } from '@/utils/tools'
 import { FormInstance, FormRules, ElMessageBox, ElTable, UploadRequestOptions } from 'element-plus'
 import { post, get, uploadFile } from "@/utils/request";
 import { useRouter, useRoute } from "vue-router";
@@ -219,6 +219,7 @@ const allLoading = reactive({
   generateFormLading: false,
   clueLoading: false,
   importLoading: false,
+  exoprtLoading: false,
 })
 const dialogVisible = reactive({
   editClueDialogVisible: false,
@@ -256,6 +257,16 @@ const transferForm = reactive({
 
 
 // 定义方法
+function exportTheadTableList() {
+  allLoading.exoprtLoading = true
+  let valueForm = getFromValue(filterCriteriaForm)
+  post(URL_EXPOERTHREAD, {...valueForm}).then((res) => {
+    downloadFile(res.data, '线索表表导出.xlsx')
+  }).finally(() => {
+    allLoading.exoprtLoading = false
+  })
+}
+
 function newTask(item: any) {
   const { id } = item
   taskModalForm.value = { ...createTaskFromType(3), clueId: id, }

+ 122 - 16
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/BusinessOpportunityController.java

@@ -1,15 +1,20 @@
 package com.management.platform.controller;
 
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.BusinessOpportunityMapper;
+import com.management.platform.mapper.SysFormMapper;
 import com.management.platform.mapper.UserMapper;
-import com.management.platform.service.BusinessOpportunityService;
-import com.management.platform.service.StageService;
-import com.management.platform.service.SysFunctionService;
-import com.management.platform.service.TaskService;
+import com.management.platform.service.*;
+import com.management.platform.service.impl.ExcelExportServiceImpl;
 import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 
@@ -19,10 +24,7 @@ import org.springframework.web.multipart.MultipartFile;
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 
 /**
  * <p>
@@ -35,14 +37,20 @@ import java.util.List;
 @RestController
 @RequestMapping("/business-opportunity")
 public class BusinessOpportunityController {
-
+    @Resource
+    private ExcelExportServiceImpl excelExportService;
+    @Resource
+    private SysFormMapper sysFormMapper;
+    @Autowired
+    private WxCorpInfoService wxCorpInfoService;
     @Resource
     private UserMapper userMapper;
     @Resource
     private SysFunctionService sysFunctionService;
     @Resource
     private StageService stageService;
-
+    @Value(value = "${upload.path}")
+    private String path;
     @Resource
     private BusinessOpportunityService bOservice;
     @Resource
@@ -60,11 +68,86 @@ public class BusinessOpportunityController {
     }
 
 
-    @RequestMapping("exportData")
-    public Object exportData(BusinessOpportunity bo, HttpServletRequest request) throws Exception {
-        return bOservice.exportData(bo,request);
+
+    @RequestMapping("/importData")
+    public HttpRespMsg importData(MultipartFile multipartFile){
+        return bOservice.importData(multipartFile);
     }
 
+
+    @RequestMapping("/exportData")
+    public HttpRespMsg exportData(BusinessOpportunity bo ,HttpServletRequest request) throws Exception {
+        User user = userMapper.selectById(request.getHeader("token"));
+        SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, user.getCompanyId()).eq(SysForm::getCode, "Thread").eq(SysForm::getIsCurrent, 1));
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+        String config = sysForm.getConfig();
+        JSONObject configOb = JSON.parseObject(config);
+        JSONArray configObJSONArray = configOb.getJSONArray("list");
+        List<List<String>> dataList=new ArrayList<>();
+        List<String> titleList=new ArrayList<>();
+        for (int i = 0; i < configObJSONArray.size(); i++) {
+            JSONObject item = configObJSONArray.getJSONObject(i);
+            titleList.add(item.getString("label"));
+        }
+        dataList.add(titleList);
+        HttpRespMsg msg = list(bo, request);
+        Map<String, Object> msgData = (Map<String, Object>) msg.getData();
+        List<BusinessOpportunity> bos = (List<BusinessOpportunity>) msgData.get("record");
+        for (BusinessOpportunity bo1 : bos) {
+            List<String> item=new ArrayList<>();
+            for (int i = 0; i < configObJSONArray.size(); i++) {
+                JSONObject target = configObJSONArray.getJSONObject(i);
+                if(target.getString("type").equals("grid")){
+                    JSONArray columns = target.getJSONArray("columns");
+                    for (int i1 = 0; i1 < columns.size(); i1++) {
+                        JSONObject columnsJSONObject = columns.getJSONObject(i1);
+                        JSONArray list = columnsJSONObject.getJSONArray("list");
+                        for (int i2 = 0; i2 < list.size(); i2++) {
+                            JSONObject object = list.getJSONObject(i2);
+                            String model = object.getString("model");
+                            String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+                            Class<? extends BusinessOpportunity> aClass = bo1.getClass();
+                            String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(bo1)==null?"":aClass.getMethod("get" + targetName).invoke(bo1));
+                            if(model.equals("inchargerId")){
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(bo1))+"$";
+                                }else {
+                                    value = String.valueOf(aClass.getMethod("getInchargerName").invoke(bo1));
+                                }
+                            }
+                            item.add(value);
+                        }
+                    }
+                }else {
+                    String model = target.getString("model");
+                    String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+                    Class<? extends BusinessOpportunity> aClass = bo1.getClass();
+                    String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(bo1)==null?"":aClass.getMethod("get" + targetName).invoke(bo1));
+                    if(model.equals("inchargerId")){
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(bo1))+"$";
+                        }else {
+                            value = String.valueOf(aClass.getMethod("getInchargerName").invoke(bo1));
+                        }
+                    }
+                    if(model.equals("contactsId")){
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            value = "$userName"+String.valueOf(aClass.getMethod("getContactsName").invoke(bo1))+"$";
+                        }else {
+                            value = String.valueOf(aClass.getMethod("getContactsName").invoke(bo1));
+                        }
+                    }
+                    item.add(value);
+                }
+            }
+            dataList.add(item);
+        }
+        String fileName="商机表导出_"+ System.currentTimeMillis();
+        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,fileName,dataList,path);
+    }
+
+
+
     // 批量放入回收站
     @RequestMapping("delete")
     public Object delete(BusinessOpportunity bo) {
@@ -132,6 +215,7 @@ public class BusinessOpportunityController {
         }else {
             //修改
             bOservice.update(bo,user.getId());
+
         }
         msg.setMsg("操作成功");
         return msg;
@@ -145,6 +229,21 @@ public class BusinessOpportunityController {
         bOservice.getAndTransfer(bo, user);
         return msg;
     }
+    @RequestMapping("saveReason")
+    public Object saveReason(BusinessOpportunity bo, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.setMsg("操作成功");
+        bOservice.saveReason(bo, user);
+        return msg;
+    } @RequestMapping("saveContactsId")
+    public Object saveContactsId(BusinessOpportunity bo, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.setMsg("操作成功");
+        bOservice.saveContactsId(bo, user);
+        return msg;
+    }
     @RequestMapping("list")
     public HttpRespMsg list(BusinessOpportunity bo, HttpServletRequest request) {
         HashMap<Object, Object> r = new HashMap<>();
@@ -214,15 +313,22 @@ public class BusinessOpportunityController {
     public HttpRespMsg saveProduct(BusinessOpportunity bo , HttpServletRequest request) {
         User user = userMapper.selectById(request.getHeader("Token"));
         return bOservice.saveProduct(bo,user);
+    } @RequestMapping("saveContacts")
+    public HttpRespMsg saveContacts(BusinessOpportunity bo , HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        return bOservice.saveProduct(bo,user);
     }
 
     @RequestMapping("saveStage")
-    public HttpRespMsg insertStage(List<Stage> stages, HttpServletRequest request) {
+    public HttpRespMsg insertStage(String stages, HttpServletRequest request) {
         return stageService.changeStage(stages,request);
+    }   @RequestMapping("deleteStage")
+    public HttpRespMsg deleteStage(Stage stage) {
+        return stageService.deleteStage(stage);
     }
     @RequestMapping("getStage")
-    public HttpRespMsg Stage() {
-        return stageService.getStage();
+    public HttpRespMsg Stage(HttpServletRequest request) {
+        return stageService.getStage(request);
     }
 
     private BusinessOpportunity setNull(BusinessOpportunity bo) {

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

@@ -44,10 +44,6 @@ public class SalesOrderController {
     @Resource
     private OrderProductDetailService orderProductDetailService;
     @Resource
-    private ProductMapper productMapper;
-    @Resource
-    private SysDictService sysDictService;
-    @Resource
     private TaskService taskService;
     @Resource
     private AuditLogCenterService auditLogCenterService;
@@ -261,35 +257,7 @@ public class SalesOrderController {
      * */
     @RequestMapping("/productWithOrder")
     public HttpRespMsg productWithOrder(Integer id){
-        HttpRespMsg msg=new HttpRespMsg();
-        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
-        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
-        List<SysDict> sysDictOfProductUnit = sysDictService.list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, "ProductUnit"));
-        List<SysDict> sysDictOfProductType = sysDictService.list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, "ProductType"));
-        List<OrderProductDetail> orderProductDetails = orderProductDetailService.list(new LambdaQueryWrapper<OrderProductDetail>().eq(OrderProductDetail::getOrderId, id));
-        List<Integer> productIds = orderProductDetails.stream().map(OrderProductDetail::getProductId).collect(Collectors.toList());
-        productIds.add(-1);
-        List<Product> productList = productMapper.selectList(new LambdaQueryWrapper<Product>().in(Product::getId, productIds));
-        productList.forEach(p->{
-            Optional<User> user = userList.stream().filter(u -> u.getId().equals(p.getInchargerId())).findFirst();
-            if(user.isPresent()){
-                p.setInchargerName(user.get().getName());
-            }
-            Optional<User> creator = userList.stream().filter(u -> u.getId().equals(p.getCreatorId())).findFirst();
-            if(creator.isPresent()){
-                p.setCreatorName(creator.get().getName());
-            }
-            Optional<SysDict> unit = sysDictOfProductUnit.stream().filter(u -> u.getId().equals(p.getUnit())).findFirst();
-            if(unit.isPresent()){
-                p.setUnitName(unit.get().getName());
-            }
-            Optional<SysDict> type = sysDictOfProductType.stream().filter(u -> u.getId().equals(p.getType())).findFirst();
-            if(type.isPresent()){
-                p.setTypeName(type.get().getName());
-            }
-        });
-        msg.setData(productList);
-        return msg;
+        return salesOrderService.productWithOrder(id);
     }
 
 

+ 5 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java

@@ -51,6 +51,11 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
      */
     @TableField("contacts_id")
     private Integer contactsId;
+    /**
+     * 赢单\输单 理由
+     */
+    @TableField("reason")
+    private String reason;
     @TableField(exist = false)
     private String contactsName;
 

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-04-03
+ * @since 2024-05-29
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -49,6 +49,12 @@ public class OrderProductDetail extends Model<OrderProductDetail> {
     @TableField("discount")
     private Integer discount;
 
+    /**
+     * 合计
+     */
+    @TableField("total_price")
+    private BigDecimal totalPrice;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -151,6 +151,9 @@ public class Product extends Model<Product> {
     @TableField(exist = false)
     private String unitName;
 
+    @TableField(exist = false)
+    private OrderProductDetail orderProductDetail;
+
 
     @Override
     protected Serializable pkVal() {

+ 5 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/Stage.java

@@ -32,6 +32,11 @@ public class Stage extends Model<Stage> {
      */
     @TableField("name")
     private String name;
+    /**
+     * 公司id
+     */
+    @TableField("company_id")
+    private Integer companyId;
 
     /**
      * 进度

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

@@ -2,7 +2,9 @@ package com.management.platform.mapper;
 
 import com.management.platform.entity.BusinessOpportunity;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.util.HttpRespMsg;
 import org.apache.ibatis.annotations.Param;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 import java.util.Map;
@@ -34,4 +36,5 @@ public interface BusinessOpportunityMapper extends BaseMapper<BusinessOpportunit
     Map<String, Object> getDataSummary(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
 
     List<Map<String, Object>> getDataStage(Integer companyId, String startDate, String endDate, String userId,@Param("list") List<String> targetUserIds);
+
 }

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

@@ -39,7 +39,6 @@ public interface BusinessOpportunityService extends IService<BusinessOpportunity
 
     void getAndTransfer(BusinessOpportunity bo, User user);
 
-    Object exportData(BusinessOpportunity bo, HttpServletRequest request) throws Exception;
 
     HttpRespMsg saveProduct(BusinessOpportunity bo, User user);
 
@@ -60,4 +59,10 @@ public interface BusinessOpportunityService extends IService<BusinessOpportunity
     List<Map<String, Object>> getDataStage(Integer companyId, String startDate, String endDate, String userId,List<String> targetUserIds);
 
     void deleterDelete(List<Integer> ids);
+
+    HttpRespMsg importData(MultipartFile multipartFile);
+
+    void saveContactsId(BusinessOpportunity bo, User user);
+
+    void saveReason(BusinessOpportunity bo, User user);
 }

+ 2 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ClueService.java

@@ -9,6 +9,7 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
 /**
@@ -54,5 +55,5 @@ public interface ClueService extends IService<Clue> {
 
     HttpRespMsg importData(MultipartFile multipartFile);
 
-    HttpRespMsg exportData(Clue clue);
+    HttpRespMsg exportData(Clue clue) throws Exception;
 }

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

@@ -28,4 +28,6 @@ public interface SalesOrderService extends IService<SalesOrder> {
     HttpRespMsg dataSummary(Integer queryType, Integer dateType);
 
     HttpRespMsg businessOpportunityStage(Integer queryType, Integer dateType);
+
+    HttpRespMsg productWithOrder(Integer id);
 }

+ 4 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/StageService.java

@@ -17,7 +17,9 @@ import java.util.List;
  */
 public interface StageService extends IService<Stage> {
 
-    HttpRespMsg changeStage(List<Stage> stages, HttpServletRequest request);
+    HttpRespMsg changeStage(String stages, HttpServletRequest request);
 
-    HttpRespMsg getStage();
+    HttpRespMsg getStage(HttpServletRequest request);
+
+    HttpRespMsg deleteStage(Stage stage);
 }

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

@@ -13,25 +13,34 @@ import com.management.platform.mapper.*;
 import com.management.platform.service.BusinessOpportunityService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.service.WxCorpInfoService;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.FileUtil;
 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.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.net.URLEncoder;
 import java.util.*;
 
+
 /**
  * <p>
  * 服务实现类
@@ -54,6 +63,8 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     @Resource
     private ActionLogMapper actionLogMapper;
     @Resource
+    private BusinessItemProductMapper bipMapper;
+    @Resource
     private UserMapper userMapper;
     @Resource
     private BusinessItemProductMapper biMapper;
@@ -61,7 +72,11 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     private SysFormMapper sysFormMapper;
     @Autowired
     private UploadFileMapper uploadFileMapper;
+    @Resource
+    private HttpServletRequest request;
 
+    @Resource
+    private SysDictMapper sysDictMapper;
     @Override
     public List<BusinessOpportunity> getAll(BusinessOpportunity bo) {
         return bOMapper.selectAllList(bo);
@@ -78,8 +93,7 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
         businessOpportunity.setActionLogList(actionLogMapper.selectList(new QueryWrapper<ActionLog>().eq("item_id", bo.getId()).eq("code", "business")));
         businessOpportunity.setUploadFilePList(uploadFileMapper.selectByInfoList("business",bo.getId()));
         businessOpportunity.setTaskList(taskMapper.selectList(new QueryWrapper<Task>().eq("business_opportunity_id",bo.getId())));
-        List<BusinessItemProduct> businessItemProducts = biMapper.selectListToBoId(bo.getId());
-        businessOpportunity.setBusinessItemProducts(businessItemProducts);
+        List<BusinessItemProduct> businessItemProducts = bipMapper.selectList(new QueryWrapper<BusinessItemProduct>().eq("business_id", bo.getId()));
         if (businessItemProducts.size() > 0){
             BigDecimal finalPrice = new BigDecimal(0);
             BigDecimal discountedPrice = new BigDecimal(0);
@@ -96,6 +110,8 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
             // 整单折扣率
             businessOpportunity.setFinalPrice(divide.multiply(new BigDecimal(100)));
         }
+        businessOpportunity.setBusinessItemProducts(businessItemProducts);
+
         return businessOpportunity;
     }
 
@@ -126,7 +142,10 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
         bo.setCreateTime(new Date());
         bOMapper.insert(bo);
         List<BusinessItemProduct> businessItemProducts = JSONArray.parseArray(bo.getBusinessItemProductList(), BusinessItemProduct.class);
-        System.out.println(businessItemProducts);
+        for (BusinessItemProduct businessItemProduct : businessItemProducts) {
+            businessItemProduct.setBusinessId(bo.getId());
+            bipMapper.insert(businessItemProduct);
+        }
         ActionLog actionLog = new ActionLog();
         actionLog.setUserId(bo.getUserId());
         actionLog.setItemId(bo.getId());
@@ -140,6 +159,12 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     public void update(BusinessOpportunity bo, String userId) {
         setNull(bo);
         bOMapper.updateById(bo);
+        bipMapper.delete(new QueryWrapper<BusinessItemProduct>().eq("business_id",bo.getId()));
+        List<BusinessItemProduct> businessItemProducts = JSONArray.parseArray(bo.getBusinessItemProductList(), BusinessItemProduct.class);
+        for (BusinessItemProduct businessItemProduct : businessItemProducts) {
+            businessItemProduct.setBusinessId(bo.getId());
+            bipMapper.insert(businessItemProduct);
+        }
         ActionLog log = new ActionLog();
         log.setItemId(bo.getId());
         log.setName("编辑了商机");
@@ -191,28 +216,7 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
     @Value(value = "${upload.file}")
     private String path;
 
-    @Override
-    public Object exportData(BusinessOpportunity bo, HttpServletRequest request) throws Exception {
-        User user = userMapper.selectById(request.getHeader("token"));
-        SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, user.getCompanyId()).eq(SysForm::getCode, "Clue").eq(SysForm::getIsCurrent, 1));
-        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
-        String config = sysForm.getConfig();
-        JSONObject configOb = JSON.parseObject(config);
-        JSONArray configObJSONArray = configOb.getJSONArray("list");
-        List<List<String>> dataList = new ArrayList<>();
-        List<String> titleList = new ArrayList<>();
-        for (int i = 0; i < configObJSONArray.size(); i++) {
-            JSONObject item = configObJSONArray.getJSONObject(i);
-            titleList.add(item.getString("label"));
-        }
-        dataList.add(titleList);//设置表头
-
 
-        bo.setCompanyId(user.getCompanyId());
-
-        String fileName = "商机表导出_" + System.currentTimeMillis();
-        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo, fileName, dataList, path);
-    }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -379,6 +383,210 @@ public class BusinessOpportunityServiceImpl extends ServiceImpl<BusinessOpportun
         bOMapper.deleteBatchIds(ids);
     }
 
+    @Override
+    public HttpRespMsg importData(MultipartFile multipartFile) {
+        HttpRespMsg msg=new HttpRespMsg();
+        String fileName = multipartFile.getOriginalFilename();
+        File file = new File(fileName == null ? "file" : fileName);
+        User user = userMapper.selectById(request.getHeader("token"));
+        Integer companyId = user.getCompanyId();
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
+        List<SysDict> sysDictOfClueSources = sysDictMapper.selectList(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCompanyId, companyId).eq(SysDict::getCode, "ClueSources"));
+        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);
+            //由于第一行需要指明列对应的标题
+            int rowNum = sheet.getLastRowNum();
+            //获取当前表单模板 校验规则
+            SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCode, "Thread").eq(SysForm::getCompanyId, companyId).eq(SysForm::getIsCurrent, 1));
+            if(sysForm==null){
+                msg.setError("当前模块未配置自定义模板,需先完成配置");
+                return msg;
+            }
+            String config = sysForm.getConfig();
+            JSONObject configOb = JSON.parseObject(config);
+            JSONArray configObJSONArray = configOb.getJSONArray("list");
+            List<String> userNameList=new ArrayList<>();
+            HttpRespMsg respMsg=new HttpRespMsg();
+            for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                //获取到当前行的列数据
+                int cellNum = row.getLastCellNum();
+                for (int i = 0; i < cellNum; i++) {
+                    JSONObject item = configObJSONArray.getJSONObject(i);
+                    String modelName = item.getString("model");
+                    XSSFCell cell = row.getCell(i);
+                    if(cell!=null){
+                        switch (item.getString("type")){
+//                            case "time":cell.setCellType(CellType.NUMERIC);
+//                                break;
+                            default:cell.setCellType(CellType.STRING);
+                        }
+                    }
+                    if(modelName.equals("inchargerId")){
+                        System.out.println("=====");
+                        System.out.println(modelName);
+                        System.out.println(cell.toString());
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            userNameList.add(cell.getStringCellValue());
+                        }
+                    }
+
+                }
+            }
+            System.out.println("参与搜索的人员列表"+userNameList + userNameList.size());
+            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1&&userNameList.size()>0){
+                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;
+            //直接忽略空行 从row1开始
+            for (int rowIndex = 1; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                //获取到当前行的列数据
+                int cellNum = row.getLastCellNum();
+                BusinessOpportunity bo =new BusinessOpportunity();
+                bo.setCompanyId(companyId);
+                bo.setCreateTime(new Date());
+                bo.setCreatorId(user.getId());
+                for (int i = 0; i < cellNum; i++) {
+                    JSONObject item = configObJSONArray.getJSONObject(i);
+                    String modelName = item.getString("model");
+                    String className = modelName.substring(0, 1).toUpperCase() + modelName.substring(1);
+                    String getter="get"+className;
+                    String setter="set"+className;
+                    XSSFCell cell = row.getCell(i);
+                    if(cell!=null){
+                        switch (item.getString("type")){
+//                            case "time":cell.setCellType(CellType.NUMERIC);
+//                                break;
+                            default:cell.setCellType(CellType.STRING);
+                        }
+                    }
+                    //校验当前列是否为必填
+                    JSONObject options = item.getJSONObject("options");
+                    JSONObject rules = options.getJSONObject("rules");
+                    Boolean required = rules.getBoolean("required");
+                    if(required){
+                        if(StringUtils.isEmpty(cell.getStringCellValue())){
+                            msg.setError(item.getString("label")+"值不能为空值");
+                            return msg;
+                        }
+                    }
+                    if(modelName.equals("inchargerId")){
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String userName = cell.getStringCellValue();
+                            Optional<User> first;
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(userName)).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(userName))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(userName)||(u.getJobNumber()!=null&&u.getJobNumber().equals(userName))).findFirst();
+                            }
+                            if (first.isPresent()) {
+                                bo.setInchargerId(first.get().getId());
+                            } else {
+                                msg.setError("负责人["+userName+"]在系统中不存在");
+                                return msg;
+                            }
+                        }
+                    }else if(modelName.equals("contactsId")){
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String userName = cell.getStringCellValue();
+                            Optional<User> first;
+                            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(userName)).findFirst();
+                                first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(userName))||(optional.isPresent()&&u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))).findFirst();
+                            }else {
+                                first= userList.stream().filter(u -> u.getName().equals(userName)||(u.getJobNumber()!=null&&u.getJobNumber().equals(userName))).findFirst();
+                            }
+                            if (first.isPresent()) {
+                                bo.setContactsId(Integer.parseInt(first.get().getId()));
+                            } else {
+                                msg.setError("负责人["+userName+"]在系统中不存在");
+                                return msg;
+                            }
+                        }
+                    }else {
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            Class<Clue> clueClass = Clue.class;
+                            Method method = clueClass.getMethod(setter,String.class);
+                            method.invoke(bo,cell.getStringCellValue());
+                        }
+                    }
+
+                }
+                bOMapper.insert(bo);
+            }
+        } catch (IOException | NoSuchMethodException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            e.printStackTrace();
+        } catch (Exception e) {
+            e.printStackTrace();
+            msg.setError("验证失败");
+            return msg;
+        }
+        return msg;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void saveContactsId(BusinessOpportunity bo, User user) {
+        bOMapper.update(bo, new UpdateWrapper<BusinessOpportunity>().eq("id", bo.getId()).set("contacts_id", bo.getContactsId()));
+        ActionLog al = new ActionLog();
+        al.setCode("business");
+        al.setName("关联了联系人");
+        al.setUserId(user.getId());
+        al.setItemId(bo.getId());
+        actionLogMapper.insert(al);
+    }
+
+    @Override
+    public void saveReason(BusinessOpportunity bo, User user) {
+        bOMapper.update(bo, new UpdateWrapper<BusinessOpportunity>().eq("id", bo.getId()).set("reason", bo.getReason()));
+        ActionLog al = new ActionLog();
+        al.setCode("business");
+        al.setName("编辑了 结束理由");
+        al.setUserId(user.getId());
+        al.setItemId(bo.getId());
+        actionLogMapper.insert(al);
+    }
+
+
     private BusinessOpportunity setNull(BusinessOpportunity bo) {
         if (bo.getPlate1() == "") {
             bo.setPlate1(null);

+ 85 - 25
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ClueServiceImpl.java

@@ -42,6 +42,7 @@ import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.net.URLEncoder;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 
@@ -85,6 +86,7 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
     @Resource
     private SysDictMapper sysDictMapper;
 
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void insert(Clue clue) {
@@ -370,6 +372,7 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg importData(MultipartFile multipartFile) {
         HttpRespMsg msg=new HttpRespMsg();
         String fileName = multipartFile.getOriginalFilename();
@@ -429,30 +432,30 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
                     XSSFCell cell = row.getCell(i);
                     if(cell!=null){
                         switch (item.getString("type")){
-                            case "time":cell.setCellType(CellType.NUMERIC);
-                                break;
+//                            case "time":cell.setCellType(CellType.NUMERIC);
+//                                break;
                             default:cell.setCellType(CellType.STRING);
                         }
                     }
-//                    if(modelName.equals("inchargerId")){
-//                        System.out.println("=====");
-//                        System.out.println(modelName);
-//                        System.out.println(cell.toString());
-//                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
-//                            userNameList.add(cell.getStringCellValue());
-//                        }
-//                    }
+                    if(modelName.equals("inchargerId")){
+                        System.out.println("=====");
+                        System.out.println(modelName);
+                        System.out.println(cell.toString());
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            userNameList.add(cell.getStringCellValue());
+                        }
+                    }
 
                 }
             }
-//            System.out.println("参与搜索的人员列表"+userNameList + userNameList.size());
-//            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1&&userNameList.size()>0){
-//                respMsg = wxCorpInfoService.getBatchSearchUserInfo(wxCorpInfo, userNameList,null);
-//                if(respMsg.code.equals("0")){
-//                    msg.setError("姓名为["+String.valueOf(respMsg.data)+"]的人员存在重复,请使用工号!");
-//                    return msg;
-//                }
-//            }
+            System.out.println("参与搜索的人员列表"+userNameList + userNameList.size());
+            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1&&userNameList.size()>0){
+                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;
             //直接忽略空行 从row1开始
             for (int rowIndex = 1; rowIndex <= rowNum; rowIndex++) {
@@ -479,8 +482,8 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
                     XSSFCell cell = row.getCell(i);
                     if(cell!=null){
                         switch (item.getString("type")){
-                            case "time":cell.setCellType(CellType.NUMERIC);
-                                break;
+//                            case "time":cell.setCellType(CellType.NUMERIC);
+//                                break;
                             default:cell.setCellType(CellType.STRING);
                         }
                     }
@@ -511,7 +514,7 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
                                 return msg;
                             }
                         }
-                    }else if(modelName.equals("customLevel")){
+                    }else if(modelName.equals("customLevelId")){
                         if(!StringUtils.isEmpty(cell.getStringCellValue())){
                             Optional<SysDict> first = sysDictOfCustomLevel.stream().filter(s -> s.getName().equals(cell.getStringCellValue())).findFirst();
                             if(first.isPresent()){
@@ -520,7 +523,7 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
                                 throw new Exception("客户级别["+cell.getStringCellValue()+"不存在,请在系统字典中增加");
                             }
                         }
-                    }else if(modelName.equals("clueSources")){
+                    }else if(modelName.equals("clueSourcesId")){
                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
                            Optional<SysDict> first = sysDictOfClueSources.stream().filter(s -> s.getName().equals(cell.getStringCellValue())).findFirst();
                            if(first.isPresent()){
@@ -530,7 +533,7 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
                                throw new Exception("线索来源["+cell.getStringCellValue()+"不存在,请在系统字典中增加");
                            }
                        }
-                   }else if(modelName.equals("customIndustry")){
+                   }else if(modelName.equals("customIndustryId")){
                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
                            Optional<SysDict> first = sysDictOfCustomIndustry.stream().filter(s -> s.getName().equals(cell.getStringCellValue())).findFirst();
                            if(first.isPresent()){
@@ -565,8 +568,65 @@ public class ClueServiceImpl extends ServiceImpl<ClueMapper, Clue> implements Cl
     }
 
     @Override
-    public HttpRespMsg exportData(Clue clue) {
-        return null;
+    public HttpRespMsg exportData(Clue clue) throws Exception {
+        User user = userMapper.selectById(request.getHeader("token"));
+        SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, user.getCompanyId()).eq(SysForm::getCode, "Thread").eq(SysForm::getIsCurrent, 1));
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+        String config = sysForm.getConfig();
+        JSONObject configOb = JSON.parseObject(config);
+        JSONArray configObJSONArray = configOb.getJSONArray("list");
+        List<List<String>> dataList=new ArrayList<>();
+        List<String> titleList=new ArrayList<>();
+        for (int i = 0; i < configObJSONArray.size(); i++) {
+            JSONObject item = configObJSONArray.getJSONObject(i);
+            titleList.add(item.getString("label"));
+        }
+        dataList.add(titleList);
+        List<Clue> list1 = getList(clue);
+        for (Clue clue1 : list1) {
+            List<String> item=new ArrayList<>();
+            for (int i = 0; i < configObJSONArray.size(); i++) {
+                JSONObject target = configObJSONArray.getJSONObject(i);
+                if(target.getString("type").equals("grid")){
+                    JSONArray columns = target.getJSONArray("columns");
+                    for (int i1 = 0; i1 < columns.size(); i1++) {
+                        JSONObject columnsJSONObject = columns.getJSONObject(i1);
+                        JSONArray list = columnsJSONObject.getJSONArray("list");
+                        for (int i2 = 0; i2 < list.size(); i2++) {
+                            JSONObject object = list.getJSONObject(i2);
+                            String model = object.getString("model");
+                            String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+                            Class<? extends Clue> aClass = clue1.getClass();
+                            String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(clue1)==null?"":aClass.getMethod("get" + targetName).invoke(clue1));
+                            if(model.equals("inchargerId")){
+                                if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                    value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(clue1))+"$";
+                                }else {
+                                    value = String.valueOf(aClass.getMethod("getInchargerName").invoke(clue1));
+                                }
+                            }
+                            item.add(value);
+                        }
+                    }
+                }else {
+                    String model = target.getString("model");
+                    String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+                    Class<? extends Clue> aClass = clue1.getClass();
+                    String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(clue1)==null?"":aClass.getMethod("get" + targetName).invoke(clue1));
+                    if(model.equals("inchargerId")){
+                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                            value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(clue1))+"$";
+                        }else {
+                            value = String.valueOf(aClass.getMethod("getInchargerName").invoke(clue1));
+                        }
+                    }
+                    item.add(value);
+                }
+            }
+            dataList.add(item);
+        }
+        String fileName="线索表导出_"+ System.currentTimeMillis();
+        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,fileName,dataList,path);
     }
 
 

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

@@ -185,21 +185,16 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
         for (CustomItemContacts contact : contacts) {
             ids.add(contact.getCustomId());
         }
-//        List<Contacts> cs = contactsMapper.selectList(new QueryWrapper<Contacts>().in("id", ids));
-        List<Contacts> cs = contactsMapper.selectListByIds(ids);
-        custom1.setContactsList(cs);
+        if (ids.size() > 0){
+            List<Contacts> cs = contactsMapper.selectListByIds(ids);
+            custom1.setContactsList(cs);
+        }
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         httpRespMsg.setData(custom1);
         return httpRespMsg;
     }
 
-//    @Override
-//    public HttpRespMsg getLog(Custom custom, HttpServletRequest request) {
-//        List<ActionLog> actionLogs = actionLogMapper.selectList(new QueryWrapper<ActionLog>().eq("code", "custom").eq("item_id", custom.getId()));
-//        HttpRespMsg httpRespMsg = new HttpRespMsg();
-//        httpRespMsg.setData(actionLogs);
-//        return httpRespMsg;
-//    }
+
 
     @Override
     public HttpRespMsg getList(Custom custom, HttpServletRequest request) {

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

@@ -81,6 +81,12 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
     @Resource
     private ContactsService contactsService;
     @Resource
+    private ProductMapper productMapper;
+    @Resource
+    private OrderProductDetailService orderProductDetailService;
+    @Resource
+    private SysDictService sysDictService;
+    @Resource
     private ClueMapper clueMapper;
     @Value(value = "${upload.path}")
     private String path;
@@ -178,6 +184,9 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         }
         IPage<SalesOrder> orderIPage = salesOrderMapper.selectPage(new Page<>(pageIndex, pageSize), orderLambdaQueryWrapper);
         List<SalesOrder> records = orderIPage.getRecords();
+        List<Integer> orderIds = records.stream().map(SalesOrder::getId).distinct().collect(Collectors.toList());
+        orderIds.add(-1);
+        List<OrderProductDetail> orderProductDetails = orderProductDetailService.list(new LambdaQueryWrapper<OrderProductDetail>().in(OrderProductDetail::getOrderId, orderIds));
         records.forEach(r->{
             Optional<User> item = userList.stream().filter(u -> u.getId().equals(r.getCreatorId())).findFirst();
             if(item.isPresent()){
@@ -199,6 +208,8 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
             if(businessOpportunity.isPresent()){
                 r.setBusinessOpportunityName(businessOpportunity.get().getName());
             }
+            List<OrderProductDetail> details = orderProductDetails.stream().filter(o -> o.getOrderId().equals(r.getId())).collect(Collectors.toList());
+            r.setOrderProductDetailList(details);
         });
         Map map=new HashMap();
         map.put("record",records);
@@ -850,6 +861,43 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         return msg;
     }
 
+    @Override
+    public HttpRespMsg productWithOrder(Integer id) {
+        HttpRespMsg msg=new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
+        List<SysDict> sysDictOfProductUnit = sysDictService.list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, "ProductUnit"));
+        List<SysDict> sysDictOfProductType = sysDictService.list(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCode, "ProductType"));
+        List<OrderProductDetail> orderProductDetails = orderProductDetailService.list(new LambdaQueryWrapper<OrderProductDetail>().eq(OrderProductDetail::getOrderId, id));
+        List<Integer> productIds = orderProductDetails.stream().map(OrderProductDetail::getProductId).collect(Collectors.toList());
+        productIds.add(-1);
+        List<Product> productList = productMapper.selectList(new LambdaQueryWrapper<Product>().in(Product::getId, productIds));
+        productList.forEach(p->{
+            Optional<User> user = userList.stream().filter(u -> u.getId().equals(p.getInchargerId())).findFirst();
+            if(user.isPresent()){
+                p.setInchargerName(user.get().getName());
+            }
+            Optional<User> creator = userList.stream().filter(u -> u.getId().equals(p.getCreatorId())).findFirst();
+            if(creator.isPresent()){
+                p.setCreatorName(creator.get().getName());
+            }
+            Optional<SysDict> unit = sysDictOfProductUnit.stream().filter(u -> u.getId().equals(p.getUnit())).findFirst();
+            if(unit.isPresent()){
+                p.setUnitName(unit.get().getName());
+            }
+            Optional<SysDict> type = sysDictOfProductType.stream().filter(u -> u.getId().equals(p.getType())).findFirst();
+            if(type.isPresent()){
+                p.setTypeName(type.get().getName());
+            }
+            Optional<OrderProductDetail> first = orderProductDetails.stream().filter(o -> o.getProductId().equals(p.getId())).findFirst();
+            if(first.isPresent()){
+                p.setOrderProductDetail(first.get());
+            }
+        });
+        msg.setData(productList);
+        return msg;
+    }
+
     @Override
     public HttpRespMsg businessOpportunityStage(Integer queryType, Integer dateType) {
         HttpRespMsg msg=new HttpRespMsg();

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

@@ -1,8 +1,17 @@
 package com.management.platform.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.management.platform.entity.BusinessOpportunity;
+import com.management.platform.entity.CustomerInfo;
 import com.management.platform.entity.Stage;
+import com.management.platform.entity.User;
+import com.management.platform.mapper.BusinessOpportunityMapper;
 import com.management.platform.mapper.StageMapper;
+import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.StageService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.util.HttpRespMsg;
@@ -26,13 +35,20 @@ public class StageServiceImpl extends ServiceImpl<StageMapper, Stage> implements
 
     @Autowired
     private StageMapper stageMapper;
+    @Autowired
+    private BusinessOpportunityMapper boMapper;
+    @Autowired
+    private UserMapper userMapper;
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public HttpRespMsg changeStage(List<Stage> stages, HttpServletRequest request) {
+    public HttpRespMsg changeStage(String stages, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
+        List<Stage> stages1 = JSONObject.parseArray(stages, Stage.class);
         int i = 0;
-        for (Stage stage : stages) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        for (Stage stage : stages1) {
             stage.setSeq(i);
+            stage.setCompanyId(user.getCompanyId());
             if(null != stage.getId()){
                 stageMapper.updateById(stage);
             }else {
@@ -44,10 +60,27 @@ public class StageServiceImpl extends ServiceImpl<StageMapper, Stage> implements
         return msg;
     }
 
+
+
+
     @Override
-    public HttpRespMsg getStage() {
+    public HttpRespMsg getStage(HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
-        msg.setData(stageMapper.selectList(new QueryWrapper<Stage>().orderBy(true, false, "seq")));
+        msg.setData(stageMapper.selectList(new QueryWrapper<Stage>().eq("company_id",userMapper.selectById(request.getHeader("Token")).getCompanyId()).orderBy(true, true, "seq")));
+        return msg;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public HttpRespMsg deleteStage(Stage stage) {
+        Integer i = boMapper.selectCount(new QueryWrapper<BusinessOpportunity>().eq("stage_id", stage.getId()));
+        HttpRespMsg msg = new HttpRespMsg();
+        if (i > 0){
+            msg.setError("此阶段已使用");
+            return msg;
+        }
+        stageMapper.deleteById(stage.getId());
+        msg.setMsg("操作成功");
         return msg;
     }
 }

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

@@ -10,11 +10,12 @@
         <result column="seal_price" property="sealPrice" />
         <result column="num" property="num" />
         <result column="discount" property="discount" />
+        <result column="total_price" property="totalPrice" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, order_id, product_id, seal_price, num, discount
+        id, order_id, product_id, seal_price, num, discount, total_price
     </sql>
 
 </mapper>

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

@@ -2114,8 +2114,8 @@ public class ReportController {
     }
 
     @RequestMapping("/getUserDailyWorkTime")
-    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception{
-        return reportService.getUserDailyWorkTime(request, startDate, endDate);
+    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception{
+        return reportService.getUserDailyWorkTime(request, startDate, endDate,hasReportDeptId);
     }
 
     @RequestMapping("/getUserDailyWorkTimeReminder")
@@ -2139,8 +2139,8 @@ public class ReportController {
     }
 
     @RequestMapping("/exportUserDailyWorkTime")
-    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception{
-        return reportService.exportUserDailyWorkTime(request, startDate, endDate);
+    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception{
+        return reportService.exportUserDailyWorkTime(request, startDate, endDate,hasReportDeptId);
     }
 
     @RequestMapping("/isWorkDay")

+ 129 - 91
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserWithBeisenController.java

@@ -229,7 +229,12 @@ public class UserWithBeisenController {
                         List<JSONObject> overTimeList = overTimeStream.filter(a -> a.getString("StaffId").equals(beisen.get().getUserId())
                                 && (a.getIntValue("ApproveStatus") == 2||a.getIntValue("ApproveStatus") == 1)).collect(Collectors.toList());
                         //加班数据可能存在结束日期是当前日期的情况的情况
-                        BigDecimal overTimeBigDecimal = new BigDecimal(0);
+                        BigDecimal overTimeBigDecimal;
+                        if(workDay){
+                            overTimeBigDecimal = new BigDecimal(0);
+                        }else {
+                            overTimeBigDecimal = new BigDecimal(8);
+                        }
                         for (JSONObject o : overTimeList) {
                             //存在开始日期为当前日期的数据以及结束日期为当天日期的数据 分开计算
                             if(LocalDateTime.parse(o.getString("StartDate"),df1).toLocalDate().isEqual(localDate)){
@@ -238,16 +243,18 @@ public class UserWithBeisenController {
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 大于才算加班 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        //如果结束时间大于13:00
-                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
-                                            timeDurantion.minusHours(1);
-                                        }
-                                        //如果结束时间大于18:00
-                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
-                                            timeDurantion.minusMinutes(30);
-                                        }
-                                        long l = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+//                                        //如果结束时间大于13:00
+//                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                            timeDurantion=timeDurantion.minusHours(1);
+//                                        }
+//                                        //如果结束时间大于18:00
+//                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                            timeDurantion=timeDurantion.minusMinutes(30);
+//                                        }
+                                        BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l = decimal.doubleValue();
                                         if(l<o.getDouble("OverTimeDuration")){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                         }else {
@@ -259,18 +266,22 @@ public class UserWithBeisenController {
                                     LocalDateTime start = LocalDateTime.parse(o.getString("StartDate"), df1);
                                     LocalDateTime stop = start.toLocalDate().atTime(LocalTime.MAX);
                                     Duration duration = Duration.between(start, stop);
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                        duration.minusHours(1);
-                                    }
-                                    //如果结束时间大于18:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                        duration.minusMinutes(30);
-                                    }
-                                    long l = duration.toMinutes() / 60;
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                        duration=duration.minusHours(1);
+//                                    }
+//                                    //如果结束时间大于18:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                        duration=duration.minusMinutes(30);
+//                                    }
+                                    BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                                    decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                    double l = decimal.doubleValue();
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        long l1 = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                        BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l1 = decimal1.doubleValue();
                                         if(l1<l){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                         }else {
@@ -284,16 +295,18 @@ public class UserWithBeisenController {
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        //如果结束时间大于13:00
-                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
-                                            timeDurantion.minusHours(1);
-                                        }
-                                        //如果结束时间大于18:00
-                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
-                                            timeDurantion.minusMinutes(30);
-                                        }
-                                        long l = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+//                                        //如果结束时间大于13:00
+//                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                            timeDurantion=timeDurantion.minusHours(1);
+//                                        }
+//                                        //如果结束时间大于18:00
+//                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                            timeDurantion=timeDurantion.minusMinutes(30);
+//                                        }
+                                        BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l = decimal.doubleValue();
                                         if(l<o.getDouble("OverTimeDuration")){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                         }else {
@@ -305,19 +318,23 @@ public class UserWithBeisenController {
                                     LocalDateTime stop = LocalDateTime.parse(o.getString("StopDate"), df1);
                                     LocalDateTime start = stop.toLocalDate().atTime(LocalTime.MIN);
                                     Duration duration = Duration.between(start, stop);
-                                    //如果结束时间大于13:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                        duration.minusHours(1);
-                                    }
-                                    //如果结束时间大于18:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                        duration.minusMinutes(30);
-                                    }
-                                    long l = duration.toMinutes() / 60;
+//                                    //如果结束时间大于13:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                        duration=duration.minusHours(1);
+//                                    }
+//                                    //如果结束时间大于18:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                        duration=duration.minusMinutes(30);
+//                                    }
+                                    BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                                    decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                    double l = decimal.doubleValue();
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        long l1 = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                        BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l1 = decimal1.doubleValue();
                                         if(l1<l){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                         }else {
@@ -332,10 +349,12 @@ public class UserWithBeisenController {
                             workTime= workTime+overTimeBigDecimal.doubleValue();
                         }else {
                             //非工作日加班 根据实际打卡时长 比较 加班单时长
-                            if((between.toMinutes()/60)>overTimeBigDecimal.doubleValue()){
+                            BigDecimal decimal = new BigDecimal(between.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            if((decimal.doubleValue())>overTimeBigDecimal.doubleValue()){
                                 workTime= workTime+overTimeBigDecimal.doubleValue();
                             }else {
-                                workTime= workTime+(between.toMinutes()/60);
+                                workTime= workTime+(decimal.doubleValue());
                             }
                         }
                         //处理修改
@@ -428,10 +447,9 @@ public class UserWithBeisenController {
                     leaveSheet.setStatus(jsonObject.getString("ApproveStatus").equals("通过")?0:jsonObject.getString("Reason").equals("审批中")?1:2);
                     leaveSheet.setProcinstId(jsonObject.getString("VacationId"));
                     LeaveSheet one = leaveSheetService.getOne(new LambdaQueryWrapper<LeaveSheet>().eq(LeaveSheet::getProcinstId,leaveSheet.getProcinstId()).eq(LeaveSheet::getOwnerId, first.get().getId()).eq(LeaveSheet::getStartDate, leaveSheet.getStartDate()).eq(LeaveSheet::getEndDate, endDate));
-                    if(one!=null){
-                        leaveSheet.setId(one.getId());
+                    if(one==null){
+                        leaveSheetList.add(leaveSheet);
                     }
-                    leaveSheetList.add(leaveSheet);
                 }
             }
         }
@@ -511,7 +529,12 @@ public class UserWithBeisenController {
                 List<JSONObject> overTimeList = overTimeStream.filter(a -> a.getString("StaffId").equals(beisen.get().getUserId())
                         && (a.getIntValue("ApproveStatus") == 2||a.getIntValue("ApproveStatus") == 1)).collect(Collectors.toList());
                 //加班数据可能存在结束日期是当前日期的情况的情况
-                BigDecimal overTimeBigDecimal = new BigDecimal(0);
+                BigDecimal overTimeBigDecimal;
+                if(workDay){
+                    overTimeBigDecimal = new BigDecimal(0);
+                }else {
+                    overTimeBigDecimal = new BigDecimal(8);
+                }
                 LocalDate localDate = LocalDate.parse(createDate, df);
                 for (JSONObject o : overTimeList) {
                     //存在开始日期为当前日期的数据以及结束日期为当天日期的数据 分开计算
@@ -521,16 +544,19 @@ public class UserWithBeisenController {
                             //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                             if(max.isAfter(LocalTime.parse("19:00:00",df4))){
                                 //判断打卡时间是不是大于19:00 大于才算加班 通过打卡计算加班时长 与加班单作比较 取小
-                                Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max);
-                                //如果结束时间大于13:00
-                                if(max.isAfter(LocalTime.parse("13:00:00",df4))){
-                                    timeDurantion.minusHours(1);
-                                }
-                                //如果结束时间大于18:00
-                                if(max.isAfter(LocalTime.parse("18:00:00",df4))){
-                                    timeDurantion.minusMinutes(30);
-                                }
-                                long l = timeDurantion.toMinutes() / 60;
+                                Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max);
+//                                //如果结束时间大于13:00
+//                                if(max.isAfter(LocalTime.parse("13:00:00",df4))){
+//                                    timeDurantion=timeDurantion.minusHours(1);
+//                                }
+//                                //如果结束时间大于18:00
+//                                if(max.isAfter(LocalTime.parse("18:00:00",df4))){
+//                                    timeDurantion=timeDurantion.minusMinutes(30);
+//                                }
+                                System.out.println(timeDurantion.toMinutes());
+                                BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                double l = decimal.doubleValue();
                                 if(l<o.getDouble("OverTimeDuration")){
                                     overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                 }else {
@@ -542,18 +568,22 @@ public class UserWithBeisenController {
                             LocalDateTime start = LocalDateTime.parse(o.getString("StartDate"), df1);
                             LocalDateTime stop = start.toLocalDate().atTime(LocalTime.MAX);
                             Duration duration = Duration.between(start, stop);
-                            if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                duration.minusHours(1);
-                            }
-                            //如果结束时间大于18:00
-                            if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                duration.minusMinutes(30);
-                            }
-                            long l = duration.toMinutes() / 60;
+//                            if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                duration=duration.minusHours(1);
+//                            }
+//                            //如果结束时间大于18:00
+//                            if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                duration=duration.minusMinutes(30);
+//                            }
+                            BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            double l = decimal.doubleValue();
                             if(max.isAfter(LocalTime.parse("19:00:00",df4))){
                                 //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max);
-                                long l1 = timeDurantion.toMinutes() / 60;
+                                Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max);
+                                BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                double l1 = decimal1.doubleValue();
                                 if(l1<l){
                                     overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                 }else {
@@ -567,16 +597,18 @@ public class UserWithBeisenController {
                             //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                             if(max.isAfter(LocalTime.parse("19:00:00",df4))){
                                 //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max);
-                                //如果结束时间大于13:00
-                                if(max.isAfter(LocalTime.parse("13:00:00",df4))){
-                                    timeDurantion.minusHours(1);
-                                }
-                                //如果结束时间大于18:00
-                                if(max.isAfter(LocalTime.parse("18:00:00",df4))){
-                                    timeDurantion.minusMinutes(30);
-                                }
-                                long l = timeDurantion.toMinutes() / 60;
+                                Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max);
+//                                //如果结束时间大于13:00
+//                                if(max.isAfter(LocalTime.parse("13:00:00",df4))){
+//                                    timeDurantion=timeDurantion.minusHours(1);
+//                                }
+//                                //如果结束时间大于18:00
+//                                if(max.isAfter(LocalTime.parse("18:00:00",df4))){
+//                                    timeDurantion=timeDurantion.minusMinutes(30);
+//                                }
+                                BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                double l = decimal.doubleValue();
                                 if(l<o.getDouble("OverTimeDuration")){
                                     overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                 }else {
@@ -588,19 +620,23 @@ public class UserWithBeisenController {
                             LocalDateTime stop = LocalDateTime.parse(o.getString("StopDate"), df1);
                             LocalDateTime start = stop.toLocalDate().atTime(LocalTime.MIN);
                             Duration duration = Duration.between(start, stop);
-                            //如果结束时间大于13:00
-                            if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                duration.minusHours(1);
-                            }
-                            //如果结束时间大于18:00
-                            if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                duration.minusMinutes(30);
-                            }
-                            long l = duration.toMinutes() / 60;
+//                            //如果结束时间大于13:00
+//                            if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                duration=duration.minusHours(1);
+//                            }
+//                            //如果结束时间大于18:00
+//                            if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                duration=duration.minusMinutes(30);
+//                            }
+                            BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            double l = decimal.doubleValue();
                             if(max.isAfter(LocalTime.parse("19:00:00",df4))){
                                 //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max);
-                                long l1 = timeDurantion.toMinutes() / 60;
+                                Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max);
+                                BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                double l1 = decimal1.doubleValue();
                                 if(l1<l){
                                     overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                 }else {
@@ -615,10 +651,12 @@ public class UserWithBeisenController {
                     workTime= workTime+overTimeBigDecimal.doubleValue();
                 }else {
                     //非工作日加班 根据实际打卡时长 比较 加班单时长
-                    if((between.toMinutes()/60)>overTimeBigDecimal.doubleValue()){
+                    BigDecimal decimal = new BigDecimal(between.toMinutes());
+                    decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                    if((decimal.doubleValue())>overTimeBigDecimal.doubleValue()){
                         workTime= workTime+overTimeBigDecimal.doubleValue();
                     }else {
-                        workTime= workTime+(between.toMinutes()/60);
+                        workTime= workTime+(decimal.doubleValue());
                     }
                 }
                 //处理修改

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java

@@ -62,9 +62,9 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg listByStateDepartment(Integer state, Integer projectId, String date, HttpServletRequest request);
 
-    HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception;
+    HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception;
 
-    HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception;
+    HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception;
 
     HttpRespMsg importData(Integer companyId, Integer withCheckIn, MultipartFile file, HttpServletRequest request);
 

+ 30 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -4300,7 +4300,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception {
+    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception {
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);
         Integer companyId = user.getCompanyId();
@@ -4335,6 +4335,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         //String[] weekDayCHN = {"周一","周二","周三","周四","周五","周六","周日"};
         String[] weekDayCHN = {MessageUtils.message("week.Monday"),MessageUtils.message("week.Tuesday"),MessageUtils.message("week.Wednesday"),MessageUtils.message("week.Thursday"),MessageUtils.message("week.Friday"),MessageUtils.message("week.Saturday"),MessageUtils.message("week.Sunday")};
         HttpRespMsg msg = new HttpRespMsg();
+        List<Integer> branchDepartment = null;
+        if(hasReportDeptId != null){
+            branchDepartment = departmentService.getBranchDepartment(Integer.valueOf(hasReportDeptId),departmentList);
+        }
         List<User> allRangeUserList = new ArrayList<>();
         List<Map<String, Object>> list = null;
         //分角色权限:管理员看全部的,部门负责人看自己部门的,个人只能看自己的。
@@ -4345,12 +4349,12 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             //检查是不是部门负责人(含主要负责人和其他负责人)
             List<Integer> allDeptIds = getAllVisibleDeptIdList(user, allDeptList);
             if (allDeptIds.size() > 0) {
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, allDeptIds, null,user.getId());
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, branchDepartment==null?allDeptIds:branchDepartment, null,user.getId());
                 //部门负责人看自己部门相关的 以及自己的
                 allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("department_id", allDeptIds).or().eq("id",user.getId()).orderByDesc("department_id"));
             } else {
                 //看自己的所负责的项目相关人员的
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId(),user.getId());
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, branchDepartment, user.getId(),user.getId());
                 //项目相关的人员列表
                 List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("incharger_id", user.getId()));
                 List<Project> collect = projectList.stream().collect(Collectors.toList());
@@ -4363,11 +4367,16 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
             }
         } else {
-            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null,user.getId());
+            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, branchDepartment, null,user.getId());
             //管理员看全公司所有人
             allRangeUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).orderByDesc("department_id"));
         }
         List<User> needRangeUserList=new ArrayList<>();
+        //按人员过滤
+        if(branchDepartment!=null&&branchDepartment.size()>0){
+            List<Integer> finalBranchDepartment = branchDepartment;
+            allRangeUserList=allRangeUserList.stream().filter(at-> finalBranchDepartment.contains(at.getDepartmentId())).collect(Collectors.toList());
+        }
         for (User u : allRangeUserList) {
             if(u.getInactiveDate()==null||(u.getInactiveDate()!=null&&u.getInactiveDate().isAfter(LocalDate.parse(startDate))||u.getIsActive()==1)){
                 needRangeUserList.add(u);
@@ -4554,8 +4563,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate) throws Exception {
-        HttpRespMsg msg = getUserDailyWorkTime(request, startDate, endDate);
+    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String startDate, String endDate,Integer hasReportDeptId) throws Exception {
+        HttpRespMsg msg = getUserDailyWorkTime(request, startDate, endDate,hasReportDeptId);
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
         WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", companyId));
         CompanyDingding dingding = companyDingdingService.getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
@@ -5654,19 +5663,30 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     }
                 }
                 Department finalDept = dept;
-                Optional<User> manager = userList.stream().filter(u -> u.getId().equals(finalDept.getManagerId())).findFirst();
+
+                String managerName = null;
+                if (finalDept != null) {
+                    User manageUser = null;
+                    Optional<User> manager = userList.stream().filter(u -> u.getId().equals(finalDept.getManagerId())).findFirst();
+                    if (manager.isPresent()) {
+                        manageUser = manager.get();
+                    }
+                    if (manageUser != null) {
+                        managerName = manageUser.getName();
+                    }
+                }
                 if(needCorpWxTranslate){
                     item.add("$userName="+(map.get("corpwxUserId")==null?"":map.get("corpwxUserId"))+"$");
                     item.add(departmentService.exportWxDepartment(dept,departments));
-                    item.add(manager.isPresent()?("$userName="+manager.get().getName()+"$"):"");
+                    item.add(managerName != null?("$userName="+managerName+"$"):"");
                 }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
                     item.add("$userName="+(map.get("name")==null?"":map.get("name"))+"$");
                     item.add(departmentService.exportDdDepartment(dept,departments));
-                    item.add(manager.isPresent()?("$userName="+manager.get().getName()+"$"):"");
+                    item.add(managerName != null?("$userName="+managerName+"$"):"");
                 }else  {
                     item.add((String) map.get("name"));
                     item.add(departmentService.getSupDepartment(dept,departments));
-                    item.add(manager.isPresent()?manager.get().getName():"");
+                    item.add(managerName != null?managerName:"");
                 }
                 item.add((String) map.get("projectCode"));
                 item.add((String) map.get("project"));

+ 65 - 47
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -2180,7 +2180,12 @@ public class TimingTask {
                         List<JSONObject> overTimeList = overTimeStream.filter(a -> a.getString("StaffId").equals(beisen.get().getUserId())
                                 && (a.getIntValue("ApproveStatus") == 2||a.getIntValue("ApproveStatus") == 1)).collect(Collectors.toList());
                         //加班数据可能存在结束日期是当前日期的情况的情况
-                        BigDecimal overTimeBigDecimal = new BigDecimal(0);
+                        BigDecimal overTimeBigDecimal;
+                        if(workDay){
+                            overTimeBigDecimal = new BigDecimal(0);
+                        }else {
+                            overTimeBigDecimal = new BigDecimal(8);
+                        }
                         for (JSONObject o : overTimeList) {
                             //存在开始日期为当前日期的数据以及结束日期为当天日期的数据 分开计算
                             if(LocalDateTime.parse(o.getString("StartDate"),df1).toLocalDate().isEqual(localDate)){
@@ -2189,16 +2194,18 @@ public class TimingTask {
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        //如果结束时间大于13:00
-                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
-                                            timeDurantion.minusHours(1);
-                                        }
-                                        //如果结束时间大于18:00
-                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
-                                            timeDurantion.minusMinutes(30);
-                                        }
-                                        long l = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+//                                        //如果结束时间大于13:00
+//                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                            timeDurantion.minusHours(1);
+//                                        }
+//                                        //如果结束时间大于18:00
+//                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                            timeDurantion.minusMinutes(30);
+//                                        }
+                                        BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l = decimal.doubleValue();
                                         if(l<o.getDouble("OverTimeDuration")){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                         }else {
@@ -2210,18 +2217,22 @@ public class TimingTask {
                                     LocalDateTime start = LocalDateTime.parse(o.getString("StartDate"), df1);
                                     LocalDateTime stop = start.toLocalDate().atTime(LocalTime.MAX);
                                     Duration duration = Duration.between(start, stop);
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                        duration.minusHours(1);
-                                    }
-                                    //如果结束时间大于18:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                        duration.minusMinutes(30);
-                                    }
-                                    long l = duration.toMinutes() / 60;
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                        duration.minusHours(1);
+//                                    }
+//                                    //如果结束时间大于18:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                        duration.minusMinutes(30);
+//                                    }
+                                    BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                                    decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                    double l = decimal.doubleValue();
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        long l1 = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                        BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l1 = decimal1.doubleValue();
                                         if(l1<l){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                         }else {
@@ -2235,16 +2246,18 @@ public class TimingTask {
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        //如果结束时间大于13:00
-                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
-                                            timeDurantion.minusHours(1);
-                                        }
-                                        //如果结束时间大于18:00
-                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
-                                            timeDurantion.minusMinutes(30);
-                                        }
-                                        long l = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+//                                        //如果结束时间大于13:00
+//                                        if(max.get().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                            timeDurantion.minusHours(1);
+//                                        }
+//                                        //如果结束时间大于18:00
+//                                        if(max.get().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                            timeDurantion.minusMinutes(30);
+//                                        }
+                                        BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l = decimal.doubleValue();
                                         if(l<o.getDouble("OverTimeDuration")){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l));
                                         }else {
@@ -2256,19 +2269,23 @@ public class TimingTask {
                                     LocalDateTime stop = LocalDateTime.parse(o.getString("StopDate"), df1);
                                     LocalDateTime start = stop.toLocalDate().atTime(LocalTime.MIN);
                                     Duration duration = Duration.between(start, stop);
-                                    //如果结束时间大于13:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
-                                        duration.minusHours(1);
-                                    }
-                                    //如果结束时间大于18:00
-                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
-                                        duration.minusMinutes(30);
-                                    }
-                                    long l = duration.toMinutes() / 60;
+//                                    //如果结束时间大于13:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("13:00:00",df4))){
+//                                        duration.minusHours(1);
+//                                    }
+//                                    //如果结束时间大于18:00
+//                                    if(stop.toLocalTime().isAfter(LocalTime.parse("18:00:00",df4))){
+//                                        duration.minusMinutes(30);
+//                                    }
+                                    BigDecimal decimal = new BigDecimal(duration.toMinutes());
+                                    decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                    double l = decimal.doubleValue();
                                     if(max.get().isAfter(LocalTime.parse("19:00:00",df4))){
                                         //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                        Duration timeDurantion = Duration.between(LocalTime.parse("19:00:00", df4), max.get());
-                                        long l1 = timeDurantion.toMinutes() / 60;
+                                        Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                        BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
+                                        decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                                        double l1 = decimal1.doubleValue();
                                         if(l1<l){
                                             overTimeBigDecimal=overTimeBigDecimal.add(new BigDecimal(l1));
                                         }else {
@@ -2283,10 +2300,12 @@ public class TimingTask {
                             workTime= workTime+overTimeBigDecimal.doubleValue();
                         }else {
                             //非工作日加班 根据实际打卡时长 比较 加班单时长
-                            if((between.toMinutes()/60)>overTimeBigDecimal.doubleValue()){
+                            BigDecimal decimal = new BigDecimal(between.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            if((decimal.doubleValue())>overTimeBigDecimal.doubleValue()){
                                 workTime= workTime+overTimeBigDecimal.doubleValue();
                             }else {
-                                workTime= workTime+(between.toMinutes()/60);
+                                workTime= workTime+(decimal.doubleValue());
                             }
                         }
                         List<JSONObject> vacationList = vacationStream.filter(a ->{
@@ -2376,10 +2395,9 @@ public class TimingTask {
                     leaveSheet.setStatus(jsonObject.getString("ApproveStatus").equals("通过")?0:jsonObject.getString("Reason").equals("审批中")?1:2);
                     leaveSheet.setProcinstId(jsonObject.getString("VacationId"));
                     LeaveSheet one = leaveSheetService.getOne(new LambdaQueryWrapper<LeaveSheet>().eq(LeaveSheet::getOwnerId, first.get().getId()).eq(LeaveSheet::getStartDate, leaveSheet.getStartDate()).eq(LeaveSheet::getProcinstId,leaveSheet.getProcinstId()).eq(LeaveSheet::getEndDate, endDate));
-                    if(one!=null){
-                        leaveSheet.setId(one.getId());
+                    if(one==null){
+                        leaveSheetList.add(leaveSheet);
                     }
-                    leaveSheetList.add(leaveSheet);
                 }
             }
         }

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

@@ -896,8 +896,7 @@
         FROM user
         left join report on user.id = report.creator_id
         left join department on department.department_id = user.department_id
-        WHERE (report.state = 0 or report.state = 1)
-        and user.company_id = #{companyId} and (user.is_active=1 or(user.is_active=0 and user.inactive_date>=#{startDate}))
+        WHERE user.company_id = #{companyId} and (user.is_active=1 or(user.is_active=0 and user.inactive_date>=#{startDate}))
         <if test="deptIds != null">
             AND ((user.department_id in
             <foreach collection="deptIds" separator="," index="index" item="item" close=")" open="(">

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

@@ -260,7 +260,7 @@
     "enterpriseWeChatReminder": "企业微信催填",
     "dingNingurgefilling": "钉钉催填",
     "weChatReminder": "微信催填",
-    "completedHours": "已填工时情况",
+    "completedHours": "工时填报情况",
     "unfilledPersonList": "未填人员列表",
     "missingDate": "未填日期",
     "attendanceData": "考勤数据",

+ 46 - 12
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -1426,11 +1426,19 @@
               <!-- 222 -->
             </div>
             <div>
+                <el-cascader v-if="user.userNameNeedTranslate != 1" :size="'small'" v-model="deptIdForHasReport" placeholder="请选择部门" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 200px;" @change="showMonthWorkTime()"></el-cascader>
+                <vueCascader :subjectId="deptIdForHasReport" :size="'small'" :widthStr="'200'" :clearable="true" :subject="trandepartmentList" :radios="true" :distinction="'14'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')"></vueCascader>
                 <el-input style="float:left;width:22%" v-if="user.userNameNeedTranslate != '1'" v-model="searchKeyword" @keyup.enter.native="searchScreen(0)" class="input-with-select" :placeholder="$t('defaultText.pleaseEnterNametoSearch')" clearable="true" size="small">
                     <el-button slot="append" @click="searchScreen(0)" icon="el-icon-search"></el-button>
                 </el-input>
                 <selectCat v-if="user.userNameNeedTranslate == '1'" :filterable="true"  :searchBoxTop="'1'" :size="'small'" :subject="usersList" :subjectId="usersListId" :distinction="'12'" :clearable="true" @selectCal="selectCal"></selectCat>
             </div>
+            <div style="float: left; height:32px; padding-top:10px;">
+              <span style="width:20px;height:20px;background-color: orange;padding:5px 8px;">待审核</span>
+              <span style="width:20px;height:20px;background-color: #32CD32;padding:5px 8px;margin-left:10px;">已通过</span>
+              <span style="width:20px;height:20px;background-color: red;padding:5px 8px;margin-left:10px;">已驳回</span>
+              <span style="width:20px;height:20px;background-color: #E0E0E0;padding:5px 8px;margin-left:10px;">未提交</span>
+            </div>
             <div style="float: right; vertical-align: middle;height:32px">
               <el-link
               type="primary"
@@ -1439,6 +1447,7 @@
              >
             </div>
             
+            
           </div>
 
           <el-table ref="hasworkTbl"
@@ -1604,7 +1613,7 @@
             <div>
                 <div style="margin-top:10px;">
                     <el-cascader v-if="user.userNameNeedTranslate != 1" :size="'small'" v-model="deptIdForNoReport" placeholder="请选择部门" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 200px;" @change="showMonthNotWorkTime()"></el-cascader>
-                    <vueCascader :size="'small'" :widthStr="'200'" :clearable="true" :subject="departmentList" :radios="true" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')" @change="showMonthNotWorkTime()"></vueCascader>
+                    <vueCascader :size="'small'" :widthStr="'200'" :clearable="true" :subject="trandepartmentList" :radios="true" :distinction="'15'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')"></vueCascader>
                     <el-input style="width:200px;margin-left: 15px" v-if="user.userNameNeedTranslate != '1'" @keyup.enter.native="searchScreen(1)" v-model="searchKeyword" class="input-with-select" :placeholder="$t('defaultText.pleaseEnterNametoSearch')" clearable="true" size="small">
                         <el-button slot="append" @click="searchScreen(1)" icon="el-icon-search"></el-button>
                     </el-input>
@@ -1692,12 +1701,12 @@
             </div>
             <div>
                 <div style="margin-top:10px;">
+                    <el-cascader v-if="user.userNameNeedTranslate != 1" :size="'small'" v-model="deptIdForReminder" placeholder="请选择部门" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 200px;" @change="showMonthWorkTimeReminder()"></el-cascader>
+                    <vueCascader :size="'small'" :widthStr="'200'" :clearable="true" :subject="trandepartmentList" :radios="true" :distinction="'16'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')"></vueCascader>
                     <el-input style="float:left;width:18%" v-if="user.userNameNeedTranslate != '1'" @keyup.enter.native="searchScreen(0)" v-model="searchKeyword" class="input-with-select" :placeholder="$t('defaultText.pleaseEnterNametoSearch')" clearable="true" size="small">
                         <el-button slot="append" @click="searchScreen(0)" icon="el-icon-search"></el-button>
                     </el-input>
                     <selectCat v-if="user.userNameNeedTranslate == '1'" :filterable="true"  :searchBoxTop="'1'" :size="'small'" :subject="usersList" :subjectId="usersListId" :distinction="'12'" :clearable="true" @selectCal="selectCal"></selectCat>
-                    <el-cascader v-if="user.userNameNeedTranslate != 1" :size="'small'" v-model="deptIdForReminder" placeholder="请选择部门" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 200px;" @change="showMonthWorkTimeReminder()"></el-cascader>
-                    <vueCascader :size="'small'" :widthStr="'200'" :clearable="true" :subject="departmentList" :radios="true" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')" @change="showMonthWorkTimeReminder()"></vueCascader>
                     <el-checkbox v-model="isReminder" @change="showMonthWorkTimeReminder()">是否异常</el-checkbox>
                 </div>
             </div>
@@ -2421,6 +2430,7 @@
 
                 notifySelList:[],
                 departmentList: [],
+                trandepartmentList:[],
 
                 approvalProcessDialog: false,
                 approvalProcessData: [],
@@ -2476,6 +2486,7 @@
                 userReportDeptList: [],
                 isReminder:true,
                 reportLogCheckDialog:false,
+                deptIdForHasReport:[],
             };
         },
         watch: {
@@ -2569,18 +2580,21 @@
 
                     const isWorkDate = filterData[0] && filterData[0].workingTime
                     const state = filterData[0] && filterData[0].state
-                    if (state == 1) {
+                    if (state == 0 || state == -1) {
+                        return "backgroundColor: orange"
+                    } else if (state == 1) {
                         //审核通过
                         return "backgroundColor: #32CD32"
                     } else if (state == 2) {
-                        return "backgroundColor: #FF0000"
+                        return "backgroundColor: red"
                     } else {
-                        if(isWorkDate < times) {
-                            return "backgroundColor: #FC3D49"
-                        }
-                        if(isWorkDate > times) {
-                            return "backgroundColor: #20a0ff"
-                        }
+                        return "backgroundColor: #E0E0E0"
+                        // if(isWorkDate < times) {
+                        //     return "backgroundColor: #FC3D49"
+                        // }
+                        // if(isWorkDate > times) {
+                        //     return "backgroundColor: #20a0ff"
+                        // }
                     }
                     
                 }
@@ -4041,6 +4055,7 @@
         let parameter={
             startDate: this.WorktimeDatepickValue[0],
             endDate: this.WorktimeDatepickValue[1],
+            hasReportDeptId:this.deptIdForHasReport.length>0?this.deptIdForHasReport[this.deptIdForHasReport.length-1]:null,
         }
       this.http.post(
         "/report/getUserDailyWorkTime",
@@ -5274,6 +5289,7 @@
                         }
                         this.departmentList = JSON.parse(JSON.stringify(res.data))
                         var list = res.data , list1 = JSON.parse(JSON.stringify(res.data));
+                        this.trandepartmentList=this.changeArr(JSON.parse(JSON.stringify(res.data)))
                         // let noAllData = JSON.parse(JSON.stringify(res.data));
                         // if (this.user.role > 0) {
                             
@@ -8489,7 +8505,7 @@
                 this.workForm.domains[obj.idx].projectAuditorId = obj.id
             },
             vueCasader(obj) {
-                console.log(obj, '看看值')
+                console.log(obj, '看看值', this.departmentList)
                 if(obj.distinction == 1 && obj.item) {
                     let arr = []
                     arr.push(obj.item.value)
@@ -8500,6 +8516,24 @@
                     const { id, other } = obj
                     this.workForm.domains[other].reportTargetDeptId = id
                 }
+                if(obj.distinction == '14' && obj) {
+                    let arr = []
+                    arr.push(obj.id)
+                    this.deptIdForHasReport = arr
+                    this.showMonthWorkTime()
+                }
+                if(obj.distinction == '15' && obj) {
+                    let arr = []
+                    arr.push(obj.id)
+                    this.deptIdForNoReport = arr
+                    this.showMonthNotWorkTime()
+                }
+                if(obj.distinction == '16' && obj) {
+                    let arr = []
+                    arr.push(obj.id)
+                    this.deptIdForReminder = arr
+                    this.showMonthWorkTimeReminder()
+                }
             },
             //分页
             handleCurrentChange(val) {