ソースを参照

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/CustomService.java
Guo1B0 1 年間 前
コミット
49a73ea3e2
60 ファイル変更2875 行追加574 行削除
  1. 3 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  2. 4 6
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/attachment.vue
  3. 17 4
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/information.vue
  4. 8 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/operationRecord.vue
  5. 43 15
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/products.vue
  6. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/stageSetting.vue
  7. 115 18
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue
  8. 18 12
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  9. 2 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts
  10. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/index.vue
  11. 85 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/corpreport/api.ts
  12. 160 55
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/corpreport/index.vue
  13. 6 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/api.ts
  14. 104 45
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/attachment.vue
  15. 188 17
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/information.vue
  16. 17 33
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/operationRecord.vue
  17. 142 45
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedBusiness.vue
  18. 104 46
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedContacts.vue
  19. 152 46
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedOrders.vue
  20. 21 14
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/detail/index.vue
  21. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/index.vue
  22. 13 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/api.ts
  23. 161 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/attachment.vue
  24. 254 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/information.vue
  25. 38 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/operationRecord.vue
  26. 185 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/products.vue
  27. 134 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/rebate.vue
  28. 175 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/detail/index.vue
  29. 6 7
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue
  30. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/product/detail/index.vue
  31. 6 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ImportModal.vue
  32. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/api.ts
  33. 20 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/customInstructions.ts
  34. 14 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/times.ts
  35. 26 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts
  36. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts
  37. 26 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ContactsController.java
  38. 14 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ReportController.java
  39. 107 11
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SalesOrderController.java
  40. 3 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/ContactsLog.java
  41. 6 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SalesOrder.java
  42. 24 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SalesOrderPayment.java
  43. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ContactsService.java
  44. 3 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/CustomService.java
  45. 4 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/ReportService.java
  46. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/SalesOrderService.java
  47. 192 73
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContactsServiceImpl.java
  48. 2 2
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/CustomServiceImpl.java
  49. 57 6
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  50. 68 11
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java
  51. 24 16
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/TaskServiceImpl.java
  52. 3 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/SalesOrderPaymentMapper.xml
  53. 5 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserWithBeisenController.java
  54. 4 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  55. 42 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  56. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  57. 42 42
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  58. 2 0
      fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue
  59. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/project/summary.vue
  60. 9 1
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

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

@@ -21,6 +21,9 @@ export const UPLOADFILEFILE = `/business-opportunity/uploadFile`
 export const URL_IMPOERBUSINESS = `/business-opportunity/importData`
 export const URL_DETELESTAGE = `/business-opportunity/deleteStage`
 export const URL_SAVECONTACT = `/business-opportunity/saveContactsId`
+export const URL_STAGEIDNEXT = `/business-opportunity/saveStageId`
+export const URL_SAVEREASON = `/business-opportunity/saveReason`
+export const URL_EXPORTBUSINESS = `/business-opportunity/exportData`
 
 
 export const stageStatus = [

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

@@ -87,11 +87,9 @@ function saveEditClue() {
     }
     const bussinessId = information.value.id
     post(REFIENAMEFILE, { name: renameVal.value, id: bussinessId }).then((res) => {
-        if (res.code == 'ok') {
-            renameDialogVisible.value = false
-            globalPopup?.showSuccess(res.msg || '')
-            emits('refreshData')
-        }
+        renameDialogVisible.value = false
+        globalPopup?.showSuccess(res.msg || '')
+        emits('refreshData')
     }).catch((_err) => { })
 }
 
@@ -121,7 +119,7 @@ async function httpUploadFile(param: UploadRequestOptions) {
 
 watchEffect(() => {
     information.value = props.information
-    attachmenttable.value = props.information.uploadFilePList
+    attachmenttable.value = (props.information.uploadFilePList || [])
 });
 
 // 生命周期钩子

+ 17 - 4
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/information.vue

@@ -3,7 +3,7 @@
         <div class="flex justify-between">
             <div class="title">基本信息</div>
             <div>
-                <el-button type="primary" @click="associateContact()" v-if="!information.cuntactsId">关联联系人</el-button>
+                <el-button type="primary" @click="associateContact()" v-if="!information.contactsId">关联联系人</el-button>
                 <el-button type="primary" @click="claimBusiness()" v-if="!information.customerId">认领</el-button>
                 <el-button type="primary" @click="showVisible('transferBusinessVisible')"
                     v-if="information.customerId">转移</el-button>
@@ -132,6 +132,7 @@ const transferValue = ref('')
 const transferOptions = ref<personnelInterface[]>([])
 const generateFormValue = ref({})
 const generateForm = ref<typeof GenerateForm>() // 自定义表单dom
+const productTableListValue = ref<any[]>([])
 const contactsId = ref('')
 const contactsList = ref<optionType[]>([])
 const allVisible = reactive({
@@ -196,17 +197,16 @@ function transferBusiness() {
 
 function claimBusiness() {
     confirmAction(`确定认领【${information.value.name}】商机吗?`).then(() => {
-        console.log('执行认领')
+        transferBusiness()
     })
 }
 
 function editBusiness() {
-    console.log('点击了保存')
-    return
     generateForm.value?.getData().then((res: any) => {
         let formVal = {
             ...res,
             expectedTransactionDate: res.expectedTransactionDate ? formatDateTime(new Date(res.expectedTransactionDate)) : '',
+            businessItemProductList: JSON.stringify(productTableListValue.value)
         }
         allLoading.businessSaveLading = true
         post(UPDATEINSET, { ...formVal }).then((_res) => {
@@ -222,10 +222,23 @@ function editBusiness() {
     })
 }
 
+function editProductShow() {
+    const productList = information.value.businessItemProducts || []
+    const list = productList.map((item: any) => {
+        const { id, productName, productId, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail, num, discount, sealPrice, totalPrice, quantity } = item
+        return {
+            id, productId: productId, productName, productCode, unit, unitName, typeName, type, price, inventory,
+            num, discount, sealPrice, totalPrice, quantity
+        }
+    })
+    productTableListValue.value = list
+}
+
 function showVisible(type: keyof typeof allVisible) {
     if (type == 'editBusinessVisible') {
         const { name, customerId, contactsId, amountOfMoney, expectedTransactionDate, stageId, inchargerId, remark } = information.value
         generateFormValue.value = { name, customerId, contactsId, amountOfMoney, expectedTransactionDate, stageId, inchargerId, remark }
+        editProductShow()
     }
     allVisible[type] = true
 }

+ 8 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/operationRecord.vue

@@ -5,7 +5,7 @@
         </div>
         <div class="flex-1 overflow-auto pt-5">
             <el-table :data="operationRecordtable" border style="width: 100%;height: 278px;">
-                <el-table-column prop="creatTime" label="操作时间" width="140" />
+                <el-table-column prop="newCreatTime" label="操作时间" width="160" />
                 <el-table-column prop="userName" label="操作人" width="120" />
                 <el-table-column prop="name" label="操作内容" />
             </el-table>
@@ -13,6 +13,7 @@
     </div>
 </template>
 <script lang="ts" setup>
+import { formatDateMinutes } from '@/utils/times';
 import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
 
 const props = defineProps<{
@@ -24,7 +25,12 @@ const information = ref<any>({})
 
 watchEffect(() => {
     information.value = props.information
-    operationRecordtable.value = props.information.actionLogList
+    operationRecordtable.value = (props.information.actionLogList || []).map((item: any) => {
+        return {
+            ...item,
+            newCreatTime: item.creatTime ? formatDateMinutes(new Date(item.creatTime)) : ''
+        }
+    })
 });
 
 // 生命周期钩子

+ 43 - 15
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/products.vue

@@ -3,7 +3,7 @@
         <div class="flex justify-between">
             <div class="title">相关产品</div>
             <div>
-                <el-button type="primary" @click="showVisible('editProductVisible')">编辑产品</el-button>
+                <el-button type="primary" @click="editProductShow()">编辑产品</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">
@@ -13,22 +13,21 @@
                         {{ scope.$index + 1 }}
                     </template>
                 </el-table-column>
-                <el-table-column prop="taskName" label="产品名称">
+                <el-table-column prop="productName" label="产品名称">
                     <template #default="scope">
                         <el-button link type="primary" size="large">{{
-                            scope.row.taskName
+                            scope.row.productName
                         }}</el-button>
                     </template>
                 </el-table-column>
-                <el-table-column prop="priority" label="产品类别" width="130" />
-                <el-table-column prop="status" label="产品类型" width="130" />
-                <el-table-column prop="executor" label="单位" width="130" />
-                <el-table-column prop="startTime" label="标准价格" width="130" />
-                <el-table-column prop="endTime" label="库存" width="130" />
-                <el-table-column prop="endTime" label="售价" width="130" />
-                <el-table-column prop="endTime" label="数量" width="130" />
-                <el-table-column prop="endTime" label="折扣(%)" width="130" />
-                <el-table-column prop="endTime" label="合计" width="130" />
+                <el-table-column prop="typeName" label="产品类型" width="130" />
+                <el-table-column prop="unitName" label="单位" width="130" />
+                <el-table-column prop="price" label="标准价格" width="130" />
+                <el-table-column prop="inventory" label="库存" width="130" />
+                <el-table-column prop="sellingPrice" label="售价" width="130" />
+                <el-table-column prop="quantity" label="数量" width="130" />
+                <el-table-column prop="discount" label="折扣(%)" width="130" />
+                <el-table-column prop="totalPrice" label="合计" width="130" />
             </el-table>
         </div>
 
@@ -45,7 +44,8 @@
                 </div>
             </template>
             <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
-                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" :height="'420px'" />
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList"
+                    :productTableListValue="productTableListValue" :height="'420px'" />
             </div>
         </el-dialog>
     </div>
@@ -56,14 +56,18 @@ import { GETTABLELIST } from '@/pages/product/api';
 import { post } from '@/utils/request';
 
 import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import { UPDATEINSET } from '../api';
+import { all } from 'axios';
 
+const emits = defineEmits(['refreshData']);
 const props = defineProps<{
     information: any
 }>()
-
+const globalPopup = inject<GlobalPopup>('globalPopup')
 const information = ref<any>({})
 const relatedTaskstable = ref([])
 const productTableList = ref<any>([])
+const productTableListValue = ref<any>([])
 const relatedProductsRef = ref<typeof RelatedProducts>()
 const allVisible = reactive({
     editProductVisible: false
@@ -74,9 +78,32 @@ const allLoading = reactive({
 
 function editProduct() {
     let productTableListData = relatedProductsRef?.value?.returnData()
+    productTableListData.forEach((item: any) => {
+        delete item.id
+    })
     const { id, name, customerId, contactsId, amountOfMoney, expectedTransactionDate, stageId, inchargerId, remark } = information.value
     const formData = { id, name, customerId, contactsId, amountOfMoney, expectedTransactionDate, stageId, inchargerId, remark }
-    console.log(productTableListData, '<===== 将要提交的数据', formData)
+    allLoading.editProductLoading = true
+    post(UPDATEINSET, { ...formData, businessItemProductList: JSON.stringify(productTableListData) }).then((_res) => {
+        allVisible.editProductVisible = false
+        globalPopup?.showSuccess('操作成功')
+        emits('refreshData')
+    }).finally(() => {
+        allLoading.editProductLoading = false
+    })
+}
+
+function editProductShow() {
+    const productList = information.value.businessItemProducts || []
+    const list = productList.map((item: any) => {
+        const { id, productName, productId, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail, num, discount, sealPrice, totalPrice, quantity } = item
+        return {
+            id, productId: productId, productName, productCode, unit, unitName, typeName, type, price, inventory,
+            num, discount, sealPrice, totalPrice, quantity
+        }
+    })
+    productTableListValue.value = list
+    showVisible('editProductVisible')
 }
 
 function showVisible(type: keyof typeof allVisible) {
@@ -113,6 +140,7 @@ function getProductTableList() {
 
 watchEffect(() => {
     information.value = props.information
+    relatedTaskstable.value = props.information?.businessItemProducts || []
 });
 
 onMounted(() => {

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

@@ -28,7 +28,7 @@
                         </el-table-column>
                         <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="primary" size="large" @click="addStage(scope.row)" :disabled="scope.row.isFinish == 1">编辑</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>

+ 115 - 18
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/detail/index.vue

@@ -7,25 +7,41 @@
         </el-link>
       </div>
       <div class="mr-8">
-        <el-select v-model="optionVal" placeholder="请选择" style="width: 150px" filterable @change="getDetail()">
+        <el-select v-model="optionVal" placeholder="请选择" style="width: 150px" filterable @change="selectChange()">
           <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
       </div>
       <div class="flex-1 flex h-full justify-end overflow-auto scroll-bar-hide cursor-pointer" @wheel="handleScroll">
-        <div :class="`${index === 0 ? 'startStep' : 'nextStep'} relative rounded-md flex items-center backGray pl-6 pr-6`"
+        <div
+          :class="`${index === 0 ? 'startStep' : 'nextStep'} ${(currentStage >= index || businessInfo.stageValue == '赢单') ? 'selected' : (currentStage >= index || businessInfo.stageValue == '输单') ? 'backOrange' : 'backGray'} relative rounded-md flex items-center pl-6 pr-6`"
           v-for="(item, index) in stageList" :key="index">
           <div class="pr-3 text-nowrap">{{ item.name }}</div>
           <div class="text-nowrap">{{ item.plan }}</div>
         </div>
       </div>
-      <div class="relative rounded-md flex items-center itemPing backGray endStep item pl-6 pr-6 mr-4 resetStyle">
+      <div class="relative rounded-md flex items-center itemPing backGray endStep item pl-6 pr-6 mr-4 resetStyle" v-if="currentStage >= 0">
         <el-select v-model="stageStatusVal" placeholder="结束" style="width: 100px" class="selectClas"
           @change="advanceChange()">
           <el-option v-for="(item, index) in stageListOption" :key="index" :label="item.label" :value="item.value" />
         </el-select>
       </div>
-      <div class="bg-[#075985] rounded-md text itemPing pl-2 pr-2 flex items-center aloneText" @click="advancementStage()">
-        <el-link :underline="false">推进至阶段【验证客户】</el-link>
+      <div class="relative rounded-md flex items-center justify-around backOrange endStep item pl-6 pr-6 mr-4 resetStyle" v-if="businessInfo.stageValue == '输单'" style="padding-top: 6px;padding-bottom: 6px;">
+        <div>{{ businessInfo.stageValue }}</div>
+        <div class="ml-5">0%</div>
+      </div>
+      <div class="relative rounded-md flex items-center justify-around backWan endStep item pl-6 pr-6 mr-4 resetStyle" v-if="businessInfo.stageValue == '赢单'" style="padding-top: 6px;padding-bottom: 6px;">
+        <div>{{ businessInfo.stageValue }}</div>
+        <div class="ml-4"> 100%</div>
+      </div>
+      <div class="relative rounded-md flex items-center justify-around backGray endStep item pl-6 pr-6 mr-4 resetStyle" v-if="businessInfo.stageValue == '无效'" style="padding-top: 6px;padding-bottom: 6px;">
+        <div>{{ businessInfo.stageValue }}</div>
+        <div class="ml-5">0%</div>
+      </div>
+      <div class="bg-[#075985] rounded-md text itemPing pl-2 pr-2 flex items-center aloneText" @click="advancementStage()"
+        v-if="currentStage != -1">
+        <el-link :underline="false" v-if="(currentStage + 1) != stageList.length">推进至下个阶段【{{ stageList[currentStage +
+          1]?.name }}】</el-link>
+        <el-link :underline="false" v-else>赢单</el-link>
       </div>
     </div>
     <!-- 内容 -->
@@ -52,7 +68,7 @@
 
       <div class="w-full h-auto flex justify-between mt-2">
         <div class="bg-white shadow-md rounded-md w-full">
-          <Products :information="businessInfo" />
+          <Products :information="businessInfo" @refreshData="getDetail" />
         </div>
       </div>
     </div>
@@ -82,7 +98,7 @@ import type { FormInstance, FormRules } from 'element-plus'
 import { Edit, ArrowLeft as IconView } from '@element-plus/icons-vue'
 import { backPath } from '@/utils/tools'
 import { useRoute } from "vue-router";
-import { BUSIESS_ALL, BUSIESS_GETSATE, BUSIESS_INFO } from '../api'
+import { BUSIESS_ALL, BUSIESS_GETSATE, BUSIESS_INFO, URL_SAVEREASON, URL_STAGEIDNEXT } from '../api'
 
 import Information from '../component/information.vue'
 import Attachment from '../component/attachment.vue'
@@ -94,7 +110,8 @@ import { post } from "@/utils/request";
 
 type stageListType = {
   name: string,
-  plan: string
+  plan: string,
+  id?: number,
 }
 
 const route = useRoute()
@@ -105,6 +122,7 @@ const stageStatusVal = ref('')
 const stageStatusValOriginally = ref('')
 const advanceVal = ref('')
 const options = ref<optionType[]>([])
+const currentStage = ref<any>('') // 当前阶段的下标
 const allLoading = reactive({
   skeletonLoading: false,
   advanceSaveLoading: false
@@ -116,7 +134,7 @@ const allText = reactive({
   advanceText: ''
 })
 
-const businessInfo = ref({})
+const businessInfo = ref<any>({})
 const stageListOption = ref<optionType[]>([])
 const stageList = ref<stageListType[]>([])
 
@@ -139,8 +157,20 @@ function advanceChange() {
     allVisible.advanceVisible = true
     return
   }
-
-  advanceSave(false)
+  console.log(item, '<=====')
+  allLoading.skeletonLoading = true
+  post(URL_STAGEIDNEXT, {
+    id: businessInfo.value.id,
+    stageId: item.value,
+    stageValue: item.label,
+  }).then((_res) => {
+    globalPopup?.showSuccess('操作成功')
+    selectChange()
+  }).finally(() => {
+    setTimeout(() => {
+      allLoading.skeletonLoading = false
+    }, 500)
+  })
 }
 
 function advanceClose() {
@@ -149,16 +179,57 @@ function advanceClose() {
 }
 
 function advanceSave(flag: boolean) {
-  if(!advanceVal && flag) {
+  if (!advanceVal.value && flag) {
     globalPopup?.showError(`请输入${allText.advanceText}原因`)
     return
   }
-  allLoading.advanceSaveLoading = true
-  post('接口', {}).then(() => {
+  console.log(flag)
+  if (flag) {
+    allLoading.advanceSaveLoading = true
+    advancementStageNext(true)
+    return
+  }
+}
+
+function loseOrder() {
+  post(URL_SAVEREASON, {
+    id: businessInfo.value.id,
+    reason: advanceVal.value
+  }).then((_res) => {
     globalPopup?.showSuccess('操作成功')
-    getDetail()
+    selectChange()
   }).finally(() => {
     allLoading.advanceSaveLoading = false
+    allVisible.advanceVisible = false
+  })
+}
+
+function advancementStageNext(flag: boolean = false) {
+  let fromVal = {}
+  if (!flag) {
+    fromVal = {
+      id: businessInfo.value.id,
+      stageId: stageList.value[currentStage.value + 1].id,
+      stageValue: stageList.value[currentStage.value + 1].name,
+    }
+  } else {
+    const item: any = stageListOption.value.find((item) => item.value === stageStatusVal.value)
+    fromVal = {
+      id: businessInfo.value.id,
+      stageId: item.value,
+      stageValue: item.label,
+    }
+  }
+  post(URL_STAGEIDNEXT, { ...fromVal }).then((_res) => {
+    if (!flag) {
+      globalPopup?.showSuccess('操作成功')
+      selectChange()
+    } else {
+      loseOrder()
+    }
+  }).catch(() => {
+    allLoading.advanceSaveLoading = false
+    allVisible.advanceVisible = false
   })
 }
 
@@ -167,6 +238,7 @@ function getDetail() {
   post(BUSIESS_INFO, { id: optionVal.value }).then(({ data }) => {
     businessInfo.value = (data || []);
     detailCompinentsData.value = data.taskList || []
+    setStageIndex()
   }).finally(() => {
     allLoading.skeletonLoading = false
   })
@@ -192,22 +264,39 @@ function getOptionAll() {
 
 function getSatge() {
   post(BUSIESS_GETSATE, {}).then(({ data }) => {
-    stageList.value = (data || []).sort((a: any, b: any) => { return a.seq - b.seq; }).filter((item: any) => !item.isFinish).map((item: any) => ({ name: item.name, plan: item.plan }))
+    stageList.value = (data || []).sort((a: any, b: any) => { return a.seq - b.seq; }).filter((item: any) => !item.isFinish).map((item: any) => ({ name: item.name, plan: item.plan, id: item.id }))
     stageListOption.value = (data || []).sort((a: any, b: any) => { return a.seq - b.seq; }).filter((item: any) => item.isFinish).map((item: any) => ({ label: item.name, value: item.id, plan: item.plan }))
+    setStageIndex()
   })
 }
 
+function selectChange() {
+  getDetail()
+}
+
+function setStageIndex() {
+  currentStage.value = stageList.value.findIndex((item: any) => item.id == businessInfo.value.stageId)
+}
+
 onMounted(() => {
   const { id } = route.query
   optionVal.value = id
-  getSatge()
-  getOptionAll()
   getDetail()
+  getOptionAll()
+  getSatge()
+  setTimeout(() => {
+    setStageIndex()
+  }, 500)
 })
 </script>
   
 <style lang="scss" scoped>
 .businessDetail {
+  .selected {
+    background-color: #075985 !important;
+    color: #fff !important;
+  }
+
   .icon {
     .el-link {
       color: #0052CC;
@@ -230,6 +319,14 @@ onMounted(() => {
     background-color: #F4F5F7;
     color: #000;
   }
+  .backOrange {
+    background-color: #FF5531;
+    color: #fff;
+  }
+  .backWan {
+    background-color: #075985;
+    color: #fff;
+  }
 
   .startStep {
     clip-path: polygon(0% 0%,

+ 18 - 12
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue

@@ -170,7 +170,7 @@
 import { ref, reactive, onMounted, inject } from "vue";
 import type { ElTable, FormInstance, FormRules, UploadRequestOptions } from 'element-plus'
 import { useRouter, useRoute } from "vue-router";
-import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM, GETBUSINESSLIST, UPDATEINSET, BUSINESSDETELE, BATCHTRANSFER, MODURL, tableColumn, BUSIESS_GETSATE, URL_IMPOERBUSINESS } from './api'
+import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM, GETBUSINESSLIST, UPDATEINSET, BUSINESSDETELE, BATCHTRANSFER, MODURL, tableColumn, BUSIESS_GETSATE, URL_IMPOERBUSINESS, BUSIESS_INFO, URL_EXPORTBUSINESS } from './api'
 import { GETTABLELIST } from '@/pages/product/api'
 import { post, get, uploadFile } from "@/utils/request";
 import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, createTaskFromType, formatDate, confirmAction, downloadTemplate, downloadFile } from '@/utils/tools'
@@ -246,14 +246,17 @@ const productTableListValue = ref([])
 
 function editBusiness(visibles: boolean) {
   businessTemplateRef.value?.getData().then((res: any) => {
-    let productTableListData = relatedProductsRef?.value?.returnData()
+    let productTableListData = relatedProductsRef?.value?.returnData() || []
+    productTableListData.forEach((item: any) => {
+      delete item.id
+    })
     let newForm = {
       ...res,
       expectedTransactionDate: res.expectedTransactionDate ? formatDateTime(new Date(res.expectedTransactionDate)) : '',
       businessItemProductList: productTableListData ? JSON.stringify(productTableListData) : []
     }
     allLoading.businessSaveLading = true
-    post(UPDATEINSET, { ...newForm }).then((_res) => {
+    post(UPDATEINSET, { ...businessTemplateValue.value, ...newForm }).then((_res) => {
       allVisible.newBusinessisible = visibles
       globalPopup?.showSuccess('保存成功')
       getBusinessTableList()
@@ -361,7 +364,7 @@ async function importBusiness(param: UploadRequestOptions) {
 function exportBusinessTableList() {
   allLoading.exoprtLoading = true
   let valueForm = getFromValue(businessOpportunityForm)
-  post('接口名称', { ...valueForm }).then((res) => {
+  post(URL_EXPORTBUSINESS, { ...valueForm }).then((res) => {
     downloadFile(res.data, '商机表导出.xlsx')
   }).finally(() => {
     allLoading.exoprtLoading = false
@@ -378,14 +381,16 @@ function changeBatch(flag: boolean = true) {
 }
 
 function editProduct(row: any) {
-  const list = row.businessItemProductList.map((item: any) => {
-    const { id, productName, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail, num, discount, sealPrice, totalPrice } = item
-    return {
-      id, productId: id, productName, productCode, unit, unitName, typeName, type, price, inventory,
-      num, discount, sealPrice, totalPrice 
-    }
+  post(BUSIESS_INFO, { id: row.id }).then(({ data }) => {
+    const list = (data.businessItemProducts || []).map((item: any) => {
+      const { id, productName, productId, productCode, unit, unitName, typeName, type, price, inventory, orderProductDetail, num, discount, sealPrice, totalPrice, quantity } = item
+      return {
+        id, productId: productId, productName, productCode, unit, unitName, typeName, type, price, inventory,
+        num, discount, sealPrice, totalPrice, quantity
+      }
+    })
+    productTableListValue.value = list
   })
-  productTableListValue.value = list
 }
 
 function showVisible(type: keyof typeof allVisible) { // 显示弹窗
@@ -510,4 +515,5 @@ onMounted(() => {
     font-size: 18px;
     line-height: 24px;
   }
-}</style>
+}
+</style>

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

@@ -18,6 +18,8 @@ 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 URL_IMPORTDATACONTACTS = `${URL}/importData`
+export const URL_EXPORTDATACONTACTS = `${URL}/exportData`
 
 export const actionButtons: any[] = [
     { text: '新建联系人' },

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

@@ -127,7 +127,7 @@
 import { ref, reactive, onMounted, inject } from "vue";
 import { getAllListByCode, getFromValue, resetFromValue, getTemplateKey, confirmAction, downloadTemplate, downloadFile, createTaskFromType } from '@/utils/tools'
 import { post, get, uploadFile } from "@/utils/request";
-import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, URL_PAGECONTACTS, getSex, URL_ADD, URL_UPLOAD, URL_BATCHDETELE, URL_DETELERECYCLE, IMPORTMOD } from "./api";
+import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, URL_PAGECONTACTS, getSex, URL_ADD, URL_UPLOAD, URL_BATCHDETELE, URL_DETELERECYCLE, IMPORTMOD, URL_IMPORTDATACONTACTS, URL_EXPORTDATACONTACTS } from "./api";
 import { useRouter, useRoute } from "vue-router";
 import { GenerateForm } from '@zmjs/form-design';
 import { URL_FETALL } from "../customer/api";
@@ -221,7 +221,7 @@ function newTask(item: any) {
 function exportCustomerTableList() {
   allLoading.exoprtLoading = true
   let valueForm = getFromValue(filterForm)
-  post('接口名称', { ...valueForm }).then((res) => {
+  post(URL_EXPORTDATACONTACTS, { ...valueForm }).then((res) => {
     downloadFile(res.data, allText.exportText)
   }).finally(() => {
     allLoading.exoprtLoading = false
@@ -232,7 +232,7 @@ async function importBusiness(param: UploadRequestOptions) {
   allLoading.importLoading = true
   const formData = new FormData();
   formData.append('multipartFile', param.file)
-  const res = await uploadFile('接口名称', formData).finally(() => {
+  const res = await uploadFile(URL_IMPORTDATACONTACTS, formData).finally(() => {
     allLoading.importLoading = false
   })
   if (res.code == 'ok') {

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

@@ -0,0 +1,85 @@
+import { post, get } from '@/utils/request';
+import { dayjs } from 'element-plus';
+
+export type RequestProps = {
+  startDate?: string;
+  endDate?: string;
+  type?: 0 | 1;
+  userId?: number;
+  departmentId?: number;
+};
+
+export async function getOverallData(payload?: RequestProps): Promise<any> {
+  return await post('report/getCustomerTotalCount', payload);
+}
+
+export async function getConversionData(payload?: RequestProps): Promise<any> {
+  return await post('report/getCustomerTransferRate', payload);
+}
+
+export async function getDepartmentData(): Promise<any> {
+  return await get('/department/normalList');
+}
+
+export async function getStaffData(): Promise<any> {
+  return await get('/user/getSimpleActiveUserList');
+}
+
+export async function exportFile(
+  payload: RequestProps,
+  type: number
+): Promise<any> {
+  return await post(
+    type === 0
+      ? '/report/exportCustomerTotalCount'
+      : '/report/exportCustomerTransferRate',
+    payload
+  );
+}
+
+export const dateCollections = [
+  {
+    name: '当日',
+    start_time: dayjs().startOf('date').toISOString(),
+    end_time: dayjs().endOf('date').toISOString()
+  },
+  {
+    name: '昨日',
+    start_time: dayjs().startOf('date').subtract(1, 'day').toISOString(),
+    end_time: dayjs().endOf('date').subtract(1, 'day').toISOString()
+  },
+  {
+    name: '本周',
+    start_time: dayjs().startOf('week').add(1, 'day').toISOString(),
+    end_time: dayjs().endOf('week').add(1, 'day').toISOString()
+  },
+  {
+    name: '上周',
+    start_time: dayjs()
+      .add(-1, 'week')
+      .startOf('week')
+      .add(1, 'day')
+      .toISOString(),
+    end_time: dayjs().add(-1, 'week').endOf('week').add(1, 'day').toISOString()
+  },
+  {
+    name: '本月',
+    start_time: dayjs().startOf('month').toISOString(),
+    end_time: dayjs().endOf('month').toISOString()
+  },
+  {
+    name: '上月',
+    start_time: dayjs().add(-1, 'month').startOf('month').toISOString(),
+    end_time: dayjs().add(-1, 'month').endOf('month').toISOString()
+  },
+  {
+    name: '本季度',
+    start_time: dayjs().month(0).toISOString(),
+    end_time: dayjs().month(2).endOf('month').toISOString()
+  },
+  {
+    name: '上季度',
+    start_time: dayjs().add(-1, 'year').month(9).toISOString(),
+    end_time: dayjs().add(-1, 'year').month(11).endOf('month').toISOString()
+  }
+];

+ 160 - 55
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/corpreport/index.vue

@@ -3,30 +3,36 @@ import { ref, reactive, onMounted, watchEffect } from 'vue';
 
 import Echarts from '@/components/ReEcharts/index.vue';
 import { EChartsOption } from 'echarts';
+import {
+  type RequestProps,
+  getOverallData,
+  getDepartmentData,
+  getStaffData,
+  dateCollections,
+  getConversionData,
+  exportFile
+} from './api';
+import { downloadFile } from '@/utils/tools';
 
-const chartOptions: EChartsOption = {
+const isLoading = ref(false);
+const dataSource = ref([]);
+const form = reactive({ type: undefined, date: undefined, member: undefined });
+const memberOptions = ref([]);
+// 0 客户总量  1 客户转化率
+const dataType = ref<0 | 1>(0);
+
+const chartOptions: EChartsOption = reactive({
   grid: { bottom: 30 },
   legend: {},
   tooltip: {},
   dataset: {
-    dimensions: ['product', '2015', '2016', '2017'],
-    source: [
-      { product: 'Matcha Latte', 2015: 43.3, 2016: 85.8, 2017: 93.7 },
-      { product: 'Milk Tea', 2015: 83.1, 2016: 73.4, 2017: 55.1 },
-      { product: 'Cheese Cocoa', 2015: 86.4, 2016: 65.2, 2017: 82.5 },
-      { product: 'Walnut Brownie', 2015: 72.4, 2016: 53.9, 2017: 39.1 }
-    ]
+    dimensions: [],
+    source: []
   },
   xAxis: { type: 'category' },
   yAxis: {},
-  // Declare several bar series, each will be mapped
-  // to a column of dataset.source by default.
-  series: [
-    { type: 'bar', barWidth: 20 },
-    { type: 'bar', barWidth: 20 },
-    { type: 'bar', barWidth: 20 }
-  ]
-};
+  series: []
+});
 
 const dateOptions = [
   { label: '今天', value: '1' },
@@ -38,36 +44,97 @@ const dateOptions = [
   { label: '本季度', value: '7' },
   { label: '上季度', value: '8' }
 ];
-const dataSource = [
-  {
-    date: '2016-05-03',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles'
-  },
-  {
-    date: '2016-05-02',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles'
-  },
-  {
-    date: '2016-05-04',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles'
-  },
-  {
-    date: '2016-05-01',
-    name: 'Tom',
-    address: 'No. 189, Grove St, Los Angeles'
+
+const onSubmit = async (isExport?: boolean) => {
+  const payload = {
+    startDate: form.date && dateCollections[form.date - 1].start_time,
+    endDate: form.date && dateCollections[form.date - 1].end_time,
+    type: form.type,
+    userId: form.type === 1 ? form.member : undefined,
+    departmentId: form.type === 1 ? undefined : form.member
+  } as RequestProps;
+
+  if (isExport === true) {
+    const { data } = await exportFile(payload, dataType.value);
+    console.log(data, '----------------');
+    await downloadFile(data, data?.split('/')?.[2] || '客户表');
+    return;
   }
-];
+
+  dataType.value === 0 ? queryOverall(payload) : queryConversion(payload);
+};
+
+const queryOverall = async (payload?: RequestProps) => {
+  isLoading.value = true;
+  const { data = [] } = await getOverallData(payload);
+  isLoading.value = false;
+
+  dataSource.value = data.map((d) => ({
+    name: d.name,
+    dealRate: d.dealRate * 100,
+    customerDeal: d.customerDeal,
+    customertotal: d.customertotal
+  }));
+
+  // @ts-ignore
+  chartOptions.series = [
+    { type: 'bar', barWidth: 20 },
+    { type: 'bar', barWidth: 20, color: '#b91c1c' }
+  ];
+  // @ts-ignore
+  chartOptions.dataset.dimensions = ['name', '成交客户数', '新增客户数'];
+  // @ts-ignore
+  chartOptions.dataset.source = data.map((d) => ({
+    name: d.name,
+    ['成交客户数']: d.customerDeal,
+    ['新增客户数']: d.customertotal
+  }));
+  chartOptions.legend = {};
+};
+
+const queryConversion = async (payload?: RequestProps) => {
+  isLoading.value = true;
+  const { data = [] } = await getConversionData(payload);
+  isLoading.value = false;
+
+  dataSource.value = data.map((d) => ({
+    name: d.name,
+    dealRate: d.dealRate * 100
+  }));
+
+  // @ts-ignore
+  chartOptions.series = [{ type: 'bar', barWidth: 20 }];
+  // @ts-ignore
+  chartOptions.dataset.dimensions = ['name', '客户转化率(%)'];
+  // @ts-ignore
+  chartOptions.dataset.source = data.map((d) => ({
+    name: d.name,
+    ['客户转化率(%)']: d.dealRate * 100
+  }));
+  chartOptions.legend = undefined;
+};
+
+watchEffect(() => {
+  dataType.value === 0 ? queryOverall() : queryConversion();
+});
+
+watchEffect(async () => {
+  const { data = [] } =
+    form.type === 1 ? await getStaffData() : await getDepartmentData();
+
+  memberOptions.value = data.map((d) => ({
+    name: form.type === 1 ? d.name : d.departmentName,
+    id: form.type === 1 ? d.id : d.departmentId
+  }));
+});
 </script>
 
 <template>
   <div class="m-5 bg-white h-full p-4 rounded">
     <div class="flex justify-between">
-      <el-form class="flex gap-4">
+      <el-form :model="form" class="flex gap-4">
         <el-form-item class="w-28">
-          <el-select>
+          <el-select clearable v-model="form.date">
             <el-option
               v-for="date in dateOptions"
               :key="date.value"
@@ -77,33 +144,71 @@ const dataSource = [
           </el-select>
         </el-form-item>
         <el-form-item class="w-28">
-          <el-select>
-            <el-option label="按部门" value="1" />
-            <el-option label="按员工" value="2" />
+          <el-select
+            clearable
+            v-model="form.type"
+            @change="form.member = undefined"
+          >
+            <el-option label="按部门" :value="0" />
+            <el-option label="按员工" :value="1" />
           </el-select>
         </el-form-item>
         <el-form-item class="w-52">
-          <el-select></el-select>
+          <el-select
+            clearable
+            v-model="form.member"
+            placeholder="选择部门(默认为本部门及下属部门)"
+          >
+            <el-option
+              v-for="date in memberOptions"
+              :key="date.id"
+              :label="date.name"
+              :value="date.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button
+            type="primary"
+            :loading="isLoading"
+            @click="onSubmit(false)"
+            >搜索</el-button
+          >
+          <el-button type="primary" @click="onSubmit(true)">导出</el-button>
         </el-form-item>
-        <el-button type="primary">搜索</el-button>
       </el-form>
-      <el-tabs type="card">
-        <el-tab-pane label="客户总量分析" name="first" />
-        <el-tab-pane label="客户转化率分析" name="first" />
-        <el-tab-pane label="合同数量分析" name="first" />
+
+      <el-tabs
+        type="card"
+        v-model="dataType"
+        @tab-change="
+          () => {
+            form.date = undefined;
+            form.member = undefined;
+            form.type = undefined;
+            dataSource = [];
+          }
+        "
+      >
+        <el-tab-pane label="客户总量分析" :name="0" />
+        <el-tab-pane label="客户转化率分析" :name="1" />
       </el-tabs>
-      <!-- <span class="mr-12 font-bold text-gray-900">客户总量分析</span> -->
     </div>
     <div class="h-96">
       <Echarts :option="chartOptions"></Echarts>
     </div>
     <el-table :data="dataSource">
-      <el-table-column prop="date" label="员工姓名" />
-      <el-table-column prop="date" label="新增客户数" />
-      <el-table-column prop="date" label="成交客户数" />
-      <el-table-column prop="date" label="客户成交率(%)" />
-      <el-table-column prop="date" label="合同总金额" />
-      <el-table-column prop="date" label="回款金额" />
+      <el-table-column prop="name" label="员工姓名" />
+      <el-table-column
+        prop="dealRate"
+        label="客户转化率(%)"
+        v-if="dataType === 1"
+      />
+      <template v-else>
+        <el-table-column prop="customertotal" label="新增客户数" />
+        <el-table-column prop="customerDeal" label="成交客户数" />
+        <el-table-column prop="dealRate" label="客户成交率(%)" />
+      </template>
     </el-table>
   </div>
 </template>

+ 6 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/api.ts

@@ -13,4 +13,9 @@ export const URL_ROWBACK = `${PREFIX}/rollback`
 export const URL_THOROUGHLYDETELE = `${PREFIX}/reallyDelete`
 export const URL_CLAIM = `${PREFIX}/claim`
 export const URL_FETALL = `${PREFIX}/getAll`
-export const URL_GETDETAIL = `${PREFIX}/getInfo`
+export const URL_GETDETAIL = `${PREFIX}/getInfo`
+export const URL_IMPORTEXELS = `${PREFIX}/importData`
+export const URL_EXPORTEXELS = `${PREFIX}/exportData`
+export const URL_UPLOADFILE = `${PREFIX}/uploadFile`
+export const URL_DETELEFILE = `${PREFIX}/deleteFile`
+export const URL_REFFILENAME = `${PREFIX}/reFileName`

+ 104 - 45
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/attachment.vue

@@ -3,65 +3,124 @@
         <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="正在上传">
+                    <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="name" label="附件名称" width="180" />
+                <el-table-column prop="size" label="附件大小" width="120" />
+                <el-table-column prop="userName" label="上传人" width="120" />
+                <el-table-column prop="createTime" 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="showVisible(scope.row)">重命名</el-button>
+                        <el-button link type="danger" size="large" @click="deteleFile(scope.row)">删除</el-button>
                     </template>
                 </el-table-column>
             </el-table>
         </div>
+
+        <!-- 弹窗 -->
+        <el-dialog v-model="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()">保存</el-button>
+                        <el-button @click="renameDialogVisible = false">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="pt-3">
+                <el-input v-model.trim="renameVal" style="width: 100%" class="pb-3" clearable />
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { post, uploadFile } from '@/utils/request';
+import { UploadRequestOptions } from 'element-plus';
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { URL_DETELEFILE, URL_REFFILENAME, URL_UPLOADFILE } from '../api';
+import { confirmAction, downloadFile } from '@/utils/tools';
+
+const emits = defineEmits(['refreshData']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const props = defineProps<{
+    data: any
+}>()
+const attachmenttable = ref([])
+const information = ref<any>({})
+const uploadRef = <any>ref()
+const renameDialogVisible = ref(false)
+const renameVal = ref('')
+
+// 下载文件
+function fileDownload(item: any) {
+    downloadFile(`${item.path}`, item.name)
+}
+
+// 删除文件
+function deteleFile(item: any) {
+    const id = item.id
+    confirmAction(`确定删除【${item.name}】文件吗?`).then(() => {
+        post(URL_DETELEFILE, { id }).then((_res) => {
+            globalPopup?.showSuccess('删除成功')
+            emits('refreshData')
+        })
+    })
+}
+
+// 保存重命名
+function saveEditClue() {
+    if (!renameVal.value) {
+        globalPopup?.showWarning('请输入文件名称')
+        return
+    }
+    const id = information.value.id
+    post(URL_REFFILENAME, { name: renameVal.value, id: id }).then((res) => {
+        renameDialogVisible.value = false
+        globalPopup?.showSuccess(res.msg || '')
+        emits('refreshData')
+    }).catch((_err) => { })
+}
+
+// 上传文件
+async function httpUploadFile(param: UploadRequestOptions) {
+    const id = information.value.id
+    const formData = new FormData();
+    formData.append('file', param.file)
+    formData.append('id', id)
+    const res = await uploadFile(URL_UPLOADFILE, formData).finally(() => {
+        uploadRef.value.clearFiles()
+    })
+    if (res.code == 'ok') {
+        globalPopup?.showSuccess(res.msg || '')
+        emits('refreshData');
+        return
+    }
+    globalPopup?.showError(res.msg || '')
+    return res
+}
+
+// 显示弹窗
+function showVisible(item: any) {
+    renameVal.value = JSON.parse(JSON.stringify(item.name))
+    renameDialogVisible.value = true
+}
+
+watchEffect(() => {
+    information.value = props.data
+    attachmenttable.value = (props.data.files || [])
+});
 
-const attachmenttable = ref([{
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}, {
-    fileName: '文件附件',
-    fileSize: '3.1MB',
-    uploader: '张三',
-    uploadTime: '2024-04-01',
-}])
 // 生命周期钩子
 onMounted(() => {
 });

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


+ 17 - 33
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/operationRecord.vue

@@ -5,45 +5,29 @@
         </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="creaTime" label="操作时间" width="140" />
+                <el-table-column prop="username" label="操作人" width="120" />
+                <el-table-column prop="name" 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 operationRecordtable = ref([])
+const information = ref({})
+
+const props = defineProps<{
+    data: any
+}>()
+
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    operationRecordtable.value = data.actionLogs || []
+})
 
-const operationRecordtable = ref([{
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-}, {
-    operationContent: '转移线索',
-    operator: '张三',
-    operatingTime: '2024-04-01',
-},])
 // 生命周期钩子
 onMounted(() => {
 });

+ 142 - 45
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedBusiness.vue

@@ -3,7 +3,7 @@
         <div class="flex justify-between">
             <div class="title">相关商机</div>
             <div>
-                <el-button type="primary">新建商机</el-button>
+                <el-button type="primary" @click="editNewBusiness()">新建商机</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">
@@ -25,56 +25,153 @@
                 <el-table-column prop="endTime" 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(false)" :loading="allLoading.businessSaveLading"
+                            :disabled="allLoading.newBusinessSaveLading">保存</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"
+                    :productTableListValue="productTableListValue" />
+            </div>
+        </el-dialog>
     </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { get, post } from '@/utils/request';
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { GenerateForm } from '@zmjs/form-design';
+import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import { GETTABLELIST } from '@/pages/product/api';
+import { GETGENERATEFOEM, UPDATEINSET } from '@/pages/business/api';
+import { setTemplateDataDisable } from '@/utils/tools';
+import { formatDateTime } from '@/utils/times';
+
+const emits = defineEmits(['refreshData']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const props = defineProps<{
+    data: any
+}>()
+
+const information = ref<any>({})
+const relatedTaskstable = ref([])
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const businessTemplateRef = ref<typeof GenerateForm>() // 自定义表单dom
+const businessTemplateValue = ref({})
+const businessTemplateKey = ref(1)
+const productTableList = ref([])
+const productTableListValue = ref([])
+const businessTemplate = ref({
+    config: {},
+    list: []
+}) // 自定义表单数据
+const allVisible = reactive({
+    newBusinessisible: false
+})
+const allLoading = reactive({
+    generateFormLading: false,
+    newBusinessSaveLading: false,
+    businessSaveLading: false
+})
+
+function editBusiness(visibles: boolean) {
+    businessTemplateRef.value?.getData().then((res: any) => {
+        let productTableListData = relatedProductsRef?.value?.returnData() || []
+        productTableListData.forEach((item: any) => {
+            delete item.id
+        })
+        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 = visibles
+            globalPopup?.showSuccess('保存成功')
+            emits('refreshData')
+        }).finally(() => {
+            allLoading.businessSaveLading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function editNewBusiness() {
+    showVisible('newBusinessisible')
+    allLoading.generateFormLading = true
+    businessTemplateValue.value = { customerId: information.value.id }
+    productTableListValue.value = []
+    setTimeout(() => {
+        businessTemplateRef.value && businessTemplateRef.value.reset()
+        businessTemplateKey.value++
+        allLoading.generateFormLading = false
+    }, 500)
+}
+
+async function getSystemField() {
+    const datas = await get(GETGENERATEFOEM)
+    let newConfig = JSON.parse(datas.data[0].config)
+    newConfig.list = setTemplateDataDisable(newConfig.list, ['customerId'])
+    businessTemplate.value = newConfig
+}
+
+function showVisible(type: keyof typeof allVisible) {
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
+
+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: ''
+                }
+            })
+        }
+    })
+}
+
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    relatedTaskstable.value = []
+})
 
-const relatedTaskstable = ref([{
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}])
 // 生命周期钩子
 onMounted(() => {
+    getProductTableList()
+    getSystemField()
 });
 </script>
 <style scoped lang="scss">

+ 104 - 46
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedContacts.vue

@@ -3,11 +3,11 @@
         <div class="flex justify-between">
             <div class="title">相关联系人</div>
             <div>
-                <el-button type="primary">新建联系人</el-button>
+                <el-button type="primary" @click="editContacts(information)">新建联系人</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">
-            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+            <el-table :data="relatedCustomertable" border style="width: 100%;height: 300px;">
                 <el-table-column label="序号" width="80">
                     <template #default="scope">
                         {{ scope.$index + 1 }}
@@ -29,56 +29,114 @@
                 <el-table-column prop="endTime" label="创建时间" width="130" />
             </el-table>
         </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" :loading="allLoading.editContactsSaveLoading"
+                            @click="editContactsSave(false)">新建</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>
     </div>
 </template>
 <script lang="ts" setup>
-import { ref, reactive, onMounted, onUnmounted, defineExpose, inject } from 'vue'
+import { GETGENERATEFOEM, URL_ADD } from '@/pages/contacts/api';
+import { get, post } from '@/utils/request';
+import { setTemplateDataDisable } from '@/utils/tools';
+import { GenerateForm } from '@zmjs/form-design';
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+
+const emits = defineEmits(['refreshData']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const props = defineProps<{
+    data: any
+}>()
+
+const information = ref({})
+const relatedCustomertable = ref([])
+const contactsTemplateValue = ref({})
+const contactsTemplateRefKey = ref(1)
+const contactsTemplateRef = ref<typeof GenerateForm>()
+const contactsTemplate = ref({
+    list: [],
+    config: {}
+})
+
+function editContactsSave(flag: boolean) {
+    contactsTemplateRef.value?.getData().then((res: any) => {
+        allLoading.editContactsSaveLoading = true
+        post(URL_ADD, { ...contactsTemplateValue.value, ...res }).then((_res) => {
+            allVisible.editContactsVisible = flag
+            globalPopup?.showSuccess('操作成功')
+            if (flag) {
+                contactsTemplateRef.value?.reset()
+            }
+            emits('refreshData')
+        }).finally(() => {
+            allLoading.editContactsSaveLoading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function editContacts(data: any) {
+    showVisible('editContactsVisible')
+    const { id, telPhone } = data
+    contactsTemplateValue.value = { customId: id, phone: telPhone }
+    allLoading.contactsTemplateRefLoading = true
+    setTimeout(() => {
+        contactsTemplateRefKey.value++
+        allLoading.contactsTemplateRefLoading = false
+    }, 1000);
+}
+
+const allLoading = reactive({
+    contactsTemplateRefLoading: false,
+    editContactsSaveLoading: false
+})
+
+const allVisible = reactive({
+    editContactsVisible: false,
+})
+
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    relatedCustomertable.value = []
+})
+
+async function getSystemField() {
+    const datas = await get(GETGENERATEFOEM)
+    let newConfig = JSON.parse(datas.data[0].config)
+    newConfig.list = setTemplateDataDisable(newConfig.list, ['customId'])
+    contactsTemplate.value = newConfig
+}
+
+function showVisible(type: keyof typeof allVisible) {
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
 
-const relatedTaskstable = ref([{
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}])
 // 生命周期钩子
 onMounted(() => {
+    getSystemField()
 });
 </script>
 <style scoped lang="scss">

+ 152 - 46
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/component/relatedOrders.vue

@@ -3,11 +3,11 @@
         <div class="flex justify-between">
             <div class="title">相关销售订单</div>
             <div>
-                <el-button type="primary">新建销售订单</el-button>
+                <el-button type="primary" @click="editOrder()">新建销售订单</el-button>
             </div>
         </div>
         <div class="flex-1 overflow-auto pt-3">
-            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+            <el-table :data="relatedOrders" border style="width: 100%;height: 300px;">
                 <el-table-column prop="priority" label="订单编号" width="130" />
                 <el-table-column prop="taskName" label="订单名称">
                     <template #default="scope">
@@ -27,56 +27,162 @@
                 <el-table-column prop="endTime" label="创建时间" width="130" />
             </el-table>
         </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">{{ '新建销售订单' }}</h4>
+                    <div>
+                        <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" :key="orderTemplateKey"
+                    :value="orderTemplateValue" />
+                <div>相关产品</div>
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList"
+                    :productTableListValue="productTableListValue" />
+            </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 { setTemplateDataDisable } from '@/utils/tools';
+import { GenerateForm } from '@zmjs/form-design';
+import { get, post } from '@/utils/request';
+
+import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import { GETGENERATEFOEM, GETTABLELIST, URL_OEDERUPDATE } from '@/pages/order/api';
+import { formatDate } from '@/utils/times';
+
+const emits = defineEmits(['refreshData']);
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const props = defineProps<{
+    data: any
+}>()
+
+const information = ref<any>({})
+const relatedOrders = ref([])
+const productTableList = ref([])
+const productTableListValue = ref([])
+const orderTemplateValue = ref({})
+const orderTemplateKey = ref(1)
+const orderTemplateRef = ref<typeof GenerateForm>()
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const orderTemplate = ref({
+    list: [],
+    config: {}
+})
+const allVisible = reactive({
+    editOrderVisible: false,
+})
+const allLoading = reactive({
+    editSaveLading: false,
+    orderTemplateLoadinng: false,
+})
+
+
+
+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()
+            }
+            emits('refreshData')
+        }).finally(() => {
+            allLoading.editSaveLading = false
+        })
+    }).catch((_err: any) => {
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function editOrder() {
+    showVisible('editOrderVisible')
+    allLoading.orderTemplateLoadinng = true
+    
+    orderTemplateValue.value = { customId: information.value.id }
+    productTableListValue.value = []
+    setTimeout(() => {
+        orderTemplateRef.value && orderTemplateRef.value.reset()
+        orderTemplateKey.value++
+        allLoading.orderTemplateLoadinng = false
+    }, 1000)
+}
+
+async function getSystemField() {
+    const datas = await get(GETGENERATEFOEM)
+    let newConfig = JSON.parse(datas.data[0].config)
+    newConfig.list = setTemplateDataDisable(newConfig.list, ['customId'])
+    orderTemplate.value = newConfig
+}
+
+function showVisible(type: keyof typeof allVisible) {
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
+
+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: ''
+                }
+            })
+        }
+    })
+}
+
+watchEffect(() => {
+    const { data } = props
+    information.value = data
+    relatedOrders.value = []
+})
 
-const relatedTaskstable = ref([{
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}, {
-    taskName: '任务名称20240316-tempalsbls',
-    priority: '中',
-    status: '进行中',
-    executor: '张三',
-    startTime: '2024-04-01',
-    endTime: '2024-04-01',
-}])
 // 生命周期钩子
 onMounted(() => {
+    getProductTableList()
+    getSystemField()
 });
 </script>
 <style scoped lang="scss">

+ 21 - 14
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/customer/detail/index.vue

@@ -13,40 +13,43 @@
       </div>
     </div>
     <!-- 内容 -->
-    <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar" v-loading="allLoading.customerDetailLoading">
+    <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar"
+      v-loading="allLoading.customerDetailLoading">
       <div class="w-full h-auto flex justify-between">
         <div class="bg-white shadow-md rounded-md" style="width: 46%;">
-          <Information />
+          <Information :data="information" @refreshData="getDetail" />
         </div>
         <div class="bg-white ml-2 shadow-md rounded-md flex-1">
-          <Attachment />
+          <Attachment :data="information" @refreshData="getDetail" />
         </div>
       </div>
 
       <div class="w-full h-auto flex justify-between mt-2">
         <div class="bg-white shadow-md rounded-md" style="width: 65%;">
-          <RelatedTasks />
+          <DetailCompinents :data="detailCompinentsData" :information="information" :formTaskType="0"
+            :filed="'customId'" :disabledList="['taskType', 'customId']"
+            @refreshData="getDetail" />
         </div>
         <div class="bg-white ml-2 shadow-md rounded-md flex-1">
-          <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">
-          <RelatedContacts />
+          <RelatedContacts :data="information" @refreshData="getDetail" />
         </div>
       </div>
 
       <div class="w-full h-auto flex justify-between mt-2">
         <div class="bg-white shadow-md rounded-md w-full">
-          <RelatedBusiness />
+          <RelatedBusiness :data="information" @refreshData="getDetail" />
         </div>
       </div>
 
       <div class="w-full h-auto flex justify-between mt-2">
         <div class="bg-white shadow-md rounded-md w-full">
-          <RelatedOrders />
+          <RelatedOrders :data="information" @refreshData="getDetail" />
         </div>
       </div>
     </div>
@@ -69,7 +72,8 @@ import OperationRecord from '../component/operationRecord.vue';
 import RelatedBusiness from '../component/relatedBusiness.vue';
 import RelatedContacts from "../component/relatedContacts.vue";
 import RelatedOrders from "../component/relatedOrders.vue"
-import { number } from "echarts";
+import DetailCompinents from '@/components/detailcompinents/relatedTasks.vue'
+import { formatDate } from "@/utils/times";
 
 const route = useRoute()
 const globalPopup = inject<GlobalPopup>('globalPopup')
@@ -77,6 +81,7 @@ const rowId = ref(+(route.query.id || ''))
 const values = ref<any>('')
 const options = ref<optionType[]>([])
 const information = ref<any>({})
+const detailCompinentsData = ref([])
 const allLoading = reactive({
   customerDetailLoading: false
 })
@@ -84,7 +89,9 @@ const allLoading = reactive({
 function getDetail() {
   allLoading.customerDetailLoading = true
   post(URL_GETDETAIL, { id: values.value }).then((res) => {
-    information.value = res.data
+    res.data.newCreateTime = formatDate(new Date(res.data.createTime)),
+      information.value = res.data
+      detailCompinentsData.value = res.data.taskList || []
   }).finally(() => {
     allLoading.customerDetailLoading = false
   })
@@ -92,10 +99,10 @@ function getDetail() {
 
 function getAllCustomer() {
   get(URL_FETALL).then(({ data }) => {
-      options.value = data.map((item: any) => ({
-          value: item.id,
-          label: item.customName
-      }))
+    options.value = data.map((item: any) => ({
+      value: item.id,
+      label: item.customName
+    }))
   })
 }
 

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

@@ -174,7 +174,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, inject } from "vue";
-import { MOD, GETSYSFILED, GETPERSONNEL, URL_TABLELIST, URL_TEMPLALE, URL_EDITSAVE, URL_DETELER, URL_CLAIM, IMPORTMOD, GETALLCLUE } from './api.ts'
+import { MOD, GETSYSFILED, GETPERSONNEL, URL_TABLELIST, URL_TEMPLALE, URL_EDITSAVE, URL_DETELER, URL_CLAIM, IMPORTMOD, GETALLCLUE, URL_IMPORTEXELS, URL_EXPORTEXELS } from './api.ts'
 import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate, getTemplateKey, createTaskFromType, confirmAction, downloadFile, downloadTemplate } from '@/utils/tools'
 import { post, get, uploadFile } from "@/utils/request";
 import { useRouter, useRoute } from "vue-router";
@@ -280,7 +280,7 @@ async function importBusiness(param: UploadRequestOptions) {
   allLoading.importLoading = true
   const formData = new FormData();
   formData.append('multipartFile', param.file)
-  const res = await uploadFile('接口名称', formData).finally(() => {
+  const res = await uploadFile(URL_IMPORTEXELS, formData).finally(() => {
     allLoading.importLoading = false
   })
   if (res.code == 'ok') {
@@ -294,7 +294,7 @@ async function importBusiness(param: UploadRequestOptions) {
 function exportCustomerTableList() {
   allLoading.exoprtLoading = true
   let valueForm = getFromValue(customerCriteriaForm)
-  post('接口名称', { ...valueForm }).then((res) => {
+  post(URL_EXPORTEXELS, { ...valueForm }).then((res) => {
     downloadFile(res.data, allText.exportText)
   }).finally(() => {
     allLoading.exoprtLoading = false

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

@@ -14,6 +14,13 @@ export const EXPORTTIME = `${MOD}/exportData`
 export const IMPORITEM = `${MOD}/importData`
 export const URL_BATCHDELETE = `${MOD}/batchDeleteOrder`
 export const URL_RECOVER = `${MOD}/recover`
+export const URL_DETEALORDER = `${MOD}/getDetail`
+export const URL_TRANSFER = `${MOD}/transfer`
+export const URL_FERDETAILTASK = `${MOD}/taskWithOrder`
+export const URL_PAYLIST = `${MOD}/paymentCollectionList`
+export const URL_ADDREBATE = `${MOD}/paymentCollection`
+export const URL_EDITEBATE = `${MOD}/editPayment`
+export const URL_DETELEITEMS = `${MOD}/deletePayment`
 
 export const tableColumns: TableColumn[] = [
     { prop: 'orderCode', label: '订单编号', event: 'toDetali', width: '150' },
@@ -33,4 +40,10 @@ export const tableColumns: TableColumn[] = [
     { prop: 'inchargerName', label: '负责人', width: '200' },
     { prop: 'creatorName', label: '创建人', width: '200' },
     { prop: 'createTime', label: '创建时间', width: '200' },
+]
+
+export const paymentStatus = [
+    { value: 0, label: '未回款' },
+    { value: 1, label: '已回款' },
+    { value: 2, label: '完全回款' },
 ]

+ 161 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/attachment.vue

@@ -0,0 +1,161 @@
+<template>
+    <div class="attachment pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">附件</div>
+            <div>
+                <el-upload ref="uploadRef" :http-request="httpUploadFile" :limit="1" :show-file-list="false"
+                    element-loading-text="正在上传">
+                    <template #trigger>
+                        <el-button type="primary" :loading="allLoading.uploadLoading">上传</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="attachmentName" 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" @click="fileDownload(scope.row)">下载</el-button>
+                        <el-button link type="primary" size="large" @click="showVisible(scope.row)">重命名</el-button>
+                        <el-button link type="danger" size="large" @click="deteleFile(scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+
+        <!-- 弹窗 -->
+        <el-dialog v-model="allVisible.renameVisible" 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" :loading="allLoading.renameLoading" :disabled="!renameVal"
+                            @click="saveEditClue()">保存</el-button>
+                        <el-button @click="allVisible.renameVisible = false">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="pt-3">
+                <el-input v-model.trim="renameVal" style="width: 100%" class="pb-3" clearable />
+            </div>
+        </el-dialog>
+    </div>
+</template>
+<script lang="ts" setup>
+import { get, post, uploadFile } from '@/utils/request';
+import { UploadRequestOptions } from 'element-plus';
+import { ref, reactive, onMounted, onUnmounted, defineEmits, inject, watchEffect } from 'vue'
+import { confirmAction, downloadFile } from '@/utils/tools';
+import { FILEDETELE, FILERENAME, UPLOADATTACHMENT } from '@/pages/product/api';
+import { IMPORTMOD } from '../api';
+
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+
+const uploadRef = ref<any>()
+const attachmenttable = ref([])
+const information: any = ref({})
+const renameItem: any = ref({})
+const renameVal = ref('')
+const allLoading = reactive({
+    uploadLoading: false,
+    renameLoading: false,
+})
+const allVisible = reactive({
+    renameVisible: false
+})
+
+const props = defineProps<{
+    data: any,
+    information: any
+}>()
+
+// 下载文件
+function fileDownload(item: any) {
+    downloadFile(item.url, `${item.attachmentName}${item.attachmentSuffix}`)
+}
+
+// 保存重命名
+function saveEditClue() {
+    const id = renameItem.value.id
+    allLoading.renameLoading = true
+    post(FILERENAME, { name: renameVal.value, id }).then((res) => {
+        if (res.code == 'ok') {
+            allVisible.renameVisible = false
+            globalPopup?.showSuccess(res.msg || '')
+            emits('refreshData', 'getFileList')
+        }
+    }).catch((err) => {
+        globalPopup?.showError(err.msg || '')
+    }).finally(() => {
+        allLoading.renameLoading = false
+    })
+}
+
+// 删除文件
+function deteleFile(item: any) {
+    const id = item.id
+    confirmAction(`确定删除【${item.attachmentName}】文件吗?`).then(() => {
+        post(FILEDETELE, { id }).then((_res) => {
+            globalPopup?.showSuccess('删除成功')
+            emits('refreshData', 'getFileList')
+        }).catch((err) => {
+            globalPopup?.showError(err.msg || '')
+        })
+    })
+}
+
+// 显示弹窗
+function showVisible(item: any) {
+    renameItem.value = JSON.parse(JSON.stringify(item))
+    renameVal.value = renameItem.value.attachmentName
+    allVisible.renameVisible = true
+}
+
+// 上传附件
+async function httpUploadFile(param: UploadRequestOptions) {
+    allLoading.uploadLoading = true
+    const id = information.value.id
+    const formData = new FormData();
+    formData.append('file', param.file)
+    formData.append('moduleId', id)
+    formData.append('moduleCode', IMPORTMOD)
+    const res = await uploadFile(UPLOADATTACHMENT, formData)
+    allLoading.uploadLoading = false
+    uploadRef.value.clearFiles()
+    if (res.code == 'ok') {
+        globalPopup?.showSuccess('上传成功')
+        emits('refreshData', 'getFileList')
+        return
+    }
+    globalPopup?.showError(res.msg || '')
+    return res
+}
+
+// 接收参数赋值
+function receiveAssignment(item: any) {
+    attachmenttable.value = item.data
+    information.value = item.information
+}
+
+watchEffect(() => {
+    receiveAssignment(props)
+});
+
+// 生命周期钩子
+onMounted(() => {
+    receiveAssignment(props)
+});
+</script>
+<style scoped lang="scss">
+.attachment {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 254 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/information.vue

@@ -0,0 +1,254 @@
+<template>
+    <div class="information pl-4 pr-4 pt-3 pb-3">
+        <div class="flex justify-between">
+            <div class="title">基本信息</div>
+            <div>
+                <el-button type="primary" @click="transferCli()">转移</el-button>
+                <el-button type="primary" @click="editInfo(info)">编辑</el-button>
+            </div>
+        </div>
+        <div class="form flex flex-wrap justify-between">
+            <div v-for="item in formItems" :key="item.label" class="formItem flex" :style="{ width: item.width }">
+                <div :class="item.labelClass">{{ item.label }}:</div>
+                <div class="flex-1 overflow-hidden text-ellipsis whitespace-nowrap ml-1">{{ item.value }}</div>
+            </div>
+        </div>
+
+        <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="transferChange()">转移</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.trim="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>
+
+        <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">编辑销售订单</h4>
+                    <div>
+                        <el-button type="primary" @click="editOrderSave()"
+                            :loading="allLoading.editOrderSaveLoading">保存</el-button>
+                        <el-button @click="closeVisible('editOrderVisible')">取消</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
+                <div class="ml-4 mr-4" v-loading="allLoading.orderTemplateLoading">
+                    <GenerateForm ref="orderTemplateRef" :data="orderTemplate" :value="orderTemplateValue"
+                        :key="orderTemplateKey" />
+                </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 { get, post } from '@/utils/request';
+import { GETGENERATEFOEM, GETPERSONNEL, URL_OEDERUPDATE, URL_PRODUTWITHORDER, URL_TRANSFER } from '../api';
+import { formatDate } from '@/utils/times';
+
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+const props = defineProps<{
+    data: any
+}>()
+const allLoading = reactive({
+    orderTemplateLoading: false,
+    editOrderSaveLoading: false,
+    transferLoading: false
+})
+const allVisible = reactive({
+    editOrderVisible: false,
+    transferVisible: false
+})
+const allText = reactive({
+    operationText: '认领联系人'
+})
+const orderTemplate = ref({
+    list: [],
+    config: {}
+})
+const orderTemplateValue = ref({})
+const orderTemplateKey = ref(1)
+const orderTemplateRef = ref<typeof GenerateForm>()
+const info: any = ref({})
+const transferValue = ref('')
+const transferOptions = ref<optionType[]>([])
+const productTableListValue = ref([])
+
+function editOrderSave() {
+    orderTemplateRef.value?.getData().then((res: any) => {
+        allLoading.editOrderSaveLoading = true
+        post(URL_OEDERUPDATE, {
+            ...orderTemplateValue.value,
+            ...res,
+            orderEndDate: res.orderEndDate ? formatDate(res.orderEndDate) : '',
+            orderStartDate: res.orderStartDate ? formatDate(res.orderStartDate) : '',
+            orderProductDetailString: JSON.stringify(productTableListValue.value || [])
+        }).then((_res) => {
+            closeVisible('editOrderVisible')
+            globalPopup?.showSuccess('操作成功')
+            emits('refreshData', 'getDetail')
+        }).finally(() => {
+            allLoading.editOrderSaveLoading = false
+        })
+    }).catch((_err: any) => {
+        console.log(_err)
+        globalPopup?.showError('请填写完整')
+    })
+}
+
+function editInfo(item: any) {
+    showVisible('editOrderVisible')
+    allLoading.orderTemplateLoading = true
+    if (item) {
+        const templateKey = getTemplateKey(orderTemplate.value.list)
+        let formVal: templateKey = { id: item.id }
+        for (let i = 0; i < templateKey.length; i++) {
+            const key = templateKey[i]
+            formVal[key] = item[key]
+        }
+        console.log(formVal, templateKey, item)
+        orderTemplateValue.value = formVal
+        editProduct(item)
+    }
+
+    setTimeout(() => {
+        orderTemplateKey.value++
+        allLoading.orderTemplateLoading = false
+    }, 500)
+}
+
+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,
+                num: +orderProductDetail?.num,
+                discount: +orderProductDetail?.discount,
+                sealPrice: +orderProductDetail?.sealPrice,
+                totalPrice: +orderProductDetail?.totalPrice
+            }
+        })
+        productTableListValue.value = list
+    })
+}
+
+function transferCli() {
+    transferValue.value = ''
+    showVisible('transferVisible')
+}
+
+function transferChange() {
+    allLoading.transferLoading = true
+    post(URL_TRANSFER, { id: info.value.id, userId: transferValue.value }).then(() => {
+        globalPopup?.showSuccess('操作成功')
+        closeVisible('transferVisible')
+        emits('refreshData', 'getDetail')
+    }).finally(() => {
+        allLoading.transferLoading = false
+    })
+}
+
+function showVisible(type: keyof typeof allVisible) {
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
+
+const formItems = reactive([
+    { label: '订单编号', key: 'orderCode', value: '', labelClass: 'w-20 text-right text-gray-500', width: '48%' },
+    { label: '订单名称', key: 'orderName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '客户名称', key: 'customName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '商机名称', key: 'businessOpportunityName', 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: 'receivedStatus', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '已回款金额', key: 'receivedPayment', value: '', labelClass: 'w-24 text-right text-gray-500', width: '48%' },
+    { label: '未回款', key: 'unReceivedPayment', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '订单类型', key: 'type', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '下单时间', key: 'placeTime', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '订单开始时间', key: 'orderStartDate', value: '', labelClass: 'w-30 text-right text-gray-500', width: '48%' },
+    { label: '订单结束时间', key: 'orderEndDate', value: '', labelClass: 'w-30 text-right text-gray-500', width: '48%' },
+    { label: '负责人', key: 'inchargerName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '创建人', key: 'createName', 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: 'customSigner', value: '', labelClass: 'w-24 text-right text-gray-500', width: '48%' },
+    { label: '公司签约人', key: 'companySigner', value: '', labelClass: 'w-24 text-right text-gray-500', width: '48%' },
+    { 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)
+    orderTemplate.value = JSON.parse(datas.data[0].config)
+}
+
+// 生命周期钩子
+onMounted(async () => {
+    getSystemField()
+});
+</script>
+<style scoped lang="scss">
+.information {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+
+    .form {
+        .formItem {
+            .text {
+                display: -webkit-box;
+                /* Safari */
+                -webkit-line-clamp: 2;
+                /* number of lines to show */
+                -webkit-box-orient: vertical;
+                overflow: hidden;
+                line-height: 1.5;
+            }
+        }
+
+        .w-20,
+        .w-22 {
+            width: 80px;
+        }
+    }
+}
+</style>

+ 38 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/operationRecord.vue

@@ -0,0 +1,38 @@
+<template>
+    <div class="operationRecord pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">操作记录</div>
+        </div>
+        <div class="flex-1 overflow-auto pt-5">
+            <el-table :data="operationRecordtable" border style="width: 100%;height: 278px;">
+                <el-table-column prop="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, watchEffect } from 'vue'
+const props = defineProps<{
+    data: any
+}>()
+
+const operationRecordtable = ref([])
+
+watchEffect(() => {
+    const { data } = props
+    operationRecordtable.value = data.contactsLogList || []
+});
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.operationRecord {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 185 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/products.vue

@@ -0,0 +1,185 @@
+<template>
+    <div class="relatedTasks pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">相关产品</div>
+            <div class="flex">
+                <el-button type="primary" @click="productClick()">编辑产品</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-3">
+            <el-table :data="relatedTaskstable" border style="width: 100%;height: 300px;">
+                <el-table-column label="序号" width="80">
+                    <template #default="scope">
+                        {{ scope.$index + 1 }}
+                    </template>
+                </el-table-column>
+                <el-table-column prop="productName" label="产品名称" width="200">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large">{{
+                            scope.row.productName
+                        }}</el-button>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="productName" label="产品类别" width="130" />
+                <el-table-column prop="typeName" label="产品类型" width="130" />
+                <el-table-column prop="unitName" label="单位" width="130" />
+                <el-table-column prop="price" label="标准价格" width="130" />
+                <el-table-column prop="inventory" label="库存" width="130" />
+                <el-table-column prop="sellingPrice" label="售价" width="130" />
+                <el-table-column prop="quantity" label="数量" width="130" />
+                <el-table-column prop="discount" label="折扣(%)" width="130" />
+                <el-table-column prop="totalPrice" label="合计" width="130" />
+            </el-table>
+        </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">{{ '编辑产品' }}</h4>
+                    <div>
+                        <el-button type="primary" :loading="allLoading.editSaveLading" @click="saveOrder()">保存</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">
+                <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList"
+                    :productTableListValue="productTableListValue" :height="'55vh'" />
+            </div>
+        </el-dialog>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { GETGENERATEFOEM, GETTABLELISTPRODUCT, URL_OEDERUPDATE } from '../api';
+import { get, post } from '@/utils/request';
+import RelatedProducts from '@/components/relatedProducts/relatedProducts.vue'
+import { all } from 'axios';
+import { formatDate } from '@/utils/times';
+import { getTemplateKey } from '@/utils/tools';
+
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+const props = defineProps<{
+    data: any,
+    information: any
+}>()
+const allVisible = reactive({
+    editOrderVisible: false
+})
+const allLoading = reactive({
+    editSaveLading: false,
+    orderTemplateLoadinng: false
+})
+const info = ref({})
+const infoValue = ref({})
+const relatedProductsRef = ref<typeof RelatedProducts>()
+const relatedTaskstable = ref([])
+const productTableListValue = ref([])
+const productTableList = ref([])
+const orderTemplate = ref({
+    list: [],
+    config: {}
+})
+
+function saveOrder() {
+    let productTableListData = relatedProductsRef?.value?.returnData()
+    const items: any = infoValue.value
+    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, {
+        ...items,
+        orderEndDate: items.orderEndDate ? items.orderEndDate : '',
+        orderStartDate: items.orderStartDate ? items.orderStartDate : '',
+        orderProductDetailString: produt
+    }).then(() => {
+        globalPopup?.showSuccess('操作成功')
+        allLoading.editSaveLading = false
+        closeVisible('editOrderVisible')
+        emits('refreshData', 'getRelatedProducts')
+    }).finally(() => {
+        allLoading.editSaveLading = false
+    })
+}
+
+function productClick() {
+    productTableListValue.value = JSON.parse(JSON.stringify(relatedTaskstable.value))
+    showVisible('editOrderVisible')
+}
+
+function showVisible(type: keyof typeof allVisible) { // 显示弹窗
+    allVisible[type] = true
+}
+
+function closeVisible(type: keyof typeof allVisible) {
+    allVisible[type] = false
+}
+
+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: ''
+                }
+            })
+        }
+    })
+}
+
+async function getSystemField() {
+    const datas = await get(GETGENERATEFOEM)
+    orderTemplate.value = JSON.parse(datas.data[0].config)
+    setInfoValue(info.value)
+}
+
+function setInfoValue(item: any) {
+    const templateKey = getTemplateKey(orderTemplate.value.list)
+    let formVal: templateKey = { id: item.id }
+    for (let i = 0; i < templateKey.length; i++) {
+        const key = templateKey[i]
+        formVal[key] = item[key]
+    }
+    infoValue.value = formVal
+}
+
+watchEffect(() => {
+    const { data, information } = props
+    relatedTaskstable.value = data || []
+    info.value = information || {}
+});
+
+// 生命周期钩子
+onMounted(() => {
+    getProductTableList()
+    getSystemField()
+});
+</script>
+<style scoped lang="scss">
+.relatedTasks {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 134 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/component/rebate.vue

@@ -0,0 +1,134 @@
+<template>
+    <div class="operationRecord pl-4 pr-4 pt-3 pb-3 h-full flex flex-col">
+        <div class="flex justify-between">
+            <div class="title">回款</div>
+            <div class="flex">
+                <el-button type="primary" @click="editRebate(false)">新增回款</el-button>
+            </div>
+        </div>
+        <div class="flex-1 overflow-auto pt-5">
+            <el-table :data="operationRecordtable" border style="width: 100%;height: 278px;">
+                <el-table-column prop="createTime" label="回款时间" width="170" />
+                <el-table-column prop="creatorName" label="操作人" width="120" />
+                <el-table-column prop="money" label="回款金额" width="120" />
+                <el-table-column prop="unReceivedPayment" label="未回款金额" width="120" />
+                <el-table-column :label="'操作'" :width="'120px'" fixed="right">
+                    <template #default="scope">
+                        <el-button link type="primary" size="large" @click="editRebate(scope.row)">编辑</el-button>
+                        <el-button link type="danger" size="large" @click="deteItem(scope.row)">删除</el-button>
+                    </template>
+                </el-table-column>
+            </el-table>
+        </div>
+
+        <!-- 弹窗 -->
+        <el-dialog v-model="allVisible.rebateVisible" width="680" :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.rebateText }}</h4>
+                    <div class="flex">
+                        <el-button @click="allVisible.rebateVisible = false">取消</el-button>
+                        <el-button type="primary" @click="saveRebate" :loading="allLoading.rebateLoading">保存</el-button>
+                    </div>
+                </div>
+            </template>
+            <div class="p-8">
+                <div class="flex flex-row items-center">
+                    <div>回款金额:</div>
+                    <div class="flex-1">
+                        <el-input v-model.trim="mony" v-enterNumber placeholder="请输入" clearable class="w-full"></el-input>
+                    </div>
+                    <div class="ml-4">元</div>
+                </div>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+<script lang="ts" setup>
+import { post } from '@/utils/request';
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+import { URL_ADDREBATE, URL_DETELEITEMS, URL_EDITEBATE } from '../api';
+import { confirmAction } from '@/utils/tools';
+import { ITEM_RENDER_EVT } from 'element-plus/es/components/virtual-list/src/defaults';
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const emits = defineEmits(['refreshData']);
+const props = defineProps<{
+    data: any,
+    information: any
+}>()
+
+const allVisible = reactive({
+    rebateVisible: false,
+})
+const allLoading = reactive({
+    rebateLoading: false
+})
+const allText = reactive({
+    rebateText: '新增回款'
+})
+
+const operationRecordtable = ref([])
+const mony = ref('')
+const monyItem = ref<any>({})
+const info = ref<any>({})
+
+function deteItem(item: any) {
+    confirmAction(`确定删除该回款记录吗?`).then(() => {
+        post(URL_DETELEITEMS, { paymentId: item.id }).then((_res) => {
+            globalPopup?.showSuccess('删除成功')
+            emits('refreshData', 'getPaymentCollectionList')
+        }).catch((err) => {
+            globalPopup?.showError(err.msg || '')
+        })
+    })
+}
+
+function editRebate(item: any) {
+    if (!item) {
+        allText.rebateText = '新增回款'
+        mony.value = ''
+        monyItem.value = {}
+    } else {
+        monyItem.value = item
+        mony.value = item.money
+        allText.rebateText = '编辑回款'
+    }
+
+    allVisible.rebateVisible = true
+}
+
+function saveRebate() {
+    if (!mony.value || mony.value == '.') {
+        globalPopup?.showWarning('请输入金额')
+        return
+    }
+    allLoading.rebateLoading = true
+    const { url, formVal } = Object.keys(monyItem.value).length === 0
+        ? { url: URL_ADDREBATE, formVal: { orderId: info.value.id, money: mony.value } }
+        : { url: URL_EDITEBATE, formVal: { paymentId: monyItem.value.id, money: mony.value } };
+    post(url, { ...formVal }).then(() => {
+        globalPopup?.showSuccess('操作成功')
+        allVisible.rebateVisible = false
+        emits('refreshData', 'getPaymentCollectionList')
+    }).finally(() => {
+        allLoading.rebateLoading = false
+    })
+}
+
+watchEffect(() => {
+    const { data, information } = props
+    operationRecordtable.value = data || []
+    info.value = information
+});
+// 生命周期钩子
+onMounted(() => {
+});
+</script>
+<style scoped lang="scss">
+.operationRecord {
+    .title {
+        font-size: 18px;
+        color: #000
+    }
+}
+</style>

+ 175 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/detail/index.vue

@@ -1,7 +1,181 @@
 <template>
-    <div>销售订单详情</div>
+    <div class="h-full flex p-3 flex-col businessDetail" v-loading="pageLoading">
+        <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center">
+            <div class="icon mr-4">
+                <el-link :underline="false" @click="backPath()">
+                    <el-icon class="el-icon--right"><icon-view /></el-icon> 返回销售订单列表
+                </el-link>
+            </div>
+            <div class="mr-8">
+                <el-select v-model="values" placeholder="请选择" style="width: 300px" @change="getAll('')">
+                    <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+                </el-select>
+            </div>
+        </div>
+
+        <div class="flex-1 flex flex-col overflow-y-auto overflow-x-hidden scroll-bar" 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" @refreshData="getAll" />
+                </div>
+                <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+                    <Attachment :data="attachment" :information="information" @refreshData="getAll" />
+                </div>
+            </div>
+
+            <div class="w-full h-auto flex justify-between mt-2">
+                <div class="bg-white shadow-md rounded-md" style="width: 65%;">
+                    <Detailcompinents :data="relatedTasks" :information="information" :formTaskType="2" :filed="'orderId'"
+                        :disabled-list="['taskType', 'orderId']" @refreshData="refreshData" />
+                </div>
+                <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+                    <OperationRecord :data="operationRecord" />
+                </div>
+            </div>
+
+            <div class="w-full h-auto flex justify-between mt-2">
+                <div class="bg-white shadow-md rounded-md" style="width: 65%;">
+                    <Products :data="relatedProducts" :information="information" @refreshData="getAll" />
+                </div>
+                <div class="bg-white ml-2 shadow-md rounded-md flex-1" style="width: 33%;">
+                    <Rebate :data="paymentCollectionList" :information="information" @refreshData="getAll" />
+                </div>
+            </div>
+        </div>
+    </div>
 </template>
 
 <script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import { Edit, ArrowLeft as IconView } from '@element-plus/icons-vue'
+import { backPath } from '../../../utils/tools'
+import { useRoute } from "vue-router";
+import { post } from "@/utils/request";
+import { GETTABLELIST, IMPORTMOD, URL_DETEALORDER, URL_FERDETAILTASK, URL_PAYLIST, URL_PRODUTWITHORDER } from "../api";
+
+import Information from '../component/information.vue'
+import Attachment from '../component/attachment.vue'
+import OperationRecord from '../component/operationRecord.vue'
+import Products from '../component/products.vue'
+import Rebate from '../component/rebate.vue'
+import Detailcompinents from '@/components/detailcompinents/relatedTasks.vue'
+import { GETATTACHMENT, GETCENTERLIST } from "@/pages/product/api";
+
+const route = useRoute()
+const globalPopup = inject<GlobalPopup>('globalPopup')
+const pageLoading = ref(false)
+const rowId = ref(+(route.query.id || ''))
+const values = ref<number | string>('')
+const options = ref<optionType[]>([])
+const information = ref({}) // 基本信息
+const attachment = ref<any[]>([]) // 附件信息
+const relatedTasks = ref<any[]>([]) // 相关任务
+const operationRecord = ref<any[]>([]) // 操作记录
+const relatedProducts = ref<any[]>([]) // 相关产品
+const paymentCollectionList = ref<any[]>([]) // 回款
+
+// 获取基本信息
+function getDetail() {
+    post(URL_DETEALORDER, { id: values.value }).then(({ data }) => {
+        information.value = data
+    })
+}
+
+// 获取附件
+function getFileList() {
+    post(GETATTACHMENT, { moduleId: values.value, moduleCode: IMPORTMOD }).then((res) => {
+        attachment.value = res.data
+    })
+}
+
+// 获取所有销售订单
+function getAllContacts() {
+    post(GETTABLELIST, { pageIndex: -1, pageSize: -1 }).then(({ data }) => {
+        options.value = (data.record || []).map((item: any) => ({ value: item.id, label: item.orderName }))
+        values.value = rowId.value
+    }).catch((err) => {
+        globalPopup?.showError(err.message)
+    })
+}
+
+// 获取相关任务
+function getRelatedTasks() {
+    post(URL_FERDETAILTASK, { id: values.value }).then(({ data }) => {
+        relatedTasks.value = data
+    })
+}
+function refreshData() {
+    getAll('getRelatedTasks')
+}
+
+// 获取操作记录
+function getOperationRecord() {
+    post(GETCENTERLIST, { id: values.value, moduleCode: 'SalesOrder' }).then(({ data }) => {
+        operationRecord.value = data
+    })
+}
+
+function getPaymentCollectionList() {
+    post(URL_PAYLIST, { orderId: values.value }).then(({ data }) => {
+        paymentCollectionList.value = data || []
+    })
+}
+
+// 获取相关产品
+function getRelatedProducts() {
+    post(URL_PRODUTWITHORDER, { id: values.value }).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
+            }
+        })
+        relatedProducts.value = list
+    })
+}
+
+type allTypeStr = 'getDetail' | 'getFileList' | 'getRelatedTasks' | 'getOperationRecord' | 'getRelatedProducts' | 'getPaymentCollectionList' | ''
+
+async function getAll(event: allTypeStr) {
+    try {
+        pageLoading.value = true
+        if (!event) {
+            await Promise.all([
+                getDetail(),
+                getFileList(),
+                getRelatedTasks(),
+                getOperationRecord(),
+                getRelatedProducts(),
+                getPaymentCollectionList()
+            ])
+        } else if (event == 'getDetail') {
+            await getDetail()
+        } else if (event == 'getFileList') {
+            await getFileList()
+        } else if (event == 'getRelatedTasks') {
+            await getFileList()
+        } else if (event == 'getOperationRecord') {
+            await getOperationRecord()
+        } else if (event == 'getRelatedProducts') {
+            await getRelatedProducts()
+        } else if (event == 'getPaymentCollectionList') {
+            await getPaymentCollectionList()
+        }
+
+        pageLoading.value = false
+    } catch {
+        pageLoading.value = false
+    }
+}
+
+onMounted(() => {
+    values.value = rowId.value
+    getAllContacts()
+    getAll('')
+})
 
 </script>

+ 6 - 7
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue

@@ -88,10 +88,10 @@
         </div>
       </template>
       <div class="h-[60vh] overflow-y-auto scroll-bar pt-3" v-loading="allLoading.orderTemplateLoadinng">
-        <GenerateForm ref="orderTemplateRef" :data="orderTemplate" :value="orderTemplateValue" />
+        <GenerateForm ref="orderTemplateRef" :data="orderTemplate" :key="orderTemplateKey" :value="orderTemplateValue" />
         <div>相关产品</div>
         <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList"
-          :productTableListValue="productTableListValue" />
+          :productTableListValue="productTableListValue"  />
       </div>
     </el-dialog>
 
@@ -277,7 +277,6 @@ function submitForm(submitData: any, isClose: boolean) {
 function newTask(item: any) {
   const { id } = item
   taskModalForm.value = { ...createTaskFromType(2), orderId: id, }
-  console.log(taskModalForm.value)
   allVisible.taskModalVisible = true
 }
 
@@ -308,7 +307,6 @@ function saveOrder(flag: boolean) {
       allLoading.editSaveLading = false
     })
   }).catch((_err: any) => {
-    console.log(_err)
     globalPopup?.showError('请填写完整')
   })
 }
@@ -320,11 +318,12 @@ function editOrder(item: any) {
     editProduct(item)
     const templateKey = getTemplateKey(orderTemplate.value.list)
     let formVal: templateKey = { id: item.id }
-    for (let i = 0; i < orderTemplate.value.list.length; i++) {
+    for (let i = 0; i < templateKey.length; i++) {
       const key = templateKey[i]
       formVal[key] = item[key]
     }
     orderTemplateValue.value = formVal
+    console.log(formVal, '<============ 数据')
     allText.orderEditText = '编辑订单'
   }
   if (!item) {
@@ -333,9 +332,10 @@ function editOrder(item: any) {
     allText.orderEditText = '新增订单'
   }
   setTimeout(() => {
+    orderTemplateRef.value && orderTemplateRef.value.reset()
     orderTemplateKey.value++
     allLoading.orderTemplateLoadinng = false
-  }, 500)
+  }, 1000)
 }
 
 function toDetali(row: any) {
@@ -441,7 +441,6 @@ function editProduct(row: any) {
         totalPrice: +orderProductDetail?.totalPrice
       }
     })
-    console.log('开始执行', list)
     productTableListValue.value = list
   })
 }

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

@@ -93,7 +93,7 @@ async function getDetail(flag: boolean) {
       getInformationData(id),
       getFileList(id),
       post(GETCENTERLIST, { id, moduleCode: MODUCODE }).then((res) => {
-        operationRecord.value = res.datag
+        operationRecord.value = res.data
       }),
       post(GETORDER, { id }).then((res) => {
         products.value = res.data

+ 6 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/tasks/ImportModal.vue

@@ -13,8 +13,10 @@
         </div>
       </div>
     </template>
-    <div class="text-lg p-5">
-      <div class="pb-5">1. 点击下载 <a @click="getTemplate()" class="text-[#79BBFF] cursor-pointer">任务导入模板</a></div>
+    <div class="p-5">
+      <div class="pb-5">1. 点击下载 <el-link type="primary"
+          @click="downloadTemplate('Task', '任务导入模板.xlsx')">任务导入模板.xlsx</el-link>
+      </div>
       <div>2. 填写excel文件, 任务名称与优先级必填</div>
 
     </div>
@@ -24,6 +26,8 @@
 <script lang="ts" setup>
 import { ref, watch } from "vue";
 import { Props, Emits } from "./type"
+import { downloadTemplate } from '@/utils/tools'
+
 
 const props = defineProps<Props>();
 const emits = defineEmits<Emits>();

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

@@ -30,9 +30,9 @@ export const defaultSearchForm = {
 };
 export const PRIORITY = [
   //优先级
-  { label: "高", value: 0 },
+  { label: "高", value: 2 },
   { label: "中", value: 1 },
-  { label: "低", value: 2 },
+  { label: "低", value: 0 },
 ];
 export const STATUS: StatusType[] = [
   //任务状态

+ 20 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/customInstructions.ts

@@ -1,4 +1,4 @@
-import { Directive } from 'vue';
+import { Directive, ObjectDirective } from 'vue';
 
 // 权限控制
 const PermissionDirective: Directive = {
@@ -19,6 +19,23 @@ const PermissionDirective: Directive = {
     }
 };
 
+const PositiveIntegerDirective: ObjectDirective = {
+    mounted(el: HTMLElement) {
+        el.addEventListener('input', handleInput);
+    },
+    beforeUnmount(el: HTMLElement) {
+        el.removeEventListener('input', handleInput);
+    },
+};
+
+function handleInput(event: Event) {
+    const input = event.target as HTMLInputElement;
+    const regex = /^\d*\.?\d*$/;
+    if (!regex.test(input.value)) {
+        input.value = input.value.replace(/[^\d\.]/g, '');
+    }
+}
+
 function extractPath(str: any) {
     const startIndex = str.indexOf('/');
     const endIndex = str.indexOf('/', startIndex + 1);
@@ -27,7 +44,8 @@ function extractPath(str: any) {
 
 // 导出的自定义指令
 const customize = [
-    { key: 'permission', directive: PermissionDirective, name: '角色权限' }
+    { key: 'permission', directive: PermissionDirective, name: '角色权限' },
+    { key: 'enterNumber', directive: PositiveIntegerDirective, name: 'input正整数' }
 ]
 
 export default customize;

+ 14 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/times.ts

@@ -44,3 +44,17 @@ export function formatDateTime(date: Date) {
   const second = date.getSeconds().toString().padStart(2, "0");
   return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
 }
+
+/**
+ * 将 Date 对象格式化为 "YYYY-MM-DD HH:mm" 的形式
+ * @param date 日期 new Date()
+ * @returns
+ */
+export function formatDateMinutes(date: Date) {
+  const year = date.getFullYear();
+  const month = (1 + date.getMonth()).toString().padStart(2, "0");
+  const day = date.getDate().toString().padStart(2, "0");
+  const hour = date.getHours().toString().padStart(2, "0");
+  const minute = date.getMinutes().toString().padStart(2, "0");
+  return `${year}-${month}-${day} ${hour}:${minute}`;
+}

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

@@ -237,3 +237,29 @@ export function getTemplateKey(list: Array<any>) {
   });
   return models;
 }
+
+/**
+ * 设置模板数据禁用
+ * @param list 模板数据
+ * @param fieldList 需要禁用的字段
+ * @returns 
+ */
+export function setTemplateDataDisable(list: Array<any>, fieldList: Array<any>) {
+  let result = list;
+  result.forEach((item: any) => {
+    if (item.type === 'grid') {
+      item.columns.forEach((column: any) => {
+        column.list.forEach((subItem: any) => {
+          if (fieldList.includes(subItem.model)) {
+            subItem.options.disabled = true
+          }
+        });
+      });
+    } else {
+      if (fieldList.includes(item.model)) {
+        item.options.disabled = true;
+      }
+    }
+  })
+  return result;
+} 

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts

@@ -3,7 +3,7 @@ import vue from '@vitejs/plugin-vue';
 
 import { resolve } from 'path';
 
-const target = 'http://192.168.2.8:10010';
+const target = 'http://192.168.2.142:10010';
 // const target = "http://127.0.0.1:10010";
 // const target = "http://192.168.2.178:10010";
 // const target = 'http://47.101.180.183:10010';

+ 26 - 0
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/ContactsController.java

@@ -208,5 +208,31 @@ public class ContactsController {
             return msg;
         }
     }
+
+
+    //认领联系人 conctacts 里面要有要转移的负责人的 owner_id,id
+    @RequestMapping("claimContacts")
+    public HttpRespMsg claimContacts(Contacts contacts,HttpServletRequest request){
+        User user = userMapper.selectById(request.getHeader("Token"));//当前登陆的用户
+        HttpRespMsg msg = new HttpRespMsg();
+        if (contacts.getOwnerId()==null|| StringUtils.isEmpty(contacts.getOwnerId())){
+            msg.setError("请选择要认领的负责人");
+            return msg;
+        }
+        Contacts contactsGet=contactsService.getById(contacts.getId());
+        if (contactsGet.getOwnerId()!=null){
+            msg.setError("该联系人已有负责人");
+            return msg;
+        }
+        contactsGet.setOwnerId(contacts.getOwnerId());
+        int count= contactsService.claimContacts(contactsGet, user);
+        if (count>0){
+            msg.setMsg("操作成功");
+            return msg;
+        }else {
+            msg.setError("操作失败");
+            return msg;
+        }
+    }
 }
 

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

@@ -226,13 +226,23 @@ public class ReportController {
      * @return
      */
     @RequestMapping("/getCustomerTotalCount")
-    public HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId) {
-        return  customService.getCustomerTotalCount(startDate, endDate, userId, request);
+    public HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId,Integer departmentId,Integer exportType) {
+        return  customService.getCustomerTotalCount(startDate, endDate, userId,departmentId,exportType, request);
     }
+    @RequestMapping("/exportCustomerTotalCount")
+    public HttpRespMsg exportCustomerTotalCount(String startDate, String endDate,String userId,Integer departmentId,Integer exportType) throws Exception {
+        return reportService.exportCustomerTotalCount(startDate, endDate,userId, departmentId,exportType, request);
+    }
+
 
     @RequestMapping("/getCustomerTransferRate")
-    public HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId) {
-        return  customService.getCustomerTransferRate(startDate, endDate, userId, request);
+    public HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId,Integer departmentId,Integer exportType) {
+        return  customService.getCustomerTransferRate(startDate, endDate, userId,departmentId,exportType, request);
+    }
+
+    @RequestMapping("/exportCustomerTransferRate")
+    public HttpRespMsg exportCustomerTransferRate(String startDate, String endDate,String userId,Integer departmentId,Integer exportType) throws Exception {
+        return reportService.exportCustomerTransferRate(startDate, endDate,userId, departmentId,exportType, request);
     }
 
     @RequestMapping("/getReportList")

+ 107 - 11
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SalesOrderController.java

@@ -62,6 +62,18 @@ public class SalesOrderController {
         return salesOrderService.getList(inchargerId,orderType,orderName,orderCode,customId,businessName,receivedStatus,startDate,endDate,productCode,pageIndex,pageSize,isDelete);
     }
 
+    /**
+    * @Description:根据id获取详情数据
+    * @Param: [id]
+    * @return: com.management.platform.util.HttpRespMsg
+    * @Author: yurk
+    * @Date: 2024/5/31
+    */
+    @RequestMapping("/getDetail")
+    public HttpRespMsg getDetail(Integer id){
+        return salesOrderService.getDetail(id);
+    }
+
 
     /**
      * 新增编辑订单数据
@@ -73,6 +85,9 @@ public class SalesOrderController {
         Integer companyId = user.getCompanyId();
         order.setCompanyId(companyId);
         order.setCreatorId(user.getId());
+        if(order.getPrice()!=null){
+            order.setUnReceivedPayment(order.getPrice());
+        }
         int count;
         if(order.getId()==null){
             count = salesOrderService.count(new LambdaQueryWrapper<SalesOrder>().eq(SalesOrder::getCompanyId, companyId).eq(SalesOrder::getOrderCode, order.getOrderCode()));
@@ -87,7 +102,8 @@ public class SalesOrderController {
         AuditLogCenter auditLogCenter =new AuditLogCenter();
         auditLogCenter.setAuditorId(user.getId());
         auditLogCenter.setAuditorName(user.getName());
-        boolean isNew=true;
+        auditLogCenter.setModuleId(order.getId());
+        auditLogCenter.setModuleCode("SalesOrder");;
         if(order.getId()==null){
             auditLogCenter.setAuditorContent("创建了订单");
         }else {
@@ -96,14 +112,11 @@ public class SalesOrderController {
             String content = beanChangeUtil.contrastObj(oldOrder, order);
             //发生变化才生成记录
             if(!StringUtils.isEmpty(content.trim())){
-                isNew=false;
-                auditLogCenter.setModuleId(order.getId());
-                auditLogCenter.setModuleCode("SalesOrder");
                 auditLogCenter.setAuditorContent("编辑了订单");
-                auditLogCenterService.save(auditLogCenter);
             }
 
         }
+        auditLogCenterService.save(auditLogCenter);
         if(!salesOrderService.saveOrUpdate(order)){
             msg.setError("验证失败");
             return msg;
@@ -296,10 +309,17 @@ public class SalesOrderController {
         List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
         List<Task> taskList = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getOrderId, id));
         taskList.forEach(p->{
-            Optional<User> user = userList.stream().filter(u -> u.getId().equals(p.getExecutorId())).findFirst();
-            if(user.isPresent()){
-                p.setExecutorName(user.get().getName());
+            String executorIds = p.getExecutorId();
+            String[] split = executorIds.split(",");
+            List<String> executorNameList=new ArrayList<>();
+            for (String s : split) {
+                Optional<User> user = userList.stream().filter(u -> u.getId().equals(s)).findFirst();
+                if(user.isPresent()){
+                    executorNameList.add(user.get().getName());
+                }
             }
+//            String collect = executorNameList.stream().map(i -> i).collect(Collectors.joining(","));
+            p.setExecutorNames(executorNameList);
         });
         msg.setData(taskList);
         return msg;
@@ -339,8 +359,8 @@ public class SalesOrderController {
     @RequestMapping("/paymentCollection")
     public HttpRespMsg paymentCollection(Integer orderId,Double money){
         HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
         SalesOrder salesOrder = salesOrderService.getById(orderId);
-        BigDecimal price = salesOrder.getPrice();
         SalesOrderPayment salesOrderPayment=new SalesOrderPayment();
         salesOrderPayment.setMoney(new BigDecimal(money));
         salesOrderPayment.setOrderId(orderId);
@@ -349,8 +369,8 @@ public class SalesOrderController {
         //订单未回款金额
         BigDecimal unReceivedPayment = salesOrder.getUnReceivedPayment();
         //更新订单数据
-        receivedPayment=receivedPayment.add(price);
-        unReceivedPayment=unReceivedPayment.subtract(price);
+        receivedPayment=receivedPayment.add(new BigDecimal(money));
+        unReceivedPayment=unReceivedPayment.subtract(new BigDecimal(money));
         salesOrder.setReceivedPayment(receivedPayment);
         salesOrder.setUnReceivedPayment(unReceivedPayment);
         if(money>0){
@@ -363,6 +383,7 @@ public class SalesOrderController {
             }
         }
         salesOrderPayment.setUnReceivedPayment(unReceivedPayment);
+        salesOrderPayment.setCreatorId(user.getId());
         salesOrderService.updateById(salesOrder);
         salesOrderPaymentService.save(salesOrderPayment);
         return msg;
@@ -390,11 +411,86 @@ public class SalesOrderController {
     @RequestMapping("/paymentCollectionList")
     public HttpRespMsg paymentCollectionList(Integer orderId){
         HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, user.getCompanyId()));
         List<SalesOrderPayment> list = salesOrderPaymentService.list(new LambdaQueryWrapper<SalesOrderPayment>().eq(SalesOrderPayment::getOrderId, orderId));
+        list.forEach(l->{
+            Optional<User> first = userList.stream().filter(u -> u.getId().equals(l.getCreatorId())).findFirst();
+            if(first.isPresent()){
+                l.setCreatorName(first.get().getName());
+            }
+        });
         msg.setData(list);
         return msg;
     }
 
 
+    /**
+    * @Description:编辑回款数据
+    * @Param: [paymentId, money]
+    * @return: com.management.platform.util.HttpRespMsg
+    * @Author: yurk
+    * @Date: 2024/5/31
+    */
+    @RequestMapping("/editPayment")
+    public HttpRespMsg editPayment(Integer paymentId,Double money){
+        HttpRespMsg msg=new HttpRespMsg();
+        SalesOrderPayment payment = salesOrderPaymentService.getById(paymentId);
+        SalesOrder salesOrder = salesOrderService.getById(payment.getOrderId());
+        if(money!=null){
+            //找到原回款数据
+            BigDecimal nowMoney = new BigDecimal(money);
+            BigDecimal oldMoney = payment.getMoney();
+            nowMoney=nowMoney.subtract(oldMoney);
+            BigDecimal unReceivedPayment = payment.getUnReceivedPayment();
+            unReceivedPayment=unReceivedPayment.add(nowMoney);
+            payment.setMoney(new BigDecimal(money));
+            payment.setUnReceivedPayment(unReceivedPayment);
+            if(!salesOrderPaymentService.updateById(payment)){
+                msg.setError("验证失败");
+                return msg;
+            }
+            //订单上的已回款金额
+            BigDecimal receivedPayment = salesOrder.getReceivedPayment();
+            receivedPayment=receivedPayment.subtract(oldMoney).add(nowMoney);
+            salesOrder.setReceivedPayment(receivedPayment);
+            salesOrder.setUnReceivedPayment(unReceivedPayment);
+            if(!salesOrderService.updateById(salesOrder)){
+                msg.setError("验证失败");
+                return msg;
+            }
+        }
+       return msg;
+    }
+
+    /**
+    * @Description:删除回款数据
+    * @Param: [paymentId]
+    * @return: com.management.platform.util.HttpRespMsg
+    * @Author: yurk
+    * @Date: 2024/5/31
+    */
+    @RequestMapping("/deletePayment")
+    public HttpRespMsg deletePayment(Integer paymentId){
+        HttpRespMsg msg=new HttpRespMsg();
+        SalesOrderPayment payment = salesOrderPaymentService.getById(paymentId);
+        SalesOrder salesOrder = salesOrderService.getById(payment.getOrderId());
+        BigDecimal oldMoney = payment.getMoney();
+        //订单上的已回款金额
+        BigDecimal receivedPayment = salesOrder.getReceivedPayment();
+        receivedPayment=receivedPayment.subtract(oldMoney);
+        BigDecimal unReceivedPayment = salesOrder.getUnReceivedPayment();
+        unReceivedPayment=unReceivedPayment.add(oldMoney);
+        salesOrder.setReceivedPayment(receivedPayment);
+        salesOrder.setUnReceivedPayment(unReceivedPayment);
+        if(!salesOrderService.updateById(salesOrder)){
+            msg.setError("验证失败");
+            return msg;
+        }
+        salesOrderPaymentService.removeById(paymentId);
+        return msg;
+    }
+
+
 }
 

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

@@ -63,6 +63,9 @@ public class ContactsLog extends Model<ContactsLog> {
     @TableField("company_id")
     private Integer companyId;
 
+    @TableField(exist = false)
+    private String operateName;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -215,6 +215,12 @@ public class SalesOrder extends Model<SalesOrder> {
     @TableField(exist = false)
     private String creatorName;
 
+    @TableField(exist = false)
+    private String customSignerName;
+
+    @TableField(exist = false)
+    private String companySignerName;
+
     @TableField(exist = false)
     private String businessOpportunityName;
 

+ 24 - 2
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SalesOrderPayment.java

@@ -1,13 +1,18 @@
 package com.management.platform.entity;
 
 import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.extension.activerecord.Model;
 import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -15,7 +20,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2024-05-21
+ * @since 2024-05-31
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -24,7 +29,7 @@ public class SalesOrderPayment extends Model<SalesOrderPayment> {
 
     private static final long serialVersionUID=1L;
 
-    @TableId("id")
+    @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
     /**
@@ -45,6 +50,23 @@ public class SalesOrderPayment extends Model<SalesOrderPayment> {
     @TableField("un_received_payment")
     private BigDecimal unReceivedPayment;
 
+    /**
+     * 操作人
+     */
+    @TableField("creator_id")
+    private String creatorId;
+
+    @TableField(exist = false)
+    private String creatorName;
+
+    /**
+     * 操作时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -45,4 +45,6 @@ public interface ContactsService extends IService<Contacts> {
     HttpRespMsg getAllContacts(Integer customerId, HttpServletRequest request);
 
     int transferContacts(Contacts contactsGet, User user);
+
+    int claimContacts(Contacts contactsGet, User user);
 }

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

@@ -55,9 +55,10 @@ public interface CustomService extends IService<Custom> {
 
     void deleterDelete(List<Integer> ids);
 
-    HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId, HttpServletRequest request);
+    HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request);
 
-    HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId, HttpServletRequest request);
+    HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request);
 
     HttpRespMsg importData(MultipartFile multipartFile);
+
 }

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

@@ -149,4 +149,8 @@ public interface ReportService extends IService<Report> {
     HttpRespMsg getHasPushForSap(String startDate, String endDate, String employeeID);
 
     HttpRespMsg getCustomDataWithDate(String startDate, String endDate, HttpServletRequest request);
+
+    HttpRespMsg exportCustomerTotalCount(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) throws Exception;
+
+    HttpRespMsg exportCustomerTransferRate(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) throws Exception;
 }

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

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

+ 192 - 73
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/ContactsServiceImpl.java

@@ -3,6 +3,7 @@ package com.management.platform.service.impl;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 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.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
@@ -21,6 +22,10 @@ import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 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.BeanUtils;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -75,6 +80,9 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
     @Resource
     private ExcelExportServiceImpl excelExportService;
 
+    @Resource
+    private StageMapper stageMapper;
+
     @Value(value = "${upload.path}")
     private String path;
 
@@ -270,11 +278,24 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
 
         ContactsVo contactsVo = new ContactsVo();
         Contacts contactsSelect = contactsMapper.selectById(contacts.getId());
+
+        String token = String.valueOf(request.getHeader("Token"));
+        User user = userMapper.selectById(token);
+
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", user.getCompanyId()).eq("is_active", 1));
         if (contactsSelect != null) {
             //操作记录
             LambdaQueryWrapper<ContactsLog> cLoglqw = new LambdaQueryWrapper<>();
             cLoglqw.eq(ContactsLog::getContactsId, contactsSelect.getId());
             List<ContactsLog> contactsLogs = contactsLogMapper.selectList(cLoglqw);
+            if (contactsLogs!=null&&!contactsLogs.isEmpty()){
+                for (ContactsLog contactsLog : contactsLogs) {
+                    if (contactsLog.getOperateId()!=null&&StringUtils.isNotEmpty(contactsLog.getOperateId())){
+                        Optional<User> first = userList.stream().filter(u -> u.getId().equals(contactsLog.getOperateId())).findFirst();
+                        first.ifPresent(value -> contactsLog.setOperateName(value.getName()));
+                    }
+                }
+            }
 
             //相关任务
             LambdaQueryWrapper<Task> tLqw = new LambdaQueryWrapper<>();
@@ -297,6 +318,9 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
             LambdaQueryWrapper<BusinessOpportunity> bLqw = new LambdaQueryWrapper<>();
             bLqw.eq(BusinessOpportunity::getContactsId,contactsSelect.getId());
             List<BusinessOpportunity> opportunityList = businessOpportunityMapper.selectList(bLqw);
+            LambdaQueryWrapper<Stage> stageLQW = new LambdaQueryWrapper<>();
+            List<Stage> stages = stageMapper.selectList(stageLQW);
+
             opportunityList.forEach(businessOpportunity -> {
                 Custom custom = customMapper.selectById(businessOpportunity.getCustomerId());
                 businessOpportunity.setCustomerName(custom.getCustomName());
@@ -308,6 +332,10 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
                     User creatorUser = userMapper.selectById(businessOpportunity.getCreatorId());
                     businessOpportunity.setCreatorName(creatorUser.getName());
                 }
+                if (businessOpportunity.getStageId()!=null){
+                    Optional<Stage> first = stages.stream().filter(s -> s.getId().equals(businessOpportunity.getStageId())).findFirst();
+                    first.ifPresent(stage -> businessOpportunity.setStageValue(stage.getName()));
+                }
             });
 
             //附件信息查询
@@ -321,6 +349,22 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
             contactsVo.setTaskList(taskList);
             contactsVo.setBusinessOpportunityList(opportunityList);
             contactsVo.setContactsDocumentList(contactsDocuments);
+            if (contactsVo.getCustomId()!=null){
+                Custom custom = customMapper.selectById(contactsVo.getCustomId());
+                contactsVo.setCustomName(custom.getCustomName());
+            }
+            if (StringUtils.isNotEmpty(contactsVo.getOwnerId())){
+//                User user = userMapper.selectById(contactsVo.getOwnerId());
+//                contactsVo.setOwnerName(user.getName());
+                Optional<User> first = userList.stream().filter(u -> u.getId().equals(contactsVo.getOwnerId())).findFirst();
+                first.ifPresent(value -> contactsVo.setOwnerName(value.getName()));
+            }
+            if (StringUtils.isNotEmpty(contactsVo.getCreatorId())){
+//                User user = userMapper.selectById(contactsVo.getCreatorId());
+//                contactsVo.setCreatorName(user.getName());
+                Optional<User> first = userList.stream().filter(u -> u.getId().equals(contactsVo.getCreatorId())).findFirst();
+                first.ifPresent(value -> contactsVo.setCreatorName(value.getName()));
+            }
         }
         msg.setData(contactsVo);
         return msg;
@@ -334,7 +378,6 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
         User user = userMapper.selectById(request.getHeader("token"));
         Integer companyId = user.getCompanyId();
         WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
-        List<Contacts> contactsList = contactsMapper.selectList(new LambdaQueryWrapper<Contacts>().eq(Contacts::getCompanyId, companyId));
         List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
         InputStream inputStream = null;
         OutputStream outputStream = null;
@@ -351,9 +394,9 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
             outputStream.close();
 
             //解析表格
-            HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(file));
+            XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(file));
             //我们只需要第一个sheet
-            HSSFSheet sheet = workbook.getSheetAt(0);
+            XSSFSheet sheet = workbook.getSheetAt(0);
             //由于第一行需要指明列对应的标题
             int rowNum = sheet.getLastRowNum();
             //获取当前表单模板 校验规则
@@ -367,12 +410,43 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
             JSONObject configOb = JSON.parseObject(config);
             JSONArray configObJSONArray = configOb.getJSONArray("list");
 
+            //可能存在栅格布局的情况需要特殊处理
+            List<String> modelNameList=new ArrayList<>();
+            List<String> typeList=new ArrayList<>();
+            List<String> labelList=new ArrayList<>();
+            List<Boolean> requiredList=new ArrayList<>();
+
+            for (int i = 0; i < configObJSONArray.size(); i++) {
+                JSONObject jsonObject = configObJSONArray.getJSONObject(i);
+                if(jsonObject.getString("type").equals("grid")){
+                    JSONArray columns = jsonObject.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);
+                            modelNameList.add(object.getString("model"));
+                            typeList.add(object.getString("type"));
+                            labelList.add(object.getString("label"));
+                            Boolean required = object.getJSONObject("options").getJSONObject("rules").getBoolean("required");
+                            requiredList.add(required);
+                        }
+                    }
+                }else {
+                    modelNameList.add(jsonObject.getString("model"));
+                    typeList.add(jsonObject.getString("type"));
+                    labelList.add(jsonObject.getString("label"));
+                    Boolean required = jsonObject.getJSONObject("options").getJSONObject("rules").getBoolean("required");
+                    requiredList.add(required);
+                }
+            }
+
             List<Contacts>  importContactsList=new ArrayList<>();
             List<String> userNameList=new ArrayList<>();
             HttpRespMsg respMsg=new HttpRespMsg();
 
             for (int rowIndex = 1; rowIndex <= rowNum; rowIndex++) {
-                HSSFRow row = sheet.getRow(rowIndex);
+                XSSFRow row = sheet.getRow(rowIndex);
                 if (row == null) {
                     continue;
                 }
@@ -384,19 +458,24 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
                 int cellNum = row.getLastCellNum();
 
                 for (int i = 0; i < cellNum; i++) {
-                    JSONObject item = configObJSONArray.getJSONObject(i);
-                    String modelName = item.getString("model");
-                    HSSFCell cell = row.getCell(i);
-                    if(cell!=null){
-                        switch (item.getString("type")){
-                            case "time":cell.setCellType(CellType.NUMERIC);
-                                break;
-                            case "int":cell.setCellType(CellType.NUMERIC);
-                                break;
-                            default:cell.setCellType(CellType.STRING);
+                    String modelName = modelNameList.get(i);
+                    XSSFCell cell = row.getCell(i);
+                    if(modelName.equals("inchargerId")){
+                        if(!org.springframework.util.StringUtils.isEmpty(cell.getStringCellValue())){
+                            userNameList.add(cell.getStringCellValue());
                         }
                     }
-                    if(modelName.equals("inchargerId")){
+                    if(modelName.equals("ownerId")){
+                        if(!org.springframework.util.StringUtils.isEmpty(cell.getStringCellValue())){
+                            userNameList.add(cell.getStringCellValue());
+                        }
+                    }
+                    if(modelName.equals("customSigner")){
+                        if(!org.springframework.util.StringUtils.isEmpty(cell.getStringCellValue())){
+                            userNameList.add(cell.getStringCellValue());
+                        }
+                    }
+                    if(modelName.equals("companySigner")){
                         if(!org.springframework.util.StringUtils.isEmpty(cell.getStringCellValue())){
                             userNameList.add(cell.getStringCellValue());
                         }
@@ -416,7 +495,7 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
             List<User> targetUserList= (List<User>) respMsg.data;
 
             for (int rowIndex = 1; rowIndex <= rowNum; rowIndex++) {
-                HSSFRow row = sheet.getRow(rowIndex);
+                XSSFRow row = sheet.getRow(rowIndex);
                 if (row == null) {
                     continue;
                 }
@@ -430,18 +509,13 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
                 contacts.setCompanyId(companyId);
                 contacts.setCreatorId(user.getId());
                 for (int i = 0; i < cellNum; i++) {
-                    JSONObject item = configObJSONArray.getJSONObject(i);
-                    String modelName = item.getString("model");
+                    String modelName = modelNameList.get(i);
                     String className = modelName.substring(0, 1).toUpperCase() + modelName.substring(1);
                     String getter="get"+className;
                     String setter="set"+className;
-                    HSSFCell cell = row.getCell(i);
+                    XSSFCell cell = row.getCell(i);
                     if(cell!=null){
-                        switch (item.getString("type")){
-                            case "time":cell.setCellType(CellType.NUMERIC);
-                                break;
-                            case "int":cell.setCellType(CellType.NUMERIC);
-                                break;
+                        switch (typeList.get(i)){
                             default:cell.setCellType(CellType.STRING);
                         }
                     }
@@ -450,25 +524,36 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
                     Class<?> type = field.getType();
                     Method method = contactsClass.getMethod(setter, type);
                     //校验当前列是否为必填
-                    JSONObject options = item.getJSONObject("options");
-                    JSONObject rules = options.getJSONObject("rules");
-                    Boolean required = rules.getBoolean("required");
-                    if(required){
+                    if(requiredList.get(i)){
                         if(StringUtils.isEmpty(cell.getStringCellValue())){
-                            msg.setError(item.getString("label")+"值不能为空值");
+                            msg.setError(labelList.get(i)+"值不能为空值");
                             return msg;
                         }
                     }
-                    /*if(modelName.equals("contactsCode")){
-                        if(!org.springframework.util.StringUtils.isEmpty(cell.getStringCellValue())){
-                            //系统中同公司已存在的产品编码 更新
-                            Optional<Contacts> first = contactsList.stream().filter(p ->p.getContactsCode()!=null&& p.getContactsCode().equals(cell.getStringCellValue())).findFirst();
-                            if(first.isPresent()){
-                                contacts.setId(first.get().getId());
+                    if(modelName.equals("customId")){
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String customerName = cell.getStringCellValue();
+                            List<Custom> customList = customMapper.selectList(new QueryWrapper<Custom>().eq("custom_name", customerName));
+                            if (customList!=null&&!customList.isEmpty()){
+                                Custom custom = customList.get(0);
+                                contacts.setCustomId(custom.getId());
+                            }
+                             else {
+                                msg.setError("负责人["+customerName+"]在系统中不存在");
+                                return msg;
                             }
                         }
-                    }*/
-                    if(modelName.equals("inchargerId")){
+                    }else if(modelName.equals("sex")){
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            String stringCellValue = cell.getStringCellValue();
+                            if (stringCellValue.equals("男")){
+                                contacts.setSex(1);
+                            } else if (stringCellValue.equals("女")) {
+                                contacts.setSex(0);
+                            }
+                        }
+                    }
+                    else if(modelName.equals("ownerId")){
                         if(!StringUtils.isEmpty(cell.getStringCellValue())){
                             String userName = cell.getStringCellValue();
                             Optional<User> first;
@@ -484,30 +569,12 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
                                 throw new Exception("["+userName+"]在系统中不存在");
                             }
                         }
-                    }
-                    /*if(cell.getCellType()==CellType.STRING.getCode()&&!StringUtils.isEmpty(cell.getStringCellValue())){
-                        method.invoke(contacts,cell.getStringCellValue());
-                    } else if (cell.getCellType()==CellType.NUMERIC.getCode()) {
-
-
-                    }*/
-                    if (cell != null && cell.getCellTypeEnum() != CellType.BLANK) {
-                        if (cell.getCellTypeEnum() == CellType.STRING && cell.getStringCellValue() != null && !cell.getStringCellValue().isEmpty()) {
-                            // 字符串类型不为空
+                    }else{
+                        if(!StringUtils.isEmpty(cell.getStringCellValue())){
+                            System.out.println(cell.getStringCellValue());
                             method.invoke(contacts,cell.getStringCellValue());
-                        } else if (cell.getCellTypeEnum() == CellType.NUMERIC) {
-                            // 数值类型不为空
-                            double numericCellValue = cell.getNumericCellValue();
-                            if (type.getSimpleName().equals("Integer")){
-                                int intValue = (int) numericCellValue;
-                                method.invoke(contacts,intValue);
-                            }
-                            else {
-                                method.invoke(contacts, numericCellValue);
-                            }
                         }
                     }
-
                 }
                 importContactsList.add(contacts);
             }
@@ -539,9 +606,26 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
         JSONArray configObJSONArray = configOb.getJSONArray("list");
         List<List<String>> dataList=new ArrayList<>();
         List<String> titleList=new ArrayList<>();
+        List<String> modelList=new ArrayList<>();
         for (int i = 0; i < configObJSONArray.size(); i++) {
             JSONObject item = configObJSONArray.getJSONObject(i);
-            titleList.add(item.getString("label"));
+            String type = item.getString("type");
+            if (type.equals("grid")){
+                JSONArray columns = item.getJSONArray("columns");
+                for (int j = 0; j < columns.size(); j++) {
+                    JSONObject columnsJSONObject = columns.getJSONObject(j);
+                    JSONArray listJsonArray = columnsJSONObject.getJSONArray("list");
+                    for (int k = 0; k < listJsonArray.size(); k++) {
+                        JSONObject listJsonArrayJSONObject = listJsonArray.getJSONObject(k);
+                        titleList.add(listJsonArrayJSONObject.getString("label"));
+                        modelList.add(listJsonArrayJSONObject.getString("model"));
+                    }
+                }
+            }
+            else {
+                titleList.add(item.getString("label"));
+                modelList.add(item.getString("model"));
+            }
         }
         dataList.add(titleList);
 
@@ -557,26 +641,40 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
 
         for (ContactsVo contactsVo : records) {
             List<String> item=new ArrayList<>();
-            for (int i = 0; i < configObJSONArray.size(); i++) {
-                JSONObject target = configObJSONArray.getJSONObject(i);
-                String model = target.getString("model");
-//                String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+            for (int i = 0; i < modelList.size(); i++) {
+
+                String model = modelList.get(i);
+                String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
                 Class<? extends ContactsVo> aClass = contactsVo.getClass();
-                String value="";
-//                String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(contactsVo)==null?"":aClass.getMethod("get" + targetName).invoke(contactsVo));
-                /*if(model.equals("inchargerId")){
+                String value = "";
+
+                if(model.equals("customId")){
                     if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                        value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(product))+"$";
+                        value = "$userName"+String.valueOf(aClass.getMethod("getCustomName").invoke(contactsVo))+"$";
                     }else {
-                        value = String.valueOf(aClass.getMethod("getInchargerName").invoke(product));
+                        value = String.valueOf(aClass.getMethod("getCustomName").invoke(contactsVo)).equals("null") ?"" :String.valueOf(aClass.getMethod("getCustomName").invoke(contactsVo));
                     }
-                }*/
-                if(model.equals("sex")){
-                    value = String.valueOf(aClass.getMethod("getSex").invoke(contactsVo)).equals("null") ?"":String.valueOf(aClass.getMethod("getSex").invoke(contactsVo));
                 }
-                if(model.equals("name")){
-                    value = String.valueOf(aClass.getMethod("getName").invoke(contactsVo)).equals("null")?"":String.valueOf(aClass.getMethod("getName").invoke(contactsVo));
+                else if(model.equals("sex")){
+                    value = String.valueOf(aClass.getMethod("getSex").invoke(contactsVo)).equals("null") ?"":String.valueOf(aClass.getMethod("getSex").invoke(contactsVo));
+                    if (StringUtils.isNotEmpty(value)){
+                        if (value.equals("0")){
+                            value="女";
+                        }else {
+                            value="男";
+                        }
+                    }
+
                 }
+                else if(model.equals("ownerId")){
+                    if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                        value = "$userName"+String.valueOf(aClass.getMethod("getOwnerName").invoke(contactsVo))+"$";
+                    }else {
+                        value = String.valueOf(aClass.getMethod("getOwnerName").invoke(contactsVo)).equals("null") ? "" :String.valueOf(aClass.getMethod("getOwnerName").invoke(contactsVo));
+                    }
+                }else
+                    value= String.valueOf(aClass.getMethod("get" + targetName).invoke(contactsVo)==null?"":aClass.getMethod("get" + targetName).invoke(contactsVo));
+
                 item.add(value);
             }
             dataList.add(item);
@@ -620,5 +718,26 @@ public class ContactsServiceImpl extends ServiceImpl<ContactsMapper, Contacts> i
         }
     }
 
+    @Override
+    public int claimContacts(Contacts contactsGet, User user) {
+        UpdateWrapper<Contacts> contactsUpdateWrapper = new UpdateWrapper<>();
+        contactsUpdateWrapper.eq("id", contactsGet.getId());
+        contactsUpdateWrapper.set("owner_id", contactsGet.getOwnerId());
+        int count1 = contactsMapper.update(null, contactsUpdateWrapper);
+
+        ContactsLog contactsLog = new ContactsLog();
+        contactsLog.setCompanyId(user.getCompanyId());
+        contactsLog.setContactsId(contactsGet.getId());
+        contactsLog.setOperateId(user.getId());
+        contactsLog.setOperateDate(LocalDateTime.now());
+        contactsLog.setMsg("认领联系人");
+        int count2 = contactsLogMapper.insert(contactsLog);
+        if (count1>0&&count2>0){
+            return 1;
+        }else {
+            return 0;
+        }
+    }
+
 
 }

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

@@ -414,7 +414,7 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
     }
 
     @Override
-    public HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId, HttpServletRequest request) {
+    public HttpRespMsg getCustomerTotalCount(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) {
         User user = userMapper.selectById(request.getHeader("token"));
         Integer companyId = user.getCompanyId();
         List<UserVO> userVoList=userMapper.getCustomerTotalCount(startDate,endDate,userId,companyId);
@@ -433,7 +433,7 @@ public class CustomServiceImpl extends ServiceImpl<CustomMapper, Custom> impleme
     }
 
     @Override
-    public HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId, HttpServletRequest request) {
+    public HttpRespMsg getCustomerTransferRate(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) {
         User user = userMapper.selectById(request.getHeader("token"));
         Integer companyId = user.getCompanyId();
         List<UserVO> userVoList=userMapper.getCustomerTransferRate(startDate,endDate,userId,companyId);

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

@@ -22,12 +22,10 @@ import org.apache.commons.io.FileUtils;
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.*;
 import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
@@ -37,7 +35,6 @@ import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
-import javax.print.DocFlavor;
 import javax.servlet.http.HttpServletRequest;
 import java.io.*;
 import java.math.BigDecimal;
@@ -168,6 +165,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     @Resource
     private TaskService taskService;
     @Resource
+    private CustomService customService;
+    @Resource
     private ReportAuditorSettingService reportAuditorSettingService;
     @Resource
     private ReportAuditorSettingMapper reportAuditorSettingMapper;
@@ -5575,8 +5574,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                             if (company.getIsInternational() == 1) {
                                 operateDate = operateDate.plusSeconds(offsetSeconds);
                             }
-							
-								
+
+
                             String time = dtf.format(operateDate);
                             String msg= "";
 							if (auditMsg != null) {
@@ -5605,7 +5604,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 								}else {
 									msg = time+" " + auditMsg;
 								}
-							} 
+							}
                             if (!isFirst) {
                                 sb.append("->");
                             } else {
@@ -8732,4 +8731,56 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         httpRespMsg.data = finalMap;
         return httpRespMsg;
     }
+
+    @Override
+    public HttpRespMsg exportCustomerTotalCount(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) throws Exception {
+        User user = userMapper.selectById(request.getHeader("token"));
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+
+
+        List<List<String>> dataList=new ArrayList<>();
+        List<String> titleList=new ArrayList<>();
+        Collections.addAll(titleList,"员工姓名","新增客户数","成交客户数","客户成交率");
+        dataList.add(titleList);
+        HttpRespMsg respMsg = customService.getCustomerTotalCount(startDate, endDate, userId,departmentId, exportType, request);
+        List<UserVO> userVOList = (List<UserVO>) respMsg.getData();
+        for (UserVO userVO : userVOList) {
+            List<String> item = new ArrayList<>();
+            item.add(userVO.getName());
+            item.add((userVO.getCustomertotal()==null ? 0: userVO.getCustomertotal())+"");
+            item.add((userVO.getCustomerDeal()==null ? 0: userVO.getCustomerDeal())+"");
+            item.add((userVO.getDealRate()==null ? 0: userVO.getDealRate())+"");
+
+            dataList.add(item);
+
+        }
+        String fileName="客户总量分析表_"+ System.currentTimeMillis();
+        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,fileName,dataList,path);
+    }
+
+    @Override
+    public HttpRespMsg exportCustomerTransferRate(String startDate, String endDate, String userId, Integer departmentId, Integer exportType, HttpServletRequest request) throws Exception {
+        User user = userMapper.selectById(request.getHeader("token"));
+        WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+
+
+        List<List<String>> dataList=new ArrayList<>();
+        List<String> titleList=new ArrayList<>();
+        Collections.addAll(titleList,"员工姓名","客户转化率");
+
+        HttpRespMsg respMsg = customService.getCustomerTransferRate(startDate, endDate, userId, departmentId, exportType, request);
+        List<UserVO> userVOList = (List<UserVO>) respMsg.getData();
+
+        dataList.add(titleList);
+
+
+        for (UserVO userVO : userVOList) {
+            List<String> item = new ArrayList<>();
+            item.add(userVO.getName());
+            item.add(userVO.getDealRate()+"");
+            dataList.add(item);
+        }
+        String fileName="客户转化率分析表_"+ System.currentTimeMillis();
+        return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,fileName,dataList,path);
+    }
 }

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

@@ -196,6 +196,14 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
             if(incharger.isPresent()){
                 r.setInchargerName(incharger.get().getName());
             }
+            Optional<User> companySigner = userList.stream().filter(u -> u.getId().equals(r.getCompanySigner())).findFirst();
+            if(companySigner.isPresent()){
+                r.setCompanySignerName(companySigner.get().getName());
+            }
+            Optional<User> customSigner = userList.stream().filter(u -> u.getId().equals(r.getCustomSigner())).findFirst();
+            if(customSigner.isPresent()){
+                r.setCustomSignerName(customSigner.get().getName());
+            }
             Optional<SysDict> type = sysDictOfOrderType.stream().filter(s -> s.getId().equals(r.getType())).findFirst();
             if(type.isPresent()){
                 r.setTypeName(type.get().getName());
@@ -218,6 +226,55 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         return msg;
     }
 
+
+    @Override
+    public HttpRespMsg getDetail(Integer id) {
+        HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        SalesOrder salesOrder = salesOrderMapper.selectById(id);
+        if(salesOrder==null){
+            msg.setError("数据异常");
+            return msg;
+        }
+        List<OrderProductDetail> orderProductDetails = orderProductDetailService.list(new LambdaQueryWrapper<OrderProductDetail>().eq(OrderProductDetail::getOrderId,salesOrder.getId()));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, user.getCompanyId()));
+        List<SysDict> sysDictOfOrderType = sysDictMapper.selectList(new LambdaQueryWrapper<SysDict>().eq(SysDict::getCompanyId, user.getCompanyId()).eq(SysDict::getCode, "OrderType"));
+        List<Custom> customList = customService.list(new LambdaQueryWrapper<Custom>().eq(Custom::getCompanyId, user.getCompanyId()));
+        List<BusinessOpportunity> businessOpportunityList = businessOpportunityService.list(new LambdaQueryWrapper<BusinessOpportunity>().eq(BusinessOpportunity::getCompanyId, user.getCompanyId()));
+        Optional<User> item = userList.stream().filter(u -> u.getId().equals(salesOrder.getCreatorId())).findFirst();
+        if(item.isPresent()){
+            salesOrder.setCreatorName(item.get().getName());
+        }
+        Optional<User> incharger = userList.stream().filter(u -> u.getId().equals(salesOrder.getInchargerId())).findFirst();
+        if(incharger.isPresent()){
+            salesOrder.setInchargerName(incharger.get().getName());
+        }
+        Optional<User> companySigner = userList.stream().filter(u -> u.getId().equals(salesOrder.getCompanySigner())).findFirst();
+        if(companySigner.isPresent()){
+            salesOrder.setCompanySignerName(companySigner.get().getName());
+        }
+        Optional<User> customSigner = userList.stream().filter(u -> u.getId().equals(salesOrder.getCustomSigner())).findFirst();
+        if(customSigner.isPresent()){
+            salesOrder.setCustomSignerName(customSigner.get().getName());
+        }
+        Optional<SysDict> type = sysDictOfOrderType.stream().filter(s -> s.getId().equals(salesOrder.getType())).findFirst();
+        if(type.isPresent()){
+            salesOrder.setTypeName(type.get().getName());
+        }
+        Optional<Custom> custom = customList.stream().filter(c -> c.getId().equals(salesOrder.getCustomId())).findFirst();
+        if(custom.isPresent()){
+            salesOrder.setCustomName(custom.get().getCustomName());
+        }
+        Optional<BusinessOpportunity> businessOpportunity = businessOpportunityList.stream().filter(b -> salesOrder.getBusinessOpportunityId() != null && b.getId().equals(salesOrder.getBusinessOpportunityId())).findFirst();
+        if(businessOpportunity.isPresent()){
+            salesOrder.setBusinessOpportunityName(businessOpportunity.get().getName());
+        }
+        List<OrderProductDetail> details = orderProductDetails.stream().filter(o -> o.getOrderId().equals(salesOrder.getId())).collect(Collectors.toList());
+        salesOrder.setOrderProductDetailList(details);
+        msg.setData(salesOrder);
+        return msg;
+    }
+
     @Override
     public HttpRespMsg importData(MultipartFile multipartFile) {
         HttpRespMsg msg=new HttpRespMsg();
@@ -505,7 +562,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
     @Override
     public HttpRespMsg exportData(String userId, String orderName, String orderCode, String productCode) throws Exception {
         User user = userMapper.selectById(request.getHeader("token"));
-        SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, user.getCompanyId()).eq(SysForm::getCode, "Product").eq(SysForm::getIsCurrent, 1));
+        SysForm sysForm = sysFormMapper.selectOne(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, user.getCompanyId()).eq(SysForm::getCode, "Order").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);
@@ -531,8 +588,8 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         dataList.add(titleList);
         HttpRespMsg respMsg = getList( userId,null, orderName,orderCode,null,null,null,null,null,productCode, null,null,0);
         Map<String, Object> msgData = (Map<String, Object>) respMsg.getData();
-        List<Product> productList = (List<Product>) msgData.get("record");
-        for (Product product : productList) {
+        List<SalesOrder> salesOrderList = (List<SalesOrder>) msgData.get("record");
+        for (SalesOrder salesOrder : salesOrderList) {
             List<String> item=new ArrayList<>();
             for (int i = 0; i < configObJSONArray.size(); i++) {
                 JSONObject target = configObJSONArray.getJSONObject(i);
@@ -545,13 +602,13 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                             JSONObject object = list.getJSONObject(i2);
                             String model = object.getString("model");
                             String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
-                            Class<? extends Product> aClass = product.getClass();
-                            String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(product)==null?"":aClass.getMethod("get" + targetName).invoke(product));
+                            Class<? extends SalesOrder> aClass = salesOrder.getClass();
+                            String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(salesOrder)==null?"":aClass.getMethod("get" + targetName).invoke(salesOrder));
                             if(model.equals("inchargerId")){
                                 if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                                    value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(product))+"$";
+                                    value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(salesOrder))+"$";
                                 }else {
-                                    value = String.valueOf(aClass.getMethod("getInchargerName").invoke(product));
+                                    value = String.valueOf(aClass.getMethod("getInchargerName").invoke(salesOrder));
                                 }
                             }
                             item.add(value);
@@ -560,13 +617,13 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                 }else {
                     String model = target.getString("model");
                     String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
-                    Class<? extends Product> aClass = product.getClass();
-                    String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(product)==null?"":aClass.getMethod("get" + targetName).invoke(product));
+                    Class<? extends SalesOrder> aClass = salesOrder.getClass();
+                    String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(salesOrder)==null?"":aClass.getMethod("get" + targetName).invoke(salesOrder));
                     if(model.equals("inchargerId")){
                         if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                            value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(product))+"$";
+                            value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(salesOrder))+"$";
                         }else {
-                            value = String.valueOf(aClass.getMethod("getInchargerName").invoke(product));
+                            value = String.valueOf(aClass.getMethod("getInchargerName").invoke(salesOrder));
                         }
                     }
                     item.add(value);

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

@@ -736,9 +736,26 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
         JSONArray configObJSONArray = configOb.getJSONArray("list");
         List<List<String>> dataList=new ArrayList<>();
         List<String> titleList=new ArrayList<>();
+        List<String> modelList=new ArrayList<>();
         for (int i = 0; i < configObJSONArray.size(); i++) {
             JSONObject item = configObJSONArray.getJSONObject(i);
-            titleList.add(item.getString("label"));
+            String type = item.getString("type");
+            if (type.equals("grid")){
+                JSONArray columns = item.getJSONArray("columns");
+                for (int j = 0; j < columns.size(); j++) {
+                    JSONObject columnsJSONObject = columns.getJSONObject(j);
+                    JSONArray listJsonArray = columnsJSONObject.getJSONArray("list");
+                    for (int k = 0; k < listJsonArray.size(); k++) {
+                        JSONObject listJsonArrayJSONObject = listJsonArray.getJSONObject(k);
+                        titleList.add(listJsonArrayJSONObject.getString("label"));
+                        modelList.add(listJsonArrayJSONObject.getString("model"));
+                    }
+                }
+            }
+            else {
+                titleList.add(item.getString("label"));
+                modelList.add(item.getString("model"));
+            }
         }
         dataList.add(titleList);//设置表头
 
@@ -762,23 +779,13 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
 
         for (TasKVo tasKVo : taskVoList) {
             List<String> item=new ArrayList<>();
-            for (int i = 0; i < configObJSONArray.size(); i++) {
-                JSONObject target = configObJSONArray.getJSONObject(i);
-                String model = target.getString("model");
-//                String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
+            for (int i = 0; i <  modelList.size(); i++) {
+
+                String model = modelList.get(i);
+                String targetName = model.substring(0, 1).toUpperCase() + model.substring(1);
                 Class<? extends TasKVo> aClass = tasKVo.getClass();
                 String value="";
-//                String value = String.valueOf(aClass.getMethod("get" + targetName).invoke(tasKVo)==null?"":aClass.getMethod("get" + targetName).invoke(tasKVo));
-                /*if(model.equals("inchargerId")){
-                    if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                        value = "$userName"+String.valueOf(aClass.getMethod("getInchargerName").invoke(product))+"$";
-                    }else {
-                        value = String.valueOf(aClass.getMethod("getInchargerName").invoke(product));
-                    }
-                }*/
-                /*if(model.equals("sex")){
-                    value = String.valueOf(aClass.getMethod("getSex").invoke(tasKVo)).equals("null") ?"":String.valueOf(aClass.getMethod("getSex").invoke(tasKVo));
-                }*/
+
                 if(model.equals("taskName")){
                     value = String.valueOf(aClass.getMethod("getTaskName").invoke(tasKVo)).equals("null")?"":String.valueOf(aClass.getMethod("getTaskName").invoke(tasKVo));
                 }
@@ -846,6 +853,7 @@ public class TaskServiceImpl extends ServiceImpl<TaskMapper, Task> implements Ta
                 else if(model.equals("phone")){
                     value = String.valueOf(aClass.getMethod("getContactsPhone").invoke(tasKVo)).equals("null")?"":String.valueOf(aClass.getMethod("getContactsPhone").invoke(tasKVo));
                 }
+                else value= String.valueOf(aClass.getMethod("get" + targetName).invoke(tasKVo)==null?"":aClass.getMethod("get" + targetName).invoke(tasKVo));
                 item.add(value);
             }
             dataList.add(item);

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

@@ -8,11 +8,13 @@
         <result column="order_id" property="orderId" />
         <result column="money" property="money" />
         <result column="un_received_payment" property="unReceivedPayment" />
+        <result column="creator_id" property="creatorId" />
+        <result column="create_time" property="createTime" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, order_id, money, un_received_payment
+        id, order_id, money, un_received_payment, creator_id, create_time
     </sql>
 
 </mapper>

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

@@ -237,7 +237,7 @@ public class UserWithBeisenController {
                                 if(LocalDateTime.parse(o.getString("StartDate"),df1).toLocalDate().isEqual(LocalDateTime.parse(o.getString("StopDate"),df1).toLocalDate())){
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     //判断打卡时间是不是大于19:00 大于才算加班 通过打卡计算加班时长 与加班单作比较 取小
-                                    Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                    Duration timeDurantion = Duration.between(LocalTime.parse(workDay?"18:00:00":"08:30:00", df4), max.get());
                                     BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
                                     decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l = decimal.doubleValue();
@@ -255,7 +255,7 @@ public class UserWithBeisenController {
                                     decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l = decimal.doubleValue();
                                     //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                    Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                    Duration timeDurantion = Duration.between(LocalTime.parse(workDay?"18:00:00":"08:30:00", df4), max.get());
                                     BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
                                     decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l1 = decimal1.doubleValue();
@@ -270,7 +270,7 @@ public class UserWithBeisenController {
                                 if(LocalDateTime.parse(o.getString("StartDate"),df1).toLocalDate().isEqual(LocalDateTime.parse(o.getString("StopDate"),df1).toLocalDate())){
                                     //开始日期和结束日期是相同的情况 说明是加班区间只存在于当天的情况
                                     //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                    Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                    Duration timeDurantion = Duration.between(LocalTime.parse(workDay?"18:00:00":"08:30:00", df4), max.get());
                                     BigDecimal decimal = new BigDecimal(timeDurantion.toMinutes());
                                     decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l = decimal.doubleValue();
@@ -288,7 +288,7 @@ public class UserWithBeisenController {
                                     decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l = decimal.doubleValue();
                                     //判断打卡时间是不是大于19:00 通过打卡计算加班时长 与加班单作比较 取小
-                                    Duration timeDurantion = Duration.between(LocalTime.parse("18:00:00", df4), max.get());
+                                    Duration timeDurantion = Duration.between(LocalTime.parse(workDay?"18:00:00":"08:30:00", df4), max.get());
                                     BigDecimal decimal1 = new BigDecimal(timeDurantion.toMinutes());
                                     decimal1=decimal1.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
                                     double l1 = decimal1.doubleValue();
@@ -402,7 +402,7 @@ public class UserWithBeisenController {
                     leaveSheet.setRemark(jsonObject.getString("Reason"));
                     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));
+                    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, leaveSheet.getEndDate()));
                     if(one==null){
                         leaveSheetList.add(leaveSheet);
                     }

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

@@ -95,10 +95,10 @@ public interface ReportMapper extends BaseMapper<Report> {
                                                          @Param("userId") String userId,
                                                          @Param("companyId") Integer companyId, String viewUserId, Integer pageStart, Integer pageSize);
 
-    Integer getReportNameByDateAndDeptCount(@Param("date") String date,
-                                                         @Param("deptIds") List<Integer> deptIds,
-                                                         @Param("userId") String userId,
-                                                         @Param("companyId") Integer companyId, String viewUserId);
+//    Integer getReportNameByDateAndDeptCount(@Param("date") String date,
+//                                                         @Param("deptIds") List<Integer> deptIds,
+//                                                         @Param("userId") String userId,
+//                                                         @Param("companyId") Integer companyId, String viewUserId);
     List<Map<String, Object>> getDetailByStateInMyProfession(@Param("state") Integer state,
                                                @Param("companyId") Integer companyId,
                                                @Param("leaderId") String leaderId);

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

@@ -71,7 +71,6 @@ import java.util.stream.Collectors;
  * @since 2019-12-31
  */
 @Service
-@Transactional
 public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
 //    @Resource
 //    AsyncTaskExecutor asyncTaskExecutor;//注入线程池对象
@@ -249,7 +248,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             TimeType timeType = timeTypeMapper.selectById(companyId);
             List<Map<String, Object>> nameList = new ArrayList<>();
             List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全公司工时");
-            Integer totalMembCount = 0;
+//            Integer totalMembCount = 0;
             if (functionList.size() == 0) {
                 List<Map<String, Object>> list = new ArrayList<>();
                 List<Integer> allVisibleDeptIdList = null;
@@ -271,10 +270,15 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 } else {
                     allVisibleDeptIdList = getAllVisibleDeptIdList(user, null);
                 }
+                long start = System.currentTimeMillis();
                 //需要看可见部门(部门主要负责人和其他负责人以及查看本部门工时权限)所有人员的日报
                 nameList = reportMapper.getReportNameByDateAndDept(date,
                         allVisibleDeptIdList.size() > 0?allVisibleDeptIdList:null, targetUid, companyId, (deptId == null?viewUserId:null), pageStart, pageSize);
-                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, allVisibleDeptIdList.size() > 0?allVisibleDeptIdList:null, targetUid, companyId, (deptId == null?viewUserId:null));
+                long middle = System.currentTimeMillis();
+                System.out.println("中间获取getReportNameByDateAndDept 耗时:" + (middle - start) + "ms");
+//                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, allVisibleDeptIdList.size() > 0?allVisibleDeptIdList:null, targetUid, companyId, (deptId == null?viewUserId:null));
+                long end = System.currentTimeMillis();
+                System.out.println("获取totalMembCount 耗时:" + (end - middle) + "ms");
             } else {
                 //查看全公司的数据
                 List<Integer> ids = null;
@@ -296,9 +300,14 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                     }
                 }
+                long start = System.currentTimeMillis();
                 nameList = reportMapper.getReportNameByDateAndDept(date,
                         ids, targetUid, companyId, null, pageStart, pageSize);
-                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, ids, targetUid, companyId, null);
+                long middle = System.currentTimeMillis();
+                System.out.println("中间获取getReportNameByDateAndDept 耗时:" + (middle - start) + "ms");
+//                totalMembCount = reportMapper.getReportNameByDateAndDeptCount(date, ids, targetUid, companyId, null);
+//                long end = System.currentTimeMillis();
+//                System.out.println("获取totalMembCount 耗时:" + (end - middle) + "ms");
             }
             if (nameList.size() > 0) {
                 List<String> userIds = new ArrayList<>();
@@ -492,7 +501,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 HashMap retMap = new HashMap();
                 retMap.put("data", nameList);
                 retMap.put("pageIndex", pageIndex);
-                retMap.put("hasMore", pageIndex * pageSize + pageSize < totalMembCount);
+                retMap.put("hasMore", nameList.size() > 0);
                 httpRespMsg.data = retMap;
             }
         } catch (NullPointerException e) {
@@ -1046,6 +1055,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //新增或编辑报告
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg editReport(List<Report> reportList, String date, List<User> userList, BigDecimal hourCost, Integer companyId, String summary) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         TimeType timeType = timeTypeMapper.selectById(companyId);
@@ -1467,6 +1477,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //删除报告
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg deleteReport(String userId, String date) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         String delUserId = request.getHeader("TOKEN");
@@ -1756,6 +1767,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //审核通过某天某人的某报告
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg approveReport(String reportIds, Integer isDepartment, HttpServletRequest request,String evaluate) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         String token = request.getHeader("Token");
@@ -2672,6 +2684,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //审核未通过 以及 撤销审核某天某人
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg denyReport(String date, String reportIds, String reason, Integer isDepartment, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("Token"));
@@ -2984,22 +2997,25 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         if (list.size() > 0) {
             //存在查看权限的部门
             //获取公司全部人员; 按照人员状态,如果是已经离职的,当前日期在离职日期以后的,不需要显示该人员
+
             QueryWrapper<User> queryWrapper = new QueryWrapper<User>().eq("company_id", companyId);
             queryWrapper.and(wrapper->wrapper.eq("is_active", 1).eq("report_status",0)
                     .or(wrapper2->wrapper2.eq("is_active", 0).gt("inactive_date", date)));
             List<User> userList = userMapper.selectList(queryWrapper);
+            long t3 = System.currentTimeMillis();
+            System.out.println("获取人员列表耗时:" + (t3 - t2) + "ms");
             List<LeaveSheet> leaveSheetList = leaveSheetMapper.selectList(
                     new QueryWrapper<LeaveSheet>().select("id, owner_id, start_date, end_date, leave_type, time_type, time_days, time_hours").eq("company_id", companyId));
-            long t3 = System.currentTimeMillis();
-            System.out.println("获取人员请假列表耗时:" + (t3 - t2) + "ms");
+            long t4 = System.currentTimeMillis();
+            System.out.println("获取人员请假列表耗时:" + (t4 - t3) + "ms");
             LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
             List<HashMap> userMapList = new ArrayList<>();
             LocalDate curDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
 
             //获取当日已填写的人员报告
             List<Map<String, Object>> reportNameByDate = reportMapper.getReportNameByDate(date, companyId, null);
-            long t4 = System.currentTimeMillis();
-            System.out.println("获取当日已填写的人员报告耗时:" + (t4 - t3) + "ms");
+            long t5 = System.currentTimeMillis();
+            System.out.println("获取当日已填写的人员报告耗时:" + (t5 - t4) + "ms");
             Company company = companyMapper.selectById(companyId);
             TimeType timeType = timeTypeMapper.selectById(companyId);
             //如果没有开通OA模块,有开通企业微信同步考勤,从user_corpwx_time表中获取请假时长
@@ -3082,6 +3098,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg batchApproveReport(String reportIds, Integer isDepartment, HttpServletRequest request,String evaluate) throws Exception {
         String token = request.getHeader("Token");
         User user = userMapper.selectById(token);
@@ -3871,6 +3888,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg batchDenyReport(String reportIds, Integer isDepartment, String reason, HttpServletRequest request) {
         User user = userMapper.selectById(request.getHeader("Token"));
         Company company = companyMapper.selectById(user.getCompanyId());
@@ -4013,6 +4031,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg cancelReport(String userId, String reportIds, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
         User user = userMapper.selectById(userId);
@@ -6023,6 +6042,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg saveProjectTime(Integer companyId, List<Report> reportList, JSONArray userReportArray) {
         //如果当天该员工已有报告,要删除
         HttpRespMsg httpRespMsg = new HttpRespMsg();
@@ -6485,6 +6505,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg approveAllImport(HttpServletRequest request) {
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);
@@ -6501,6 +6522,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg denyHisReport(Integer hisId, String reason, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try {
@@ -6675,6 +6697,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg fixIssue(Integer companyId, String startDate, String endDate) {
         HttpRespMsg msg = new HttpRespMsg();
         List<Map<String, Object>> reportList = reportMapper.selectErrorGroupData(companyId, startDate, endDate);
@@ -6928,6 +6951,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg uploadThirdReportData(String yearMonth,HttpServletRequest request) {
         HttpRespMsg httpRespMsg=new HttpRespMsg();
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
@@ -7070,6 +7094,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg correctWorkingTime(String userIds, String startDate, String endDate) {
         HttpRespMsg msg=new HttpRespMsg();
         QueryWrapper<Report> queryWrapper=new QueryWrapper<>();
@@ -7208,7 +7233,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         reportMapper.update(upR, new QueryWrapper<Report>().in("id", targetRids));
     }
 
-
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg approveDeptAuditReport(User user, String auditDeptId) {
         HttpRespMsg msg = new HttpRespMsg();
         Integer companyId = user.getCompanyId();
@@ -7261,6 +7286,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
      * @return
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void moveReport(User sourceUser,User targetUser) {
         //转移日报
         List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().eq("creator_id", sourceUser.getId()));
@@ -7332,6 +7358,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg fillWorkingHours(String yearMonth, MonthWorkingTimeVO monthVO, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
 
@@ -7859,6 +7886,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg batchDelete(String userIds, Integer deptId, String startDate, String endDate, HttpServletRequest request) {
         //限制只针对赛元微电子
         HttpRespMsg msg = new HttpRespMsg();
@@ -7896,6 +7924,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg batchApproveByDate(String startDate, String endDate, Integer departmentId, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
         //限制只针对赛元微电子
@@ -8170,6 +8199,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg pushProjectReportToSap(String pushDate,Integer reportId) {
         HttpRespMsg httpRespMsg =new HttpRespMsg();
         LocalDateTime localDateTime=LocalDateTime.now();
@@ -9124,6 +9154,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg changeReminder(HttpServletRequest request,String createDate,String userId,String startDate,String endDate) throws Exception {
         HttpRespMsg msg=new HttpRespMsg();
         DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -9342,6 +9373,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public HttpRespMsg cancelReminder(HttpServletRequest request, String createDate, String userId) {
         HttpRespMsg msg=new HttpRespMsg();
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();

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

@@ -2350,7 +2350,7 @@ public class TimingTask {
                     leaveSheet.setRemark(jsonObject.getString("Reason"));
                     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));
+                    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, leaveSheet.getEndDate()));
                     if(one==null){
                         leaveSheetList.add(leaveSheet);
                     }

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

@@ -652,7 +652,7 @@
             AND a.create_date=#{date}
         </if>
         <if test="companyId != null and companyId != ''">
-            AND b.company_id=#{companyId}
+            AND a.company_id=#{companyId}
         </if>
         <if test="leaderId != null and leaderId != ''">
             AND (a.project_auditor_id = #{leaderId} or project.incharger_id = #{leaderId})
@@ -670,7 +670,7 @@
             AND a.create_date=#{date}
         </if>
         <if test="companyId != null and companyId != ''">
-            AND b.company_id=#{companyId}
+            AND a.company_id=#{companyId}
         </if>
         <if test="leaderId != null and leaderId != ''">
             AND project_ccuser.`user_id` = #{leaderId}
@@ -722,45 +722,45 @@
         </if>
     </select>
 
-    <select id="getReportNameByDateAndDeptCount" resultType="java.lang.Integer">
-        SELECT count(DISTINCT b.id) as count
-        FROM report AS a
-        JOIN user AS b ON a.creator_id=b.id
-        left join project on project.id = a.project_id
-        LEFT JOIN project_ccuser ON a.project_id = project_ccuser.project_id
-        WHERE 1=1
-        <if test="date != null and date != ''">
-            AND a.create_date=#{date}
-        </if>
-        <choose>
-            <when test="userId != null">
-                AND b.id=#{userId}
-            </when>
-            <otherwise>
-                <!--自己,担任项目经理,日报审核人,抄送人,或者自己部门的人填的日报 -->
-                <if test="viewUserId != null">
-                    and (a.creator_id = #{viewUserId} or a.project_auditor_id = #{viewUserId} or project.incharger_id = #{viewUserId}
-                    or project_ccuser.`user_id` = #{viewUserId}
-                    <if test="deptIds != null">
-                        or b.department_id in
-                        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">
-                            #{item, jdbcType=INTEGER}
-                        </foreach>
-                    </if>
-                    )
-                </if>
-                <if test="viewUserId == null">
-                    <if test="deptIds != null">
-                        and b.department_id in
-                        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">
-                            #{item, jdbcType=INTEGER}
-                        </foreach>
-                    </if>
-                </if>
-            </otherwise>
-        </choose>
-        AND b.company_id = #{companyId}
-    </select>
+<!--    <select id="getReportNameByDateAndDeptCount" resultType="java.lang.Integer">-->
+<!--        SELECT count(DISTINCT b.id) as count-->
+<!--        FROM report AS a-->
+<!--        JOIN user AS b ON a.creator_id=b.id-->
+<!--        left join project on project.id = a.project_id-->
+<!--        LEFT JOIN project_ccuser ON a.project_id = project_ccuser.project_id-->
+<!--        WHERE 1=1-->
+<!--        <if test="date != null and date != ''">-->
+<!--            AND a.create_date=#{date}-->
+<!--        </if>-->
+<!--        <choose>-->
+<!--            <when test="userId != null">-->
+<!--                AND b.id=#{userId}-->
+<!--            </when>-->
+<!--            <otherwise>-->
+<!--                &lt;!&ndash;自己,担任项目经理,日报审核人,抄送人,或者自己部门的人填的日报 &ndash;&gt;-->
+<!--                <if test="viewUserId != null">-->
+<!--                    and (a.creator_id = #{viewUserId} or a.project_auditor_id = #{viewUserId} or project.incharger_id = #{viewUserId}-->
+<!--                    or project_ccuser.`user_id` = #{viewUserId}-->
+<!--                    <if test="deptIds != null">-->
+<!--                        or b.department_id in-->
+<!--                        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">-->
+<!--                            #{item, jdbcType=INTEGER}-->
+<!--                        </foreach>-->
+<!--                    </if>-->
+<!--                    )-->
+<!--                </if>-->
+<!--                <if test="viewUserId == null">-->
+<!--                    <if test="deptIds != null">-->
+<!--                        and b.department_id in-->
+<!--                        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">-->
+<!--                            #{item, jdbcType=INTEGER}-->
+<!--                        </foreach>-->
+<!--                    </if>-->
+<!--                </if>-->
+<!--            </otherwise>-->
+<!--        </choose>-->
+<!--        AND b.company_id = #{companyId}-->
+<!--    </select>-->
 
     <!--专业待审核的报告列表-->
     <select id="getDetailByStateInMyProfession" resultType="java.util.Map">
@@ -785,7 +785,7 @@
         SELECT DISTINCT b.id, b.name, cast(b.department_id as SIGNED) as departmentId,a.create_date AS date
         FROM report AS a
         JOIN user AS b ON a.creator_id=b.id
-        WHERE a.department_audit_state = 0 and a.state = 0 AND b.company_id=#{companyId}
+        WHERE a.department_audit_state = 0 and a.state = 0 AND a.company_id=#{companyId}
         AND a.creator_id in (select id from user where department_id in
         <foreach collection="departmentIds" item="deptId" open="(" close=")" index="index" separator=",">
             #{deptId}

+ 2 - 0
fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue

@@ -236,6 +236,8 @@
                             <el-option :value="3" label="员工自由选择审批人"></el-option>
                             <el-option :value="4" label="项目所属BU审核"></el-option>
                             <el-option :value="5" label="所属部门负责人审核"></el-option>
+                            <el-option :value="6" label="直属或部门负责人审核->项目日报审核人审核"></el-option>
+                            <el-option :value="7" label="项目和部门并行审核"></el-option>
                         </el-select>
                     </el-form-item>
                     <el-form-item>

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/project/summary.vue

@@ -90,7 +90,7 @@
                                     :props="{ checkStrictly: true, expandTrigger: 'hover' }" :show-all-levels="false"
                                     clearable @change="getTaskTimeCompare()" size="mini"></el-cascader>
                                 
-                                <vueCascader :size="'mini'" :widthStr="'200'" :clearable="true" :subject="departmentOption" :radios="false" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1"></vueCascader>
+                                <vueCascader :size="'mini'" :widthStr="'200'" :clearable="true" :subject="departmentOption" :radios="true" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1"></vueCascader>
                                 
 
                                 <el-link @click="exportTaskCompare" style="float:right;"><i

+ 9 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -5689,7 +5689,15 @@
                     this.listLoading = false;
                     if (res.code == "ok") {
                         //扩增
-                        this.reportList = this.reportList.concat(res.data.data);
+                        if (res.data.data.length > 0) {
+                            this.reportList = this.reportList.concat(res.data.data);
+                        } else if (this.pageIndex > 1){
+                            this.$message({
+                            message: '没有更多数据了',
+                            type: "info"
+                        });
+                        }
+                        
                         this.hasMore = res.data.hasMore;//标记是否还有更多数据
                         if(document.querySelector("#day"+this.choseDay)){
                             document.querySelector("#day"+this.choseDay).scrollIntoView(true);