Просмотр исходного кода

Merge remote-tracking branch 'origin/master'

yusm 1 год назад
Родитель
Сommit
39a554d148
65 измененных файлов с 2846 добавлено и 402 удалено
  1. 3 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/api.ts
  2. 42 7
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/relatedProducts.vue
  3. 58 19
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/index.vue
  4. 0 10
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/type.d.ts
  5. 24 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/api.ts
  6. 66 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/attachment.vue
  7. 92 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/information.vue
  8. 58 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/operationRecord.vue
  9. 80 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedBusiness.vue
  10. 84 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedTasks.vue
  11. 71 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/detail/index.vue
  12. 140 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/index.vue
  13. 24 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/type.d.ts
  14. 4 4
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue
  15. 36 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/api.ts
  16. 7 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/detail/index.vue
  17. 186 2
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue
  18. 0 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/type.d.ts
  19. 2 1
      fhKeeper/formulahousekeeper/customerBuler-crm/vite.config.ts
  20. 58 3
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SalesOrderController.java
  21. 0 5
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WxCorpInfoController.java
  22. 2 0
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/BusinessOpportunity.java
  23. 3 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/SalesOrderService.java
  24. 39 4
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/SalesOrderServiceImpl.java
  25. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/resources/mapper/BusinessOpportunityMapper.xml
  26. 23 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  27. 258 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportLogController.java
  28. 6 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskCommentController.java
  29. 13 6
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java
  30. 80 23
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserWithBeisenController.java
  31. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/UserFvTime.java
  32. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  33. 6 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  34. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  35. 8 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java
  36. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectPercentageServiceImpl.java
  37. 280 6
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  38. 189 102
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  39. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  40. 77 21
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  41. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java
  42. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  43. 33 4
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  44. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserFvTimeMapper.xml
  45. 4 0
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  46. 25 20
      fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateFormItem.vue
  47. 1 1
      fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.css
  48. 239 25
      fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.es.js
  49. 1 1
      fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.es.js.map
  50. 1 1
      fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/views/workView/fillReport.vue
  51. 6 6
      fhKeeper/formulahousekeeper/timesheet/src/components/cascader.vue
  52. 1 5
      fhKeeper/formulahousekeeper/timesheet/src/components/cascaderOption.vue
  53. 24 3
      fhKeeper/formulahousekeeper/timesheet/src/components/select.vue
  54. 130 39
      fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue
  55. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/components/translationOpenData.vue
  56. 1 0
      fhKeeper/formulahousekeeper/timesheet/src/components/translationOpenDataText.vue
  57. 61 1
      fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue
  58. 3 3
      fhKeeper/formulahousekeeper/timesheet/src/views/project/finance.vue
  59. 24 4
      fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue
  60. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/team/index.vue
  61. 163 15
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  62. 19 19
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  63. 52 8
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/editPerfect/editPerfect.vue
  64. 17 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue
  65. 1 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue

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

@@ -2,7 +2,9 @@ export const MOD = "/business";
 export const prefix = "/clue";
 export const GETSYSFILED = "/sys-dict/getListByCode";
 export const GETPERSONNEL = "/user/getSimpleActiveUserList";
-export const GETGENERATEFOEM = `sys-form/getListByCode${MOD}`
+export const GETGENERATEFOEM = `/sys-form/getListByCode${MOD}`
+export const GETBUSINESSLIST = `/business-opportunity/list`
+export const UPDATEINSET = `/business-opportunity/insertAndUpdate`
 
 export const stageStatus = [
     { id: 1, name: "赢单", progress: "100%" },

+ 42 - 7
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/component/relatedProducts.vue

@@ -11,8 +11,9 @@
                 <template #default="scope">
                     <el-select v-model="productTable[scope.$index].productId" placeholder="请选择"
                         v-if="productTableIndex == scope.$index" clearable @clear="clearTableItem(scope.$index)"
-                        @change="selectChange(scope.$index, productTable[scope.$index].productName)">
-                        <el-option v-for="item in productArrar" :key="item.productId" :label="item.productName" :value="item.productId" />
+                        @change="selectChange(scope.$index, productTable[scope.$index].productId)">
+                        <el-option v-for="item in productArrar" :key="item.productId" :label="item.productName"
+                            :value="item.productId" />
                     </el-select>
                     <span v-else>{{ productTable[scope.$index].productName }}</span>
                 </template>
@@ -23,19 +24,25 @@
             <el-table-column prop="inventory" label="库存" width="80"></el-table-column>
             <el-table-column prop="sellingPrice" label="售价" width="180">
                 <template #default="scope">
-                    <el-input-number v-model="productTable[scope.$index].sellingPrice" class="mx-4" :min="0" :max="100000000" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <el-input-number @change="inputNumberChange('sellingPrice', productTable[scope.$index])"
+                        v-model="productTable[scope.$index].sellingPrice" class="mx-4" :min="0" :max="100000000"
+                        controls-position="right" v-if="productTableIndex == scope.$index" />
                     <span v-else>{{ productTable[scope.$index].sellingPrice }}</span>
                 </template>
             </el-table-column>
             <el-table-column prop="quantity" label="数量" width="180">
                 <template #default="scope">
-                    <el-input-number v-model="productTable[scope.$index].quantity" class="mx-4" :min="0" :max="100000000" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <el-input-number @change="inputNumberChange('quantity', productTable[scope.$index])"
+                        v-model="productTable[scope.$index].quantity" class="mx-4" :min="0" :max="100000000"
+                        controls-position="right" v-if="productTableIndex == scope.$index" />
                     <span v-else>{{ productTable[scope.$index].quantity }}</span>
                 </template>
             </el-table-column>
             <el-table-column prop="discount" label="折扣(%)" width="180">
                 <template #default="scope">
-                    <el-input-number v-model="productTable[scope.$index].discount" class="mx-4" :min="0" :max="100" controls-position="right" v-if="productTableIndex == scope.$index" />
+                    <el-input-number @change="inputNumberChange('discount', productTable[scope.$index])"
+                        v-model="productTable[scope.$index].discount" class="mx-4" :min="0" :max="100"
+                        controls-position="right" v-if="productTableIndex == scope.$index" />
                     <span v-else>{{ productTable[scope.$index].discount }}</span>
                 </template>
             </el-table-column>
@@ -62,9 +69,20 @@ const productTable: any = ref([{}])
 const productTableIndex = ref(0) // 可以编辑索引
 const productArrar: any = ref([])
 
+type ProductTableField = 'sellingPrice' | 'quantity' | 'discount'
+
+function inputNumberChange(field: ProductTableField, row: any) {
+    const { sellingPrice, quantity, discount, price } = row
+    row.discount = field == 'sellingPrice' ? Math.round(sellingPrice / price * 100) : row.discount
+    row.sellingPrice = field == 'discount' ? Math.round(price * discount / 100) : row.sellingPrice
+    if (sellingPrice && quantity && price && discount) {
+        row.totalPrice = Math.round(Math.round(price * discount / 100) * quantity)
+    }
+}
+
 function selectChange(index: number, val: number | string) {
-    let newObj = productArrar.value.find((item: any) => item.id == val)
-    productTable.value.splice(index, 1, newObj)
+    let newObj = productArrar.value.find((item: any) => item.productId == val)
+    productTable.value.splice(index, 1, { index, ...newObj })
 }
 
 function tableRowItem(row: any) {
@@ -77,7 +95,9 @@ function tableRowClassName({ row, rowIndex, }: { row: any, rowIndex: number }) {
 }
 
 function addTableItem(index: number) {
+    productTableIndex.value = -1
     productTable.value.splice(index + 1, 0, {})
+    console.log(productTable.value)
 }
 
 function clearTableItem(index: number) {
@@ -85,13 +105,28 @@ function clearTableItem(index: number) {
 }
 
 function deteleTableItem(index: number) {
+    productTableIndex.value = -1
     productTable.value.splice(index, 1)
 }
 
+function returnData() {
+    let jsonstr = JSON.stringify(productTable.value)
+    let json = '[{"index":0}]'
+    if(jsonstr == json) {
+        return false
+    }
+    return productTable.value
+}
+
 watchEffect(() => {
     const { productTableList } = props
     productArrar.value = productTableList || []
 });
+
+defineExpose({
+    returnData
+})
+
 </script>
   
 <style lang="scss" scoped></style>

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

@@ -39,8 +39,8 @@
           </el-form>
         </div>
         <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
-          <El-button class="w-full">重置</El-Button>
-          <El-button type="primary" class="w-full">搜索</El-Button>
+          <El-button class="w-full" @click="resetForm()">重置</El-Button>
+          <El-button type="primary" class="w-full" @click="getBusinessTableList()">搜索</El-Button>
         </div>
       </div>
     </div>
@@ -102,16 +102,18 @@
         <div class="flex justify-between items-center border-b pb-3 dialog-header">
           <h4 :id="titleId">{{ allText.newBusinessisibleText }}</h4>
           <div>
-            <el-button type="primary">保存并新建</el-button>
-            <el-button type="primary" @click="editBusiness()">保存</el-button>
+            <el-button type="primary" :loading="allLoading.newBusinessSaveLading"
+              :disabled="allLoading.businessSaveLading" @click="editBusiness(true)">保存并新建</el-button>
+            <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">
-        <!-- <GenerateForm ref="generateForm" :data="generateFormData" /> -->
+      <div class="h-[60vh] overflow-y-auto scroll-bar pt-3">
+        <GenerateForm ref="generateForm" :data="generateFormData" />
         <div>相关产品</div>
-        <RelatedProducts :productTableList="productTableList" />
+        <RelatedProducts ref="relatedProductsRef" :productTableList="productTableList" />
       </div>
     </el-dialog>
   </div>
@@ -121,7 +123,7 @@
 import { ref, reactive, onMounted, inject } from "vue";
 import type { FormInstance, FormRules } from 'element-plus'
 import { useRouter, useRoute } from "vue-router";
-import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM } from './api'
+import { GETSYSFILED, MOD, GETPERSONNEL, GETGENERATEFOEM, GETBUSINESSLIST, UPDATEINSET } from './api'
 import { GETTABLELIST } from '@/pages/product/api'
 import { post, get } from "@/utils/request";
 import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
@@ -133,17 +135,18 @@ const router = useRouter()
 const globalPopup = inject<GlobalPopup>('globalPopup')
 const businessTotalTable = ref(0)
 const generateForm = ref<typeof GenerateForm>() // 自定义表单dom
+const relatedProductsRef = ref<typeof RelatedProducts>()
 const generateFormData = ref({
   config: {},
   list: []
 }) // 自定义表单数据
-const businessTable = ref([
-  { name: '商机040101', phone: '张山' }
-])
-const allLoading = reactive<AllLoadingInterface>({
-  businessTableLading: false
+const businessTable = ref([])
+const allLoading = reactive({
+  businessTableLading: false,
+  businessSaveLading: false,
+  newBusinessSaveLading: false,
 })
-const allVisible = reactive<AllVisibleInterface>({
+const allVisible = reactive({
   newBusinessisible: false,
   recycleVisible: false,
 })
@@ -170,20 +173,33 @@ const fixedData = reactive({
 const productTableList = ref([])
 
 
-function editBusiness() {
+function editBusiness(visibles: boolean) {
   generateForm.value?.getData().then((res: any) => {
-    console.log('正确')
-    console.log(res)
+    let productTableListData = relatedProductsRef?.value?.returnData()
+    let newForm = {
+      ...res,
+      expectedTransactionDate: res.expectedTransactionDate ? formatDate(new Date(res.expectedTransactionDate)) : '',
+      businessItemProductList: productTableListData ? JSON.stringify(productTableListData) : []
+    }
+    allLoading.businessSaveLading = true
+    post(UPDATEINSET, { ...newForm }).then((_res) => {
+      allVisible.newBusinessisible = visibles
+      globalPopup?.showSuccess('保存成功')
+      getBusinessTableList()
+    }).finally(() => {
+      allLoading.businessSaveLading = false
+    })
   }).catch((_err: any) => {
+    console.log(_err)
     globalPopup?.showError('请填写完整')
   })
 }
 
-function showVisible(type: keyof AllVisibleInterface) { // 显示弹窗
+function showVisible(type: keyof typeof allVisible) { // 显示弹窗
   allVisible[type] = true
 }
 
-function closeVisible(type: keyof AllVisibleInterface) {
+function closeVisible(type: keyof typeof allVisible) {
   allVisible[type] = false
 }
 
@@ -191,6 +207,27 @@ function handleClose(done: () => void) {
   done()
 }
 
+function getBusinessTableList() {
+  const formValue = getFromValue(businessOpportunityForm)
+  post(GETBUSINESSLIST, { ...formValue }).then((res) => {
+    const { data, total } = res.data
+    businessTable.value = data
+    businessTotalTable.value = total
+  })
+}
+
+function resetForm() {
+  let reset = {
+    startTime: getFirstDayOfMonth(new Date()),
+    endTime: formatDate(new Date()),
+    pageIndex: 1,
+    pageFrom: 10
+  }
+  let newBusinessOpportunityForm = resetFromValue(businessOpportunityForm, { ...reset })
+  Object.assign(businessOpportunityForm, newBusinessOpportunityForm)
+  getBusinessTableList()
+}
+
 async function getSystemField() {
   const systemField = getAllListByCode(['商机阶段'])
   for (let i in systemField) {
@@ -229,6 +266,7 @@ function getProductTableList() {
       productTableList.value = record.map((item: any) => {
         const { id, productName, productCode, unit, unitName, typeName, type, price, inventory } = item
         return {
+          id,
           productId: id,
           productName,
           productCode,
@@ -250,6 +288,7 @@ function getProductTableList() {
 onMounted(() => {
   getSystemField()
   getProductTableList()
+  getBusinessTableList()
 })
 </script>
 

+ 0 - 10
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/business/type.d.ts

@@ -26,13 +26,3 @@ interface personnelInterface {
   phone: string;
   jobNumber: string;
 }
-
-type AllLoadingInterface = {
-  businessTableLading: boolean;
-}
-
-type AllVisibleInterface = {
-  newBusinessisible: boolean;
-  recycleVisible: boolean;
-}
-

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

@@ -0,0 +1,24 @@
+export const MOD = 'contacts'
+
+export const GETSYSFILED = "/sys-dict/getListByCode";
+export const GETPERSONNEL = "/user/getSimpleActiveUserList";
+export const GETGENERATEFOEM = `sys-form/getListByCode${MOD}`
+
+export const actionButtons: any[] = [
+    { text: '批量转移' },
+    { text: '批量删除' },
+    { text: '导入' },
+    { text: '导出' },
+]
+
+export const tableColumns: TableColumn[] = [
+    { prop: 'name', label: '联系人', event: 'toDetali', width: '150' },
+    { prop: 'mobile', label: '客户名称', width: '150' },
+    { prop: 'email', label: '电话号码', width: '200' },
+    { prop: 'wechat', label: '邮箱', width: '200' },
+    { prop: 'position', label: '职务', width: '100' },
+    { prop: 'company', label: '性别', width: '100' },
+    { prop: 'companya', label: '负责人', width: '100' },
+    { prop: 'companyb', label: '创建人', width: '100' },
+    { prop: 'companyc', label: '创建时间', width: '200' },
+]

+ 66 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/attachment.vue

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

+ 92 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/information.vue

@@ -0,0 +1,92 @@
+<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">转移</el-button>
+                <el-button type="primary">编辑</el-button>
+            </div>
+        </div>
+        <div class="form flex flex-wrap justify-between">
+            <div v-for="item in formItems" :key="item.label" class="formItem flex pt-2 pb-1" :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>
+    </div>
+</template>
+<script lang="ts" setup>
+import { ref, reactive, onMounted, onUnmounted, defineExpose, inject, watchEffect } from 'vue'
+
+const props = defineProps<{
+    data: any
+}>()
+
+const info: any = ref({})
+
+const formItems = reactive([
+    { label: '联系人', key: 'productCode', value: '', labelClass: 'w-20 text-right text-gray-500', width: '48%' },
+    { label: '客户', key: 'productName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '电话号码', key: 'typeName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '邮箱', key: 'unitName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '职务', key: 'price', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '性别', key: 'unit', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '地址', key: 'status', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '负责人', key: 'inchargerName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '创建人', key: 'creatorName', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '创建时间', key: 'createTime', value: '', labelClass: 'w-22 text-right text-gray-500', width: '48%' },
+    { label: '备注', key: 'descs', value: '', labelClass: 'w-22 text-right text-gray-500', width: '100%' },
+])
+
+watchEffect(() => {
+
+});
+
+// 生命周期钩子
+onMounted(async () => {
+    info.value = {
+        productCode: '联系的人',
+        productName: '客户名称',
+        typeName: '电话号码',
+        unitName: '邮箱地址',
+        price: '职务',
+        unit: '性别',
+        status: 1,
+        inchargerName: '负责人姓名',
+        creatorName: '创建人姓名',
+        createTime: '创建时间',
+        descs: '备注信息'
+    };
+
+    formItems.forEach(item => {
+        item.value = info.value[item.key];
+    });
+});
+</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>

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

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

+ 80 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/component/relatedBusiness.vue

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

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

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

+ 71 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/detail/index.vue

@@ -0,0 +1,71 @@
+<template>
+    <div class="h-full flex p-3 flex-col businessDetail">
+        <div class="w-full bg-white p-2 mb-2 shadow-md rounded-md flex items-center">
+            <div class="icon mr-4">
+                <el-link :underline="false" @click="backPath()">
+                    <el-icon class="el-icon--right"><icon-view /></el-icon> 返回联系人列表
+                </el-link>
+            </div>
+            <div class="mr-8">
+                <el-select v-model="value" placeholder="请选择" style="width: 150px">
+                    <el-option v-for="item in options" :key="item.id" :label="item.productName" :value="item.id" />
+                </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" />
+                </div>
+                <div class="bg-white ml-2 shadow-md rounded-md flex-1">
+                    <Attachment :data="attachment" :information="information" />
+                </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 :data="relatedTasks" :information="information" />
+                </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 w-full">
+                    <RelatedBusiness :data="relatedBusiness" />
+                </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 Information from '../component/information.vue'
+import Attachment from '../component/attachment.vue'
+import RelatedTasks from '../component/relatedTasks.vue'
+import OperationRecord from '../component/operationRecord.vue'
+import RelatedBusiness from '../component/relatedBusiness.vue'
+
+const information = ref({}) // 基本信息
+const attachment = ref([]) // 附件
+const relatedTasks = ref([]) // 相关任务
+const operationRecord = ref([]) // 操作记录
+const relatedBusiness = ref([]) // 相关商机
+
+const pageLoading = ref(false)
+
+const value = ref('')
+const options: any = ref([])
+
+onMounted(() => {
+    
+})
+</script>
+    
+<style lang="scss" scoped></style>

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

@@ -1,11 +1,149 @@
 <template>
-  <div>
-    contacts
+  <div class="h-full flex">
+    <div class="p-5 w-80 pr-0">
+      <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
+        <div class="flex-1 p-3 overflow-y-auto">
+          <!-- 筛选条件 -->
+          <el-form :model="filterForm" label-width="70px" style="max-width: 600px">
+            <el-form-item v-for="(item, index) in filterItems" :key="index" :label="item.label">
+              <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable placeholder="请输入"></el-input>
+              <el-select v-else v-model="filterForm[item.key as keyof FilterForm]" placeholder="请选择" clearable>
+                <el-option v-for="option in item.options" :key="option.id" :label="option.name" :value="option.id" />
+              </el-select>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+    </div>
+    <div class="flex-1 p-5 overflow-auto">
+      <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
+        <div class="flex justify-end pb-3">
+          <!-- 操作按钮 -->
+          <el-button v-for="(button, index) in actionButtons" :key="index" type="primary">{{ button.text }}</el-button>
+        </div>
+        <div class="flex-1 w-full overflow-hidden">
+          <!-- 表格 -->
+          <el-table ref="clueTableRef" :data="formTable" border v-loading="allLoading.formTableLading"
+            style="width: 100%;height: 100%;">
+            <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop" :label="column.label" :width="column.width">
+              <template #default="scope">
+                <template v-if="column.event === 'toDetali'">
+                  <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row.name }}</el-button>
+                </template>
+              </template>
+            </el-table-column>
+            <el-table-column :label="'操作'" :width="'200px'" fixed="right">
+              <template #default="scope">
+                <el-button link type="primary" size="large">编辑</el-button>
+                <el-button link type="primary" size="large">新建任务</el-button>
+                <el-button link type="danger" size="large">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div class="flex justify-end pt-3">
+          <!-- 分页 -->
+          <el-pagination layout="total, prev, pager, next, sizes" :total="formTablePaging.total"
+            :hide-on-single-page="true" />
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
+import { post, get } from "@/utils/request";
+import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD } from "./api";
+import { useRouter, useRoute } from "vue-router";
 
+const router = useRouter()
+
+const filterForm = reactive<FilterForm>({ // 筛选条件
+  contactPerson: "",
+  customerId: "",
+  phoneNumber: '',
+  responsibleId: '',
+  createId: '',
+  email: '',
+});
+const selectData = reactive({ // 下拉数据
+  Personnel: [] as personnelInterface[],
+  Customer: [] as any[],
+})
+const filterItems = ref<FilterItem[]>([
+    { label: '联系人', key: 'contactPerson', type: 'input' },
+    { label: '客户名称', key: 'customerId', type: 'select', options: selectData.Customer },
+    { label: '电话号码', key: 'phoneNumber', type: 'input' },
+    { label: '邮箱', key: 'email', type: 'input' },
+    { label: '负责人', key: 'responsibleId', type: 'select', options: selectData.Personnel },
+    { label: '创建人', key: 'createId', type: 'select', options: selectData.Personnel },
+])
+const formTablePaging = reactive({ // 分页条件
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+})
+const generateFormData = ref([]) // 自定义表单数据
+
+const formTable = ref([
+  {name: '联系人', mobile: '13311112222', email: '123@qq.com', responsible: '张三', createId: '张三'}
+]) // 表格数据
+const allLoading = reactive({ // 按钮加载 Loading
+  formTableLading: false,
+})
+const dialogVisible = reactive({
+
+})
+
+function toDetali(row: any) {
+  router.push({
+    path: `${MOD}/detail`,
+    query: { id: row.name }
+  })
+}
+
+async function getSystemField() {
+  const systemField = getAllListByCode([])
+  for (let i in systemField) {
+    const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
+    for (let key of Object.keys(selectData)) {
+      if (systemField[i] == key) {
+        Object.assign(selectData, { [key]: data })
+      }
+    }
+  }
+
+  const { data } = await post(GETPERSONNEL, {})
+  selectData.Personnel = data.map((item: any) => {
+    const { id, name, phone, jobNumber } = item
+    return {
+      id, name, phone, jobNumber
+    }
+  })
+
+  // const res = await get(GETGENERATEFOEM)
+  // generateFormData.value = JSON.parse(res.data[0].config)
+
+  setFilterItems()
+}
+
+function setFilterItems() {
+  console.log(selectData)
+  filterItems.value = [
+    { label: '联系人', key: 'contactPerson', type: 'input' },
+    { label: '客户名称', key: 'customerId', type: 'select', options: selectData.Customer },
+    { label: '电话号码', key: 'phoneNumber', type: 'input' },
+    { label: '邮箱', key: 'email', type: 'input' },
+    { label: '负责人', key: 'responsibleId', type: 'select', options: selectData.Personnel },
+    { label: '创建人', key: 'createId', type: 'select', options: selectData.Personnel },
+  ]
+}
+
+onMounted(() => {
+  getSystemField()
+})
 </script>
 
 <style lang="scss" scoped></style>

+ 24 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/contacts/type.d.ts

@@ -0,0 +1,24 @@
+interface FilterItem {
+    label: string;
+    key: string;
+    type: string;
+    options?: any[];
+}
+
+interface TableColumn {
+    prop: string;
+    label: string;
+    width: string;
+    event?: string;
+}
+
+interface FilterForm {
+    contactPerson: string;
+    customerId: string;
+    phoneNumber: string;
+    responsibleId: string;
+    createId: string;
+    email: string;
+    startTime?: string;
+    endTime?: string
+}

+ 4 - 4
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/login.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="loginView w-screen h-screen overflow-hidden flex">
-    <div class="w-96 bg-white m-auto border-t-8 border-blue-400 shadow-xl rounded-md pl-9 pr-9">
+    <div class="w-96 bg-white m-auto border-t-8 border-[#075985] shadow-xl rounded-md pl-9 pr-9">
       <div class="m-auto pt-4">
         <img class="w-1/5 h-1/5 m-auto" :src="loginLogo" alt="">
       </div>
@@ -24,10 +24,10 @@
         <img class="w-9 m-auto p-1 rounded-full border-blue-300 border-2 cursor-pointer" :src="qiyeweixin" alt="">
       </div>
       <div class="flex justify-between pb-5">
-        <div class="cursor-pointer text-blue-400 hover:text-blue-300">联系客服</div>
+        <el-link type="primary" :underline="false">联系客服</el-link>
         <div class="flex justify-around">
-          <div class="mr-4 cursor-pointer text-blue-400 hover:text-blue-300" @click="useHelp()">使用说明</div>
-          <div class="cursor-pointer text-blue-400 hover:text-blue-300" @click="toRegister()">企业注册</div>
+          <el-link class="mr-4" type="primary" :underline="false"  @click="useHelp()">使用说明</el-link>
+          <el-link type="primary" :underline="false" @click="toRegister()">企业注册</el-link>
         </div>
       </div>
       <el-dialog v-model="helpDialog" width="30%" title="使用说明">

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

@@ -0,0 +1,36 @@
+export const MOD = '/order'
+
+export const GETSYSFILED = "/sys-dict/getListByCode";
+export const GETPERSONNEL = "/user/getSimpleActiveUserList";
+export const GETGENERATEFOEM = `/sys-form/getListByCode${MOD}`
+export const GETALLPRODUCT = `/sys-form/getListByCode/product`
+export const GETTABLELIST =  `${MOD}/list`
+
+export const actionButtons: any[] = [
+    { text: '新建订单' },
+    { text: '批量转移' },
+    { text: '批量删除' },
+    { text: '回收站' },
+    { text: '导入' },
+    { text: '导出' },
+]
+
+export const tableColumns: TableColumn[] = [
+    { prop: 'name', label: '订单编号', event: 'toDetali', width: '150' },
+    { prop: 'mobile', label: '订单名称', width: '150' },
+    { prop: 'email', label: '客户名称', width: '200' },
+    { prop: 'wechat', label: '商机名称', width: '200' },
+    { prop: 'position', label: '订单金额', width: '100' },
+    { prop: 'company', label: '已回款', width: '100' },
+    { prop: 'companya', label: '未回款', width: '100' },
+    { prop: 'companyb', label: '回放状态', width: '100' },
+    { prop: 'companyc', label: '订单类型', width: '200' },
+    { prop: 'companyd', label: '下单时间', width: '200' },
+    { prop: 'companye', label: '订单开始时间', width: '200' },
+    { prop: 'companyf', label: '订单结束时间', width: '200' },
+    { prop: 'companyg', label: '客户签的人', width: '200' },
+    { prop: 'companyh', label: '公司签的人', width: '200' },
+    { prop: 'companyj', label: '负责人', width: '200' },
+    { prop: 'companyk', label: '创建人', width: '200' },
+    { prop: 'companyl', label: '创建时间', width: '200' },
+]

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

@@ -0,0 +1,7 @@
+<template>
+    <div>销售订单详情</div>
+</template>
+
+<script lang="ts" setup>
+
+</script>

+ 186 - 2
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/order/index.vue

@@ -1,11 +1,195 @@
 <template>
-  <div>
-    order
+  <div class="h-full flex">
+    <div class="p-5 w-80 pr-0">
+      <div class="bg-white w-full h-full shadow-md rounded-md flex flex-col">
+        <div class="flex-1 p-3 overflow-y-auto">
+          <!-- 筛选条件 -->
+          <el-form :model="filterForm" label-width="70px" style="max-width: 600px">
+            <template v-for="(item, index) in filterItems">
+              <el-form-item :label="item.label" v-if="item.type != 'date'">
+                <el-input v-if="item.type === 'input'" v-model="filterForm[item.key as keyof FilterForm]" clearable
+                  placeholder="请输入"></el-input>
+                <el-select v-else v-model="filterForm[item.key as keyof FilterForm]" placeholder="请选择" clearable>
+                  <el-option v-for="option in item.options" :key="option.id" :label="option.name" :value="option.id" />
+                </el-select>
+              </el-form-item>
+              <template v-if="item.type == 'date'">
+                <el-form-item :label="item.label">
+                  <el-date-picker v-model="filterForm.startTime" type="date" placeholder="请选择" :clearable="false"
+                    format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+                </el-form-item>
+                <el-form-item label="">
+                  <el-date-picker v-model="filterForm.endTime" type="date" placeholder="请选择" :clearable="false"
+                    format="YYYY-MM-DD" value-format="YYYY-MM-DD" />
+                </el-form-item>
+              </template>
+            </template>
+          </el-form>
+        </div>
+        <div class="w-full flex p-3 shadow-[0_-3px_5px_0px_rgba(0,0,0,0.2)]">
+          <El-button class="w-full" @click="resetFilterForm()">重置</El-Button>
+          <El-button type="primary" class="w-full" @click="getTableList()">搜索</El-Button>
+        </div>
+      </div>
+    </div>
+    <div class="flex-1 p-5 overflow-auto">
+      <div class="bg-white w-full h-full p-3 shadow-md rounded-md flex flex-col">
+        <div class="flex justify-end pb-3">
+          <!-- 操作按钮 -->
+          <el-button v-for="(button, index) in actionButtons" :key="index" type="primary">{{ button.text }}</el-button>
+        </div>
+        <div class="flex-1 w-full overflow-hidden">
+          <!-- 表格 -->
+          <el-table ref="clueTableRef" :data="formTable" border v-loading="allLoading.formTableLading"
+            style="width: 100%;height: 100%;">
+            <el-table-column v-for="(column, index) in tableColumns" :key="index" :prop="column.prop"
+              :label="column.label" :width="column.width">
+              <template #default="scope">
+                <template v-if="column.event === 'toDetali'">
+                  <el-button link type="primary" size="large" @click="toDetali(scope.row)">{{ scope.row.name
+                  }}</el-button>
+                </template>
+              </template>
+            </el-table-column>
+            <el-table-column :label="'操作'" :width="'200px'" fixed="right">
+              <template #default="scope">
+                <el-button link type="primary" size="large">编辑</el-button>
+                <el-button link type="primary" size="large">新建任务</el-button>
+                <el-button link type="danger" size="large">删除</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div class="flex justify-end pt-3">
+          <!-- 分页 -->
+          <el-pagination layout="total, prev, pager, next, sizes" :total="formTablePaging.total"
+            :hide-on-single-page="true" />
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { ref, reactive, onMounted, inject } from "vue";
+import { getAllListByCode, getFromValue, resetFromValue, getFirstDayOfMonth, getLastDayOfMonth, formatDate } from '@/utils/tools'
+import { post, get } from "@/utils/request";
+import { actionButtons, tableColumns, GETSYSFILED, GETPERSONNEL, GETGENERATEFOEM, MOD, GETTABLELIST, GETALLPRODUCT } from "./api";
+import { useRouter, useRoute } from "vue-router";
 
+const router = useRouter()
+
+const filterForm = reactive<FilterForm>({ // 筛选条件 Value
+  contactPerson: "",
+  customerId: "",
+  phoneNumber: '',
+  responsibleId: '',
+  createId: '',
+  email: '',
+  startTime: getFirstDayOfMonth(new Date()),
+  endTime: formatDate(new Date())
+});
+const selectData = reactive({ // 下拉数据
+  Personnel: [] as personnelInterface[],
+  Customer: [] as any[], // 客户名称
+  OrderType: [] as any[], // 订单类型
+  RemittanceStatus: [] as any[], // 回款状态
+  AllProduct: [] as any[] // 所有产品
+})
+const filterItems = ref<FilterItem[]>([]) // 渲染筛选条件
+const formTablePaging = reactive({ // 分页条件
+  currentPage: 1,
+  pageSize: 10,
+  total: 0,
+})
+const generateFormData = ref([]) // 自定义表单数据
+
+const formTable = ref([
+  { name: '订单名称', mobile: '13311112222', email: '123@qq.com', responsible: '张三', createId: '张三' }
+]) // 表格数据
+const allLoading = reactive({ // 按钮加载 Loading
+  formTableLading: false,
+})
+const dialogVisible = reactive({
+  
+})
+
+function toDetali(row: any) {
+  router.push({
+    path: `${MOD}/detail`,
+    query: { id: row.name }
+  })
+}
+
+function getTableList() {
+  const formValue = getFromValue(filterForm)
+  const formPaging = { pageIndex: formTablePaging.currentPage, pageSize: formTablePaging.pageSize }
+  allLoading.formTableLading = true
+  post(GETTABLELIST, { ...formValue, ...formPaging }).then(res => {
+    const { total, record } = res.data
+    formTable.value = record
+    formTablePaging.total = total
+  }).finally(() => {
+    allLoading.formTableLading = false
+  })
+}
+
+function resetFilterForm() {
+  let newFilterForm = resetFromValue(filterForm, { startTime: getFirstDayOfMonth(new Date()), endTime: formatDate(new Date()) })
+  Object.assign(filterForm, newFilterForm)
+  getTableList()
+}
+
+function getAllProduct() {
+  post(GETALLPRODUCT, { pageIndex: -1, pageSize: -1 }).then((res) => {
+    const { record } = res.data
+    selectData.AllProduct = record
+  })
+}
+
+async function getSystemField() {
+  const systemField = getAllListByCode([])
+  for (let i in systemField) {
+    const { data } = await get(`${GETSYSFILED}?code=${systemField[i]}`)
+    for (let key of Object.keys(selectData)) {
+      if (systemField[i] == key) {
+        Object.assign(selectData, { [key]: data })
+      }
+    }
+  }
+
+  const { data } = await post(GETPERSONNEL, {})
+  selectData.Personnel = data.map((item: any) => {
+    const { id, name, phone, jobNumber } = item
+    return {
+      id, name, phone, jobNumber
+    }
+  })
+
+  // const res = await get(GETGENERATEFOEM)
+  // generateFormData.value = JSON.parse(res.data[0].config)
+
+  setFilterItems()
+}
+
+function setFilterItems() {
+  filterItems.value = [
+    { label: '订单编号', key: 'orderCode', type: 'input' },
+    { label: '订单名称', key: 'orderName', type: 'input' },
+    { label: '客户名称', key: 'customId', type: 'select', options: selectData.Customer },
+    { label: '商机名称', key: 'email', type: 'input' },
+    { label: '订单类型', key: 'type', type: 'select', options: selectData.OrderType },
+    { label: '回款状态', key: 'remittanceStatus', type: 'select', options: selectData.RemittanceStatus },
+    { label: '负责人', key: 'responsibleId', type: 'select', options: selectData.Personnel },
+    { label: '下单时间', key: '', type: 'date' },
+  ]
+}
+
+onMounted(() => {
+  getSystemField()
+  getAllProduct()
+  getTableList()
+})
 </script>
 
 <style lang="scss" scoped></style>

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


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

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

+ 58 - 3
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SalesOrderController.java

@@ -47,16 +47,28 @@ public class SalesOrderController {
     @Resource
     private TaskService taskService;
 
+
+    /**
+     * 获取列表数据 ps:分页参数(-1,-1)获取全部数据
+     * */
     @RequestMapping("/list")
-    public HttpRespMsg list(String userId, String orderName,String orderCode, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete){
-        return salesOrderService.getList(userId,orderName,orderCode,productCode,pageIndex,pageSize,isDelete);
+    public HttpRespMsg list(String inchargerId,Integer orderType, String orderName,
+                            String orderCode,Integer customId,String businessName,
+                            Integer receivedStatus,String startDate,String endDate, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete){
+        return salesOrderService.getList(inchargerId,orderType,orderName,orderCode,customId,businessName,receivedStatus,startDate,endDate,productCode,pageIndex,pageSize,isDelete);
     }
 
+
+    /**
+     * 新增编辑订单数据
+     * */
     @RequestMapping("/addOrUpdate")
     public HttpRespMsg addOrUpdate(SalesOrder order){
         HttpRespMsg msg=new HttpRespMsg();
-        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        User user = userMapper.selectById(request.getHeader("token"));
+        Integer companyId = user.getCompanyId();
         order.setCompanyId(companyId);
+        order.setCreatorId(user.getId());
         int count;
         if(order.getId()==null){
             count = salesOrderService.count(new LambdaQueryWrapper<SalesOrder>().eq(SalesOrder::getCompanyId, companyId).eq(SalesOrder::getOrderCode, order.getOrderCode()));
@@ -85,6 +97,23 @@ public class SalesOrderController {
         return msg;
     }
 
+    /**
+     * 转移订单 负责人
+     * */
+    @RequestMapping("/transfer")
+    public HttpRespMsg transfer(Integer id,String userId){
+        HttpRespMsg msg=new HttpRespMsg();
+        SalesOrder byId = salesOrderService.getById(id);
+        byId.setInchargerId(userId);
+        if(!salesOrderService.updateById(byId)){
+            msg.setError("验证失败");
+        }
+        return msg;
+    }
+
+    /**
+     * 删除订单(假删除 isDelete标记 0-->1)
+     * */
     @RequestMapping("/delete")
     public HttpRespMsg delete(String ids){
         HttpRespMsg msg=new HttpRespMsg();
@@ -104,6 +133,10 @@ public class SalesOrderController {
         return msg;
     }
 
+
+    /**
+     * 恢复订单(假删除 isDelete标记 1-->0)
+     * */
     @RequestMapping("/recover")
     public HttpRespMsg recover(String ids){
         HttpRespMsg msg=new HttpRespMsg();
@@ -123,16 +156,34 @@ public class SalesOrderController {
         return msg;
     }
 
+    /**
+    * @Description:导入订单数据
+    * @Param: [multipartFile]
+    * @return: com.management.platform.util.HttpRespMsg
+    * @Author: yurk
+    * @Date: 2024/5/14
+    */
     @RequestMapping("/importData")
     public HttpRespMsg importData(MultipartFile multipartFile){
         return salesOrderService.importData(multipartFile);
     }
 
+    /**
+    * @Description:导出订单数据
+    * @Param: [userId, orderName, orderCode, productCode]
+    * @return: com.management.platform.util.HttpRespMsg
+    * @Author: yurk
+    * @Date: 2024/5/14
+    */
     @RequestMapping("/exportData")
     public HttpRespMsg exportData(String userId, String orderName,String orderCode, String productCode) throws Exception {
         return salesOrderService.exportData(userId,orderName,orderCode,productCode);
     }
 
+
+    /**
+     * 订单相关的产品数据
+     * */
     @RequestMapping("/productWithOrder")
     public HttpRespMsg productWithOrder(Integer id){
         HttpRespMsg msg=new HttpRespMsg();
@@ -166,6 +217,10 @@ public class SalesOrderController {
         return msg;
     }
 
+
+    /**
+     * 订单相关的任务数据
+     * */
     @RequestMapping("/taskWithOrder")
     public HttpRespMsg taskWithOrder(Integer id){
         HttpRespMsg msg=new HttpRespMsg();

+ 0 - 5
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/WxCorpInfoController.java

@@ -92,11 +92,6 @@ public class WxCorpInfoController {
         return new HttpRespMsg();
     }
 
-    @RequestMapping("/wxLeaveTest")
-    public void wxLeaveTest() throws Exception {
-        timingTask.synWxLeave();
-    }
-
     @RequestMapping("/batchTransferLicense")
     public HttpRespMsg batchTransferLicense(HttpServletRequest request,String handoverId,String takeoverId) throws Exception {
         return wxCorpInfoService.batchTransferLicense(request,handoverId,takeoverId);

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

@@ -148,6 +148,8 @@ public class BusinessOpportunity extends Model<BusinessOpportunity> {
     private String plate1;
     @TableField(exist = false)
     private String userId;
+    @TableField(exist = false)
+    private Integer productId;
 
     /**
      * 自定义字段存值

+ 3 - 1
fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/SalesOrderService.java

@@ -15,7 +15,9 @@ import org.springframework.web.multipart.MultipartFile;
  */
 public interface SalesOrderService extends IService<SalesOrder> {
 
-    HttpRespMsg getList(String userId, String orderName,String orderCode, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete);
+    HttpRespMsg getList(String inchargerId,Integer orderType, String orderName,
+                        String orderCode,Integer customId,String businessName,
+                        Integer receivedStatus,String startDate,String endDate, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete);
 
     HttpRespMsg importData(MultipartFile multipartFile);
 

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

@@ -83,7 +83,9 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
     private String path;
 
     @Override
-    public HttpRespMsg getList(String userId, String orderName,String orderCode, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete) {
+    public HttpRespMsg getList(String inchargerId,Integer orderType, String orderName,
+                               String orderCode,Integer customId,String businessName,
+                               Integer receivedStatus,String startDate,String endDate, String productCode, Integer pageIndex, Integer pageSize,Integer isDelete) {
         HttpRespMsg msg=new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
         List<Department> departments = departmentMapper.selectList(new LambdaQueryWrapper<Department>().eq(Department::getCompanyId, user.getCompanyId()));
@@ -92,6 +94,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         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"));
         LambdaQueryWrapper<SalesOrder> orderLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        List<Custom> customList = customService.list(new LambdaQueryWrapper<Custom>().eq(Custom::getCompanyId, user.getCompanyId()));
         orderLambdaQueryWrapper.eq(SalesOrder::getCompanyId,user.getCompanyId());
         if(isDelete!=null){
             orderLambdaQueryWrapper.eq(SalesOrder::getIsDelete,isDelete);
@@ -130,6 +133,30 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
                 orderLambdaQueryWrapper.in(SalesOrder::getCreatorId,userIds);
             }
         }
+        if (!StringUtils.isEmpty(inchargerId)){
+            orderLambdaQueryWrapper.eq(SalesOrder::getInchargerId,inchargerId);
+        }
+        if(orderType!=null){
+            orderLambdaQueryWrapper.eq(SalesOrder::getType,orderType);
+        }
+        if(customId!=null){
+            orderLambdaQueryWrapper.eq(SalesOrder::getCustomId,customId);
+        }
+        if(!StringUtils.isEmpty(businessName)){
+            orderLambdaQueryWrapper.inSql(SalesOrder::getBusinessOpportunityId,"select id from business_opportunity where name like '%"+businessName+"%'");
+        }
+        if(receivedStatus!=null){
+            if(receivedStatus==1){
+                //回款金额不为空 切大于0 作为已回款状态
+                orderLambdaQueryWrapper.and(wrapper->wrapper.isNotNull(SalesOrder::getReceivedPayment).gt(SalesOrder::getReceivedPayment,0));
+            }else {
+                //回款金额为空 或者等于0 作为未回款状态
+                orderLambdaQueryWrapper.and(wrapper->wrapper.isNull(SalesOrder::getReceivedPayment).or().eq(SalesOrder::getReceivedPayment,0));
+            }
+        }
+        if(!StringUtils.isEmpty(startDate)&&!StringUtils.isEmpty(endDate)){
+            orderLambdaQueryWrapper.lt(SalesOrder::getOrderStartDate,endDate).gt(SalesOrder::getOrderEndDate,startDate);
+        }
         if(!StringUtils.isEmpty(orderName)){
             orderLambdaQueryWrapper.like(SalesOrder::getOrderName,orderName);
         }
@@ -150,12 +177,20 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
         records.forEach(r->{
             Optional<User> item = userList.stream().filter(u -> u.getId().equals(r.getCreatorId())).findFirst();
             if(item.isPresent()){
-                r.setInchargerName(item.get().getName());
+                r.setCreatorName(item.get().getName());
             }
-            Optional<SysDict> type = sysDictOfOrderType.stream().filter(u -> u.getId().equals(r.getCreatorId())).findFirst();
+            Optional<User> incharger = userList.stream().filter(u -> u.getId().equals(r.getInchargerId())).findFirst();
+            if(incharger.isPresent()){
+                r.setInchargerName(incharger.get().getName());
+            }
+            Optional<SysDict> type = sysDictOfOrderType.stream().filter(s -> s.getId().equals(r.getType())).findFirst();
             if(type.isPresent()){
                 r.setTypeName(type.get().getName());
             }
+            Optional<Custom> custom = customList.stream().filter(c -> c.getId().equals(r.getCustomId())).findFirst();
+            if(custom.isPresent()){
+                r.setCustomName(custom.get().getCustomName());
+            }
         });
         Map map=new HashMap();
         map.put("record",records);
@@ -351,7 +386,7 @@ public class SalesOrderServiceImpl extends ServiceImpl<SalesOrderMapper, SalesOr
             titleList.add(item.getString("label"));
         }
         dataList.add(titleList);
-        HttpRespMsg respMsg = getList( userId, orderName,orderCode, productCode, null, null,0);
+        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) {

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

@@ -28,7 +28,7 @@
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, name, contacts_id, customer_id, product_id, amount_of_money, expected_transaction_date, stage_id, create_time, edit_time, creator_id, incharger_id, remark, is_delete, plate1, plate2, plate3, plate4, plate5
+        id, company_id, name, contacts_id, customer_id,  amount_of_money, expected_transaction_date, stage_id, create_time, edit_time, creator_id, incharger_id, remark, is_delete, plate1, plate2, plate3, plate4, plate5
     </sql>
     <select id="selectAllList" resultType="com.management.platform.entity.BusinessOpportunity">
         select

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

@@ -21,6 +21,7 @@ import okhttp3.WebSocket;
 import org.apache.ibatis.annotations.Param;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.*;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.RestTemplate;
@@ -514,6 +515,12 @@ public class ReportController {
                 multiDegrId[i] = null;
             }
         }
+        if (reportTargetDeptId == null) {
+            reportTargetDeptId = new Integer[projectId.length];
+            for (int i=0;i<reportTargetDeptId.length; i++) {
+                reportTargetDeptId[i] = -1;
+            }
+        }
         if (company.getPackageProject() == 1) {
             //检查是否设置了预算的工时预警
             ProjectBasecostSetting alarmSetting = projectBasecostSettingMapper.selectOne(new QueryWrapper<ProjectBasecostSetting>()
@@ -2075,13 +2082,13 @@ public class ReportController {
     }
 
     @RequestMapping("/getUserDailyWorkTimeReminder")
-    public HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception{
-        return reportService.getUserDailyWorkTimeReminder(request, startDate, endDate);
+    public HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,@RequestParam(defaultValue = "1") Integer viewValue) throws Exception{
+        return reportService.getUserDailyWorkTimeReminder(request, startDate, endDate,deptId,viewValue);
     }
 
     @RequestMapping("/exportUserDailyWorkTimeReminder")
-    public HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception{
-        return reportService.exportUserDailyWorkTimeReminder(request, startDate, endDate);
+    public HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,@RequestParam(defaultValue = "1") Integer viewValue) throws Exception{
+        return reportService.exportUserDailyWorkTimeReminder(request, startDate, endDate,deptId,viewValue);
     }
 
     @RequestMapping("/getNoReportUserList")
@@ -2524,10 +2531,17 @@ public class ReportController {
 
     //todo:推送工时管家工时考勤数据到SAP
     @RequestMapping("/pushProjectReportToSap")
+    @LimitRequest(count = 1)
     public HttpRespMsg pushProjectReportToSap(String pushDate,Integer reportId){
         return reportService.pushProjectReportToSap(pushDate,reportId);
     }
 
+    //todo:推送工时管家工时考勤数据到SAP
+    @RequestMapping("/cancelHasPushForSap")
+    public HttpRespMsg cancelHasPushForSap(String uuids){
+        return reportService.cancelHasPushForSap(uuids);
+    }
+
     //todo:查询已推送到SAP的工时管家工时考勤数据 并撤销推送
     @RequestMapping("/getHasPushForSap")
     public HttpRespMsg getHasPushForSap(String startDate,String endDate,String employeeID){
@@ -2638,5 +2652,10 @@ public class ReportController {
     public HttpRespMsg changeReminder(String createDate,String userId,String startDate,String endDate) throws Exception {
         return reportService.changeReminder(request,createDate,userId,startDate,endDate);
     }
+
+    @RequestMapping("/cancelReminder")
+    public HttpRespMsg cancelReminder(String createDate,String userId) throws Exception {
+        return reportService.cancelReminder(request,createDate,userId);
+    }
 }
 

+ 258 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportLogController.java

@@ -3,22 +3,38 @@ package com.management.platform.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.CompanyDingding;
-import com.management.platform.entity.ReportLog;
-import com.management.platform.entity.User;
-import com.management.platform.entity.WxCorpInfo;
-import com.management.platform.mapper.CompanyDingdingMapper;
-import com.management.platform.mapper.UserMapper;
-import com.management.platform.mapper.WxCorpInfoMapper;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
 import com.management.platform.service.ReportLogService;
 import com.management.platform.service.ReportService;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
+import com.management.platform.util.MessageUtils;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+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.hamcrest.core.Is;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.lang.reflect.Array;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -40,6 +56,16 @@ public class ReportLogController {
     WxCorpInfoMapper wxCorpInfoMapper;
     @Resource
     private CompanyDingdingMapper companyDingdingMapper;
+    @Resource
+    private HttpServletRequest request;
+    @Resource
+    private ReportMapper reportMapper;
+    @Resource
+    private ProjectMapper projectMapper;
+    @Value(value = "${upload.path}")
+    private String path;
+    @Resource
+    private ReportService reportService;
 
     @RequestMapping("/get")
     public HttpRespMsg get(String creatorId, String createDate) {
@@ -66,5 +92,230 @@ public class ReportLogController {
         msg.data = list;
         return msg;
     }
+
+    @RequestMapping("/exportReportLog")
+    public HttpRespMsg exportReportLog(String startDate,String endDate){
+        HttpRespMsg msg = new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        DateTimeFormatter df1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        List<ReportLog> list = reportLogService.list(new LambdaQueryWrapper<ReportLog>().eq(ReportLog::getCompanyId,companyId).between(ReportLog::getCreateDate, startDate,endDate));
+        list=list.stream().filter(l->l.getMsg().contains("审核通过了")).collect(Collectors.toList());
+        List<List<String>> dataList=new ArrayList<>();
+        List<String> titleList=new ArrayList<>();
+        titleList.add("编号(不可随意修改)");
+        titleList.add("姓名");
+        titleList.add("工号");
+        titleList.add("工作日期");
+        titleList.add("项目名称");
+        titleList.add("项目编号");
+        titleList.add("审核通过时间");
+        dataList.add(titleList);
+        List<String> reportIds = list.stream().map(ReportLog::getReportIds).collect(Collectors.toList());
+        List<Integer> reportIdList=new ArrayList<>();
+        reportIds.forEach(r->{
+            String[] split = r.split(",");
+            List<Integer> reportIdListSub = Arrays.asList(split).stream().map(i -> Integer.valueOf(i)).collect(Collectors.toList());
+            reportIdList.addAll(reportIdListSub);
+        });
+        reportIdList.add(-1);
+        List<Report> reportList = reportMapper.selectList(new LambdaQueryWrapper<Report>().in(Report::getId, reportIdList));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId,companyId));
+        List<Integer> projectIds= reportList.stream().map(Report::getProjectId).distinct().collect(Collectors.toList());
+        projectIds.add(-1);
+        List<Project> projectList = projectMapper.selectList(new LambdaQueryWrapper<Project>().in(Project::getId, projectIds));
+        for (ReportLog reportLog : list) {
+            List<String> item=new ArrayList<>();
+            item.add(String.valueOf(reportLog.getId()));
+            Optional<User> user = userList.stream().filter(u -> u.getId().equals(reportLog.getCreatorId())).findFirst();
+            if(user.isPresent()){
+                item.add(String.valueOf(user.get().getName()));
+                item.add(String.valueOf(user.get().getJobNumber()));
+            }else {
+                item.add("");
+                item.add("");
+            }
+            item.add(String.valueOf(df.format(reportLog.getCreateDate())));
+            String ids = reportLog.getReportIds();
+            String[] split = ids.split(",");
+            StringBuilder projectNames=new StringBuilder();
+            StringBuilder projectCodes=new StringBuilder();
+            for (int i = 0; i < split.length; i++) {
+                String reportId = split[i];
+                Optional<Report> report = reportList.stream().filter(r -> r.getId().equals(Integer.valueOf(reportId))).findFirst();
+                if(report.isPresent()){
+                    Optional<Project> first = projectList.stream().filter(p -> p.getId().equals(report.get().getProjectId())).findFirst();
+                    if(first.isPresent()){
+                        if(i==0){
+                            projectNames.append(first.get().getProjectName());
+                            projectCodes.append((first.get().getProjectCode()==null?"":first.get().getProjectCode()));
+                        }else {
+                            projectNames.append(","+first.get().getProjectName());
+                            projectCodes.append(","+(first.get().getProjectCode()==null?"":first.get().getProjectCode()));
+                        }
+                    }
+                }
+            }
+            item.add(projectNames.toString());
+            item.add(projectCodes.toString());
+            item.add(String.valueOf(df1.format(reportLog.getOperateDate())));
+            dataList.add(item);
+        }
+        String resp = ExcelUtil.exportGeneralExcelByTitleAndList("日报审核记录", dataList, path);
+        msg.setData(resp);
+        return msg;
+    }
+
+    @RequestMapping("/importReportLogChange")
+    public HttpRespMsg importReportLogChange(MultipartFile multipartFile){
+        HttpRespMsg msg=new HttpRespMsg();
+        //然后处理文件
+        String fileName = multipartFile.getOriginalFilename();
+        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        File file = new File(fileName == null ? "file" : fileName);
+        InputStream inputStream = null;
+        OutputStream outputStream = null;
+        try {
+            inputStream = multipartFile.getInputStream();
+            outputStream = new FileOutputStream(file);
+            byte[] buffer = new byte[4096];
+            int temp = 0;
+            while ((temp = inputStream.read(buffer, 0, 4096)) != -1) {
+                outputStream.write(buffer, 0, temp);
+            }
+            inputStream.close();
+            outputStream.close();
+            //然后解析表格
+            XSSFWorkbook workbook = new XSSFWorkbook(file);
+            //我们只需要第一个sheet
+            XSSFSheet sheet = workbook.getSheetAt(0);
+            //由于第一行需要指明列对应的标题
+            int rowNum = sheet.getLastRowNum();
+            List<Integer> reportLogIds=new ArrayList<>();
+            for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                XSSFCell numCell = row.getCell(0);
+                if (numCell != null) {
+                    String code = numCell.getStringCellValue().trim().replaceAll("\\u00a0", "");
+                    if ((code.equals("编号(不可随意修改)")||(code.equals("编号(不可随意修改)")) && rowIndex == 0)){
+                        //跳过第一行标题
+                        continue;
+                    }
+                }
+                if (numCell != null)numCell.setCellType(CellType.STRING);
+                Integer numCellValue = Integer.valueOf(numCell.getStringCellValue());
+                reportLogIds.add(numCellValue);
+            }
+            List<ReportLog> reportLogList = reportLogService.list(new LambdaQueryWrapper<ReportLog>().in(ReportLog::getId, reportLogIds));
+            List<String> reportIds = reportLogList.stream().map(ReportLog::getReportIds).collect(Collectors.toList());
+            List<Integer> reportIdList =new ArrayList<>();
+            reportIds.forEach(r->{
+                String[] split = r.split(",");
+                List<Integer> reportSubIds = Arrays.asList(split).stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
+                reportIdList.addAll(reportSubIds);
+            });
+            List<Report> reportList = reportMapper.selectList(new LambdaQueryWrapper<Report>().in(Report::getId, reportIdList));
+            StringBuilder sb=new StringBuilder();
+            List<Report> needUpdateReportList=new ArrayList<>();
+            List<ReportLog> needUpdateReportLogList=new ArrayList<>();
+            for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
+                XSSFRow row = sheet.getRow(rowIndex);
+                if (row == null) {
+                    continue;
+                }
+                //跳过空行
+                if (ExcelUtil.isRowEmpty(row)) {
+                    continue;
+                }
+                XSSFCell numCell = row.getCell(0);
+                XSSFCell nameCell = row.getCell(1);
+                XSSFCell jobNumCell = row.getCell(2);
+                XSSFCell createDateCell = row.getCell(3);
+                XSSFCell projectNameCell = row.getCell(4);
+                XSSFCell projectCodeCell = row.getCell(5);
+                XSSFCell auditDateCell = row.getCell(6);
+
+                if (numCell != null) {
+                    String code = numCell.getStringCellValue().trim().replaceAll("\\u00a0", "");
+                    if ((code.equals("编号(不可随意修改)")||(code.equals("编号(不可随意修改)")) && rowIndex == 0)){
+                        //跳过第一行标题
+                        continue;
+                    }
+                }
+
+                if (numCell != null)numCell.setCellType(CellType.STRING);
+                if (nameCell != null)nameCell.setCellType(CellType.STRING);
+                if (jobNumCell != null)jobNumCell.setCellType(CellType.STRING);
+                if (createDateCell != null)createDateCell.setCellType(CellType.STRING);
+                if (projectNameCell != null)projectNameCell.setCellType(CellType.STRING);
+                if (projectCodeCell != null)projectCodeCell.setCellType(CellType.STRING);
+                if (auditDateCell != null)auditDateCell.setCellType(CellType.STRING);
+
+                Integer numCellValue = Integer.valueOf(numCell.getStringCellValue());
+                String nameCellValue = nameCell.getStringCellValue();
+                String jobNumCellValue =jobNumCell.getStringCellValue();
+                String createDateCellValue = createDateCell.getStringCellValue();
+                String projectNameCellValue = projectNameCell.getStringCellValue();
+                String projectCodeCellValue = projectCodeCell.getStringCellValue();
+                String auditDateCellValue = auditDateCell.getStringCellValue();
+
+
+                Optional<ReportLog> first = reportLogList.stream().filter(r -> r.getId().equals(numCellValue)).findFirst();
+                if(first.isPresent()){
+                    ReportLog reportLog = first.get();
+                    String reportIdSplit = reportLog.getReportIds();
+                    List<Integer> ids = Arrays.asList(reportIdSplit.split(",")).stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
+                    List<Report> reports = reportList.stream().filter(r -> ids.contains(r.getId())).collect(Collectors.toList());
+                    if(auditDateCellValue!=null){
+                        LocalDateTime time = null;
+                        try {
+                             time = LocalDateTime.parse(auditDateCellValue, df);
+                        }catch (Exception e){
+                            msg.setError("第"+row+"行审核时间格式错误,请检查审核时间数据");
+                            return msg;
+                        }
+                        reportLog.setOperateDate(time);
+                        needUpdateReportLogList.add(reportLog);
+                        LocalDateTime finalTime = time;
+                        reports.forEach(r->{
+                            r.setProjectAuditTime(finalTime);
+                        });
+                        needUpdateReportList.addAll(reports);
+                    }
+                }else {
+                    if(sb.toString().length()==0){
+                        sb.append(numCell);
+                    }else {
+                        sb.append(","+numCell);
+                    }
+                }
+            }
+            if(needUpdateReportList.size()>0){
+                reportService.updateBatchById(needUpdateReportList);
+            }
+            if(needUpdateReportLogList.size()>0){
+                reportLogService.updateBatchById(needUpdateReportLogList);
+            }
+            if(sb.length()>0){
+                msg.setMsg("更新完成,其中编号["+sb.toString()+"]的填报数据不存在");
+            }else {
+                msg.setMsg("更新完成");
+            }
+        }catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (InvalidFormatException e) {
+            e.printStackTrace();
+        }
+        return msg;
+    }
 }
 

+ 6 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskCommentController.java

@@ -87,15 +87,18 @@ public class TaskCommentController {
                 if (item.getId().equals(taskComment.getUserId())){
                     if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
                         userWxId = item.getCorpwxRealUserid();
+                        String content = taskComment.getContent();
+                        String newContent = content.replace(userName, "$userName=" + userWxId + "$");
+                        taskComment.setContent(newContent);
                     }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
                         userWxId = item.getDingdingUserid();
+                        String content = taskComment.getContent();
+                        String newContent = content.replace(userName, "$userName=" + userWxId + "$");
+                        taskComment.setContent(newContent);
                     }
                     break;
                 }
             }
-            String content = taskComment.getContent();
-            String newContent = content.replace(userName, "$userName=" + userWxId + "$");
-            taskComment.setContent(newContent);
             for (TaskPersonLiable taskPersonLiable : taskPersonLiableList) {
                 Optional<User> first = users.stream().filter(us -> us.getId().equals(taskPersonLiable.getPersonLiableId())).findFirst();
                 if(first.isPresent()){

+ 13 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java

@@ -276,6 +276,8 @@ public class TaskController {
                         .map(TaskExecutor::getId).collect(Collectors.toList());
             List<TaskExecutor> hasRemoveExecutor = oldExeList.stream().filter(old -> !task.getExecutorList().stream().anyMatch(newT -> newT.getId() != null && newT.getId().equals(old.getId()))).collect(Collectors.toList());
             if(hasRemoveExecutor.size()>0){
+                //存在执行认为空的情况
+                hasRemoveExecutor=hasRemoveExecutor.stream().filter(h->h.getExecutorId()!=null).collect(Collectors.toList());
                 if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
                     taskCommentString.append(",移除了执行人"+hasRemoveExecutor.stream().map(h->{
                         if(!h.getExecutorId().equals(user.getId())){
@@ -302,6 +304,7 @@ public class TaskController {
                         .map(TaskExecutor::getExecutorId).collect(Collectors.toList());
                 List<TaskExecutor> newAddExecutor = task.getExecutorList().stream().filter(newT -> !StringUtils.isEmpty(newT.getExecutorId()) && !oldExeList.stream().anyMatch(old -> newT.getExecutorId().equals(old.getExecutorId()))).collect(Collectors.toList());
                 if(newAddExecutor.size()>0){
+                    newAddExecutor=newAddExecutor.stream().filter(n->n.getExecutorId()!=null).collect(Collectors.toList());
                     if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
                         taskCommentString.append(",新增了执行人"+newAddExecutor.stream().map(h->{
                             if(!h.getExecutorId().equals(user.getId())){
@@ -315,6 +318,10 @@ public class TaskController {
                     }
                 }
                 for (TaskExecutor oldHa : oldHas) {
+                    //执行人为空的情况跳过
+                    if(oldHa.getExecutorId()==null){
+                        continue;
+                    }
                     Optional<TaskExecutor> first = oldExeList.stream().filter(o -> o.getId().equals(oldHa.getId())).findFirst();
                     if(first.isPresent()){
                         if(!oldHa.getPlanHours().equals(first.get().getPlanHours())){
@@ -678,13 +685,13 @@ public class TaskController {
         queryWrapper.orderByDesc("id");
         List<Task> list = taskService.nameList(queryWrapper);
         Project project = projectService.getById(projectId);
-
-        //没有权限只能看到自己创建的,负责的和待分配的任务
-        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部任务");
-        if (functionList.size() == 0 && !userId.equals(project.getInchargerId())) {
-            list = list.stream().filter(t->t.getExecutorId() == null || t.getExecutorId().contains(userId) || t.getCreaterId().equals(userId)).collect(Collectors.toList());
+        if (project != null) {
+            //没有权限只能看到自己创建的,负责的和待分配的任务
+            List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部任务");
+            if (functionList.size() == 0 && !userId.equals(project.getInchargerId())) {
+                list = list.stream().filter(t->t.getExecutorId() == null || t.getExecutorId().contains(userId) || t.getCreaterId().equals(userId)).collect(Collectors.toList());
+            }
         }
-
         msg.data = list;
         return msg;
     }

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

@@ -10,9 +10,7 @@ import com.management.platform.mapper.BeisenConfigMapper;
 import com.management.platform.mapper.TimeTypeMapper;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.mapper.UserWithBeisenMapper;
-import com.management.platform.service.UserFvTimeService;
-import com.management.platform.service.UserService;
-import com.management.platform.service.UserWithBeisenService;
+import com.management.platform.service.*;
 import com.management.platform.util.BeiSenUtils;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.WorkDayCalculateUtils;
@@ -64,6 +62,10 @@ public class UserWithBeisenController {
     private TimeTypeMapper timeTypeMapper;
     @Resource
     private UserService userService;
+    @Resource
+    private HolidaySettingService holidaySettingService;
+    @Resource
+    private LeaveSheetService leaveSheetService;
 
     @RequestMapping("/getByTimeWindow")
     public HttpRespMsg getByTimeWindow(String startTime,String stopTime){
@@ -155,6 +157,8 @@ public class UserWithBeisenController {
         List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, companyId));
         List<UserWithBeisen> userWithBeisenList = userWithBeisenService.list(new LambdaQueryWrapper<UserWithBeisen>().eq(UserWithBeisen::getCompanyId, companyId));
         BeisenConfig beisenConfig = beisenConfigMapper.selectById(companyId);
+        //获取特殊节假日设置
+        List<HolidaySetting> holidaySettings = holidaySettingService.list(new LambdaQueryWrapper<HolidaySetting>().eq(HolidaySetting::getCompanyId, companyId));
         if(beisenConfig==null){
             msg.setError("北森基础数据配置未完成,请联系服务商完成配置");
             return msg;
@@ -165,6 +169,7 @@ public class UserWithBeisenController {
         List<LocalDate> workDaysListInRange = WorkDayCalculateUtils.getWorkDaysListInRange(startDate, endDate, 1);
         JSONArray allOverTimeList=new JSONArray();
         JSONArray allVacationList=new JSONArray();
+        List<LeaveSheet> leaveSheetList=new ArrayList<>();
         List<UserFvTime> userFvTimeList=new ArrayList<>();
         for (LocalDate localDate : workDaysListInRange) {
             JSONArray statisticList = BeiSenUtils.getAttendanceStatistics(df.format(localDate), df.format(localDate), beisenConfig.getAppKey(), beisenConfig.getAppSecret(), 1, 100);
@@ -193,18 +198,24 @@ public class UserWithBeisenController {
                 List<LocalTime> maxLocalTimeList = timeStream1.filter(t -> t.getIntValue("Type") == 9).map(i -> LocalDateTime.parse(i.getString("ActualTime"),df1).toLocalTime()).collect(Collectors.toList());
                 Optional<LocalTime> max = maxLocalTimeList.stream().max(LocalTime::compareTo);
                 if(first.isPresent()){
-                    boolean workDay = WorkDayCalculateUtils.isWorkDay(localDate);
+                    boolean workDay = WorkDayCalculateUtils.isWorkDay(localDate)&&!holidaySettings.stream().anyMatch(h->h.getHolidayDate().isEqual(localDate));
                     //todo:针对景昱 工作日默认以8小时工作制度加上加班时长 非工作日以加班时长为准
                     Duration between = Duration.between(min.get(), max.get());
                     Double workTime;
-                    if(between.toHours()>8){
-                        workTime=8.0;
-                    }else if(between.toHours()<0){
-                        workTime=0.0;
+                    if(timeType.getCompanyId()==5978){
+                        if(workDay){
+                            workTime=8.0;
+                        }else {
+                            workTime=0.0;
+                        }
                     }else {
-                        BigDecimal decimal = new BigDecimal(between.toMinutes());
-                        decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
-                        workTime=decimal.doubleValue();
+                        if(between.toHours()<0){
+                            workTime=0.0;
+                        }else {
+                            BigDecimal decimal = new BigDecimal(between.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            workTime=decimal.doubleValue();
+                        }
                     }
                     Stream<JSONObject> overTimeStream = allOverTimeList.stream().map(elment -> (JSONObject) elment);
                     Stream<JSONObject> vacationStream = allVacationList.stream().map(elment -> (JSONObject) elment);
@@ -216,11 +227,7 @@ public class UserWithBeisenController {
                                 &&LocalDateTime.parse(a.getString("StartDate"),df1).toLocalDate().isEqual(localDate)).collect(Collectors.toList());
                         if(overTimeList.size()>0){
                             double actualOverTimeDuration = overTimeList.stream().mapToDouble(i -> i.getDouble("OverTimeDuration")).sum();
-                            if(workDay){
-                                workTime= workTime+actualOverTimeDuration;
-                            }else {
-                                workTime= actualOverTimeDuration;
-                            }
+                            workTime= workTime+actualOverTimeDuration;
                         }
                         //处理修改
                         List<JSONObject> vacationList = vacationStream.filter(a ->{
@@ -242,13 +249,11 @@ public class UserWithBeisenController {
                             double vacationDuration = vacationList.stream().mapToDouble(i -> i.getDouble("VacationDuration")).sum();
                             BigDecimal decimal = new BigDecimal(vacationDuration);
                             decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
-                            //考勤打卡区间大于8小时 但是存在休假数据
-                            if(between.toHours()>8){
-                                if(decimal.doubleValue()>8){
-                                    workTime= workTime-8;
-                                }else {
-                                    workTime= workTime-decimal.doubleValue();
-                                }
+//                            //可能存在休假多天 只减去一天
+                            if(decimal.doubleValue()>=8){
+                                workTime= workTime-8;
+                            }else {
+                                workTime= workTime-decimal.doubleValue();
                             }
                         }
                     }
@@ -272,6 +277,58 @@ public class UserWithBeisenController {
                 msg.setError("同步验证失败");
             }
         }
+        //同步休假数据到工时管家
+        for (int i = 0; i < allVacationList.size(); i++) {
+            JSONObject jsonObject = allVacationList.getJSONObject(i);
+            Optional<UserWithBeisen> beisen = userWithBeisenList.stream().filter(b -> b.getUserId().equals(jsonObject.getString("StaffId"))).findFirst();
+            if(beisen.isPresent()){
+                Optional<User> first = userList.stream().filter(u -> u.getJobNumber().equals(beisen.get().getJobNumber())).findFirst();
+                if(first.isPresent()){
+                    LeaveSheet leaveSheet=new LeaveSheet();
+                    leaveSheet.setCompanyId(beisenConfig.getCompanyId());
+                    leaveSheet.setStatus(0);
+                    leaveSheet.setOwnerId(first.get().getId());
+                    leaveSheet.setOwnerName(first.get().getName());
+                    leaveSheet.setStartDate(LocalDateTime.parse(String.valueOf(jsonObject.get("VacationStartDateTime")),df3).toLocalDate());
+                    leaveSheet.setEndDate(LocalDateTime.parse(String.valueOf(jsonObject.get("VacationStopDateTime")),df3).toLocalDate());
+                    leaveSheet.setTimeType(1);
+                    leaveSheet.setTimeDays(jsonObject.getFloatValue("DayValueOfDuration"));
+                    leaveSheet.setTimeHours(jsonObject.getFloatValue("VacationDuration")/60);
+                    Integer leaveType;
+                    switch (jsonObject.getString("VacationType")){
+                        case "事假":leaveType=0;
+                            break;
+                        case "病假":leaveType=1;
+                            break;
+                        case "年假":leaveType=2;
+                            break;
+                        case "产假":leaveType=3;
+                            break;
+                        case "婚假":leaveType=4;
+                            break;
+                        case "丧假":leaveType=5;
+                            break;
+                        case "调休":leaveType=6;
+                            break;
+                        case "陪产假":leaveType=7;
+                            break;
+                        default:leaveType=8;
+                    }
+                    leaveSheet.setLeaveType(leaveType);
+                    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));
+                    if(one!=null){
+                        leaveSheet.setId(one.getId());
+                    }
+                    leaveSheetList.add(leaveSheet);
+                }
+            }
+        }
+        if(leaveSheetList.size()>0){
+            leaveSheetService.saveOrUpdateBatch(leaveSheetList);
+        }
         return msg;
     }
 

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

@@ -19,7 +19,7 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2022-09-13
+ * @since 2024-05-08
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -51,6 +51,12 @@ public class UserFvTime extends Model<UserFvTime> {
     @TableField("work_hours")
     private Float workHours;
 
+    /**
+     * 是否被标记排除异常范围
+     */
+    @TableField("cancel_normal")
+    private Integer cancelNormal;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -109,7 +109,7 @@ public interface ReportMapper extends BaseMapper<Report> {
     List<Map<String, Object>> getDepartmentDetailByState(@Param("departmentIds") List<Integer> departmentIds,
                                                @Param("companyId") Integer companyId);
 
-    List<Map<String, Object>> getUserDailyWorkTime(Integer companyId, String startDate, String endDate, List<Integer> deptIds, String leaderId);
+    List<Map<String, Object>> getUserDailyWorkTime(Integer companyId, String startDate, String endDate, List<Integer> deptIds, String leaderId,String userId);
 
     List<Map<String, Object>> getUserWorkingTimeByRange(Integer companyId, String startDate, String endDate);
 
@@ -215,4 +215,6 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     @Update("update report set state=2,reject_reason='提醒工时变更' where create_date=#{createDate} and creator_id=#{userId}")
     void denyReportWithUserAndCreateDate(String userId, String createDate);
+
+    List<Map<String, Object>> getUserDailyWorkTimeReminder(Integer companyId,String startDate, String endDate,@Param("list") List<Integer> deptIds,Integer deptId,String leaderId);
 }

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

@@ -154,7 +154,11 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg changeReminder(HttpServletRequest request,String createDate,String userId,String startDate,String endDate) throws Exception;
 
-    HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception;
+    HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,Integer viewValue) throws Exception;
 
-    HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception;
+    HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,Integer viewValue) throws Exception;
+
+    HttpRespMsg cancelReminder(HttpServletRequest request, String createDate, String userId);
+
+    HttpRespMsg cancelHasPushForSap(String uuids);
 }

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

@@ -500,7 +500,7 @@ public class DingDingServiceImpl implements DingDingService {
         if (oldD == null) {
             //新开通的情况,给字段默认值
             dingding.setSyncContact(1);
-            dingding.setContactNeedTranslate(1);//上架到钉钉应用市场,通讯录为加密模式,需要转译
+//            dingding.setContactNeedTranslate(1);//上架到钉钉应用市场,通讯录为加密模式,需要转译;暂时不上,待定
             //第一次,查询对方企业的accessToken
             SysConfig config = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "dingding_suite_ticket"));
             String suiteTicket = config.getParamValue();

+ 8 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java

@@ -1313,7 +1313,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                                     if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
                                         membRowData.add("$userName="+(us.getCorpwxUserid()==null?"":us.getCorpwxUserid())+"$");
                                     }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                                        membRowData.add("$userName="+npu.getName()+"$");
+                                        membRowData.add("$userName="+us.getDingdingUserid()+"$");
                                     }else {
                                         membRowData.add(npu.getName());
                                     }
@@ -1321,7 +1321,13 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                                     //增加部门
                                     Optional<Department> first = allDepartments.stream().filter(dp -> dp.getDepartmentId().equals(us.getDepartmentId())).findFirst();
                                     if (first.isPresent()) {
-                                        membRowData.add(first.get().getDepartmentName());
+                                        if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
+                                            membRowData.add("$departmentName="+(first.get().getCorpwxDeptid()==null?"":first.get().getCorpwxDeptid())+"$");
+                                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                                            membRowData.add("$departmentName="+(first.get().getDdDeptid()==null?"":first.get().getDdDeptid())+"$");
+                                        }else {
+                                            membRowData.add(first.get().getDepartmentName());
+                                        }
                                     } else {
                                         membRowData.add("");
                                     }

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

@@ -292,7 +292,7 @@ public class ProjectPercentageServiceImpl extends ServiceImpl<ProjectPercentageM
                     Optional<User> userOp;
                     if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
                         Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(username)).findFirst();
-                        userOp= allUserList.stream().filter(u ->((u.getJobNumber()!=null&&u.getJobNumber().equals(username))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getCorpwxUserid()!=null&&u.getDingdingUserid().equals(optional.get().getDingdingUserid())))))).findFirst();
+                        userOp= allUserList.stream().filter(u ->((u.getJobNumber()!=null&&u.getJobNumber().equals(username))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid())))))).findFirst();
                     }else {
                         userOp= allUserList.stream().filter(u -> u.getName().equals(username)||(u.getJobNumber()!=null&&u.getJobNumber().equals(username))).findFirst();
                     }

+ 280 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -4487,9 +4487,22 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     }
                     XSSFCell participatorCell = row.getCell(6+(projectWithDept?1:0));
                     XSSFCell inchargerCell = row.getCell(7+(projectWithDept?1:0));
+                    XSSFCell reportAuditorsCell=null;
+                    XSSFCell reportCcCell=null;
+                    int i=0;
+                    if(timeType.getReportAuditType()==0||timeType.getReportAuditType()==4||timeType.getReportAuditType()==6){
+                        i++;
+                        reportAuditorsCell = row.getCell(7+(projectWithDept?1:0)+i);
+                    }
+                    if(timeType.getReportAuditType()==7){
+                        i++;
+                        reportCcCell=row.getCell(7+(projectWithDept?1:0)+i);
+                    }
 
                     String part = "";
                     String inchargerName = "";
+                    String reportAuditors = "";
+                    String reportCc = "";
                     if (participatorCell!=null) {
                         participatorCell.setCellType(CellType.STRING);
                         part = participatorCell.getStringCellValue().trim();
@@ -4498,16 +4511,36 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                         inchargerCell.setCellType(CellType.STRING);
                         inchargerName = inchargerCell.getStringCellValue().trim();
                     }
+                    if (reportAuditorsCell!=null) {
+                        reportAuditorsCell.setCellType(CellType.STRING);
+                        reportAuditors = reportAuditorsCell.getStringCellValue().trim();
+                    }
+                    if (reportCcCell!=null) {
+                        participatorCell.setCellType(CellType.STRING);
+                        reportCc = participatorCell.getStringCellValue().trim();
+                    }
                     //兼容繁体
                     if(part.equals("参与人") || part.equals("參與人")){
                         continue;
                     }
                     String[] partSplit = part.split("\\,|\\,");
+                    String[] reportAuditorsSplit = reportAuditors.split("\\,|\\,");
+                    String[] reportCcSplit = reportCc.split("\\,|\\,");
                     for (String s : partSplit) {
                         if(!userNameList.contains(s)&&!s.equals("")){
                             userNameList.add(s);
                         }
                     }
+                    for (String s : reportAuditorsSplit) {
+                        if(!userNameList.contains(s)&&!s.equals("")){
+                            userNameList.add(s);
+                        }
+                    }
+                    for (String s : reportCcSplit) {
+                        if(!userNameList.contains(s)&&!s.equals("")){
+                            userNameList.add(s);
+                        }
+                    }
                     if(!userNameList.contains(inchargerName)&&!inchargerName.equals("")){
                         userNameList.add(inchargerName);
                     }
@@ -4914,6 +4947,109 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             }
                         }
                     }
+                    if (reportAuditorsCell != null) {
+                        String part = reportAuditorsCell.getStringCellValue().trim();
+                        if (!StringUtils.isEmpty(part)) {
+                            String[] partSplit = part.split("\\,|\\,");
+                            for (String str : partSplit) {
+//                                if(str.equals(inchargerCell.getStringCellValue())){
+//                                    continue;
+//                                }
+                                ProjectAuditor projectAuditor = new ProjectAuditor();
+                                String s1;
+                                if(str.startsWith("/")){
+                                    s1=str.substring(1,str.length());
+                                }else s1=str;
+                                String s2;
+                                if(s1.endsWith("/")){
+                                    s2=s1.substring(0,s1.length()-1);
+                                }else s2=s1;
+                                String[] split = str.split("/");
+                                Optional<User> first;
+                                Integer exception=null;
+                                if(split.length==1){
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                    }
+                                    exception=0;
+                                }else {
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                        exception=1;
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                    }
+                                }
+                                Participation p = new Participation();
+                                if (first.isPresent()) {
+                                    if (!participationList.stream().anyMatch(partOne->partOne.getUserId().equals(first.get().getId()))) {
+                                        p.setUserId(first.get().getId());
+                                        p.setProjectId(project.getId());
+                                        participationList.add(p);
+                                    }
+                                } else {
+                                    switch (exception){
+                                        case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                        case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (reportCcCell != null) {
+                        String part = reportCcCell.getStringCellValue().trim();
+                        if (!StringUtils.isEmpty(part)) {
+                            String[] partSplit = part.split("\\,|\\,");
+                            for (String str : partSplit) {
+                                ProjectCcuser projectCcuser = new ProjectCcuser();
+                                String s1;
+                                if(str.startsWith("/")){
+                                    s1=str.substring(1,str.length());
+                                }else s1=str;
+                                String s2;
+                                if(s1.endsWith("/")){
+                                    s2=s1.substring(0,s1.length()-1);
+                                }else s2=s1;
+                                String[] split = str.split("/");
+                                Optional<User> first;
+                                Integer exception=null;
+                                if(split.length==1){
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                    }
+                                    exception=0;
+                                }else {
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                        exception=1;
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                    }
+                                }
+                                Participation p = new Participation();
+                                if (first.isPresent()) {
+                                    if (!participationList.stream().anyMatch(partOne->partOne.getUserId().equals(first.get().getId()))) {
+                                        p.setUserId(first.get().getId());
+                                        p.setProjectId(project.getId());
+                                        participationList.add(p);
+                                    }
+                                } else {
+                                    switch (exception){
+                                        case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                        case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                    }
+                                }
+                            }
+                        }
+                    }
                     if (participationList.size() > 0) {
                         //批量保存
                         List<Participation> finalOldPartList = oldPartList;
@@ -4932,9 +5068,9 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                         if (!StringUtils.isEmpty(part)) {
                             String[] partSplit = part.split("\\,|\\,");
                             for (String str : partSplit) {
-                                if(str.equals(inchargerCell.getStringCellValue())){
-                                    continue;
-                                }
+//                                if(str.equals(inchargerCell.getStringCellValue())){
+//                                    continue;
+//                                }
                                 ProjectAuditor projectAuditor = new ProjectAuditor();
                                 String s1;
                                 if(str.startsWith("/")){
@@ -5137,30 +5273,65 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     }
                     HSSFCell participatorCell=null;
                     HSSFCell inchargerCell=null;
+                    HSSFCell reportAuditorsCell=null;
+                    HSSFCell reportCcCell=null;
                     int c=0;
+                    int i=0;
                     if(company.getId()==936){
                         c++;
                     }
                     if(timeType.getMainProjectState()==1){
                         participatorCell = row.getCell(5+c);
                         inchargerCell = row.getCell(6+c);
+                        if(timeType.getReportAuditType()==0||timeType.getReportAuditType()==4||timeType.getReportAuditType()==6){
+                            i++;
+                            reportAuditorsCell=row.getCell(6+i);
+                        }
+                        if(timeType.getReportAuditType()==6){
+                            i++;
+                            reportCcCell=row.getCell(6+i);
+                        }
                     }else {
                         participatorCell = row.getCell(6+c);
                         inchargerCell = row.getCell(7+c);
+                        if(timeType.getReportAuditType()==0||timeType.getReportAuditType()==4||timeType.getReportAuditType()==6){
+                            i++;
+                            reportAuditorsCell=row.getCell(7+i);
+                        }
+                        if(timeType.getReportAuditType()==6){
+                            i++;
+                            reportCcCell=row.getCell(7+i);
+                        }
                     }
                     if (participatorCell!=null)participatorCell.setCellType(CellType.STRING);
                     if (inchargerCell != null)inchargerCell.setCellType(CellType.STRING);
+                    if (reportAuditorsCell != null)reportAuditorsCell.setCellType(CellType.STRING);
+                    if (reportCcCell != null)reportCcCell.setCellType(CellType.STRING);
                     String part = participatorCell.getStringCellValue().trim();
                     String inchargerName = inchargerCell.getStringCellValue().trim();
+                    String reportAuditors = reportAuditorsCell.getStringCellValue().trim();
+                    String reportCc = reportCcCell.getStringCellValue().trim();
                     if(part.equals("参与人") || part.equals("參與人")){
                         continue;
                     }
                     String[] partSplit = part.split("\\,|\\,");
+                    String[] reportAuditorsSplit = reportAuditors.split("\\,|\\,");
+                    String[] reportCcSplit = reportCc.split("\\,|\\,");
                     for (String s : partSplit) {
                         if(!userNameList.contains(s)&&!s.equals("")){
                             userNameList.add(s);
                         }
                     }
+                    for (String s : reportAuditorsSplit) {
+                        if(!userNameList.contains(s)&&!s.equals("")){
+                            userNameList.add(s);
+                        }
+                    }
+                    for (String s : reportCcSplit) {
+                        if(!userNameList.contains(s)&&!s.equals("")){
+                            userNameList.add(s);
+                        }
+                    }
                     if(!userNameList.contains(inchargerName)&&!inchargerName.equals("")){
                         userNameList.add(inchargerName);
                     }
@@ -6170,6 +6341,109 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             }
                         }
                     }
+                    if (reportAuditorsCell != null) {
+                        String part = reportAuditorsCell.getStringCellValue().trim();
+                        if (!StringUtils.isEmpty(part)) {
+                            String[] partSplit = part.split("\\,|\\,");
+                            for (String str : partSplit) {
+//                                if(str.equals(inchargerCell.getStringCellValue())){
+//                                    continue;
+//                                }
+                                ProjectAuditor projectAuditor = new ProjectAuditor();
+                                String s1;
+                                if(str.startsWith("/")){
+                                    s1=str.substring(1,str.length());
+                                }else s1=str;
+                                String s2;
+                                if(s1.endsWith("/")){
+                                    s2=s1.substring(0,s1.length()-1);
+                                }else s2=s1;
+                                String[] split = str.split("/");
+                                Optional<User> first;
+                                Integer exception=null;
+                                if(split.length==1){
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                    }
+                                    exception=0;
+                                }else {
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                        exception=1;
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                    }
+                                }
+                                Participation p = new Participation();
+                                if (first.isPresent()) {
+                                    if (!participationList.stream().anyMatch(partOne->partOne.getUserId().equals(first.get().getId()))) {
+                                        p.setUserId(first.get().getId());
+                                        p.setProjectId(project.getId());
+                                        participationList.add(p);
+                                    }
+                                } else {
+                                    switch (exception){
+                                        case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                        case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (reportCcCell != null) {
+                        String part = reportCcCell.getStringCellValue().trim();
+                        if (!StringUtils.isEmpty(part)) {
+                            String[] partSplit = part.split("\\,|\\,");
+                            for (String str : partSplit) {
+                                ProjectCcuser projectCcuser = new ProjectCcuser();
+                                String s1;
+                                if(str.startsWith("/")){
+                                    s1=str.substring(1,str.length());
+                                }else s1=str;
+                                String s2;
+                                if(s1.endsWith("/")){
+                                    s2=s1.substring(0,s1.length()-1);
+                                }else s2=s1;
+                                String[] split = str.split("/");
+                                Optional<User> first;
+                                Integer exception=null;
+                                if(split.length==1){
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[0])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[0])||(u.getJobNumber()!=null&&u.getJobNumber().equals(split[0]))).findFirst();
+                                    }
+                                    exception=0;
+                                }else {
+                                    if((wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1)||(dingding!=null&&dingding.getContactNeedTranslate()==1)){
+                                        Optional<User> optional = targetUserList.stream().filter(tl -> tl.getName().equals(split[1])).findFirst();
+                                        first= userList.stream().filter(u ->(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))||(optional.isPresent()&&((u.getCorpwxUserid()!=null&&u.getCorpwxUserid().equals(optional.get().getCorpwxUserid()))||(u.getDingdingUserid()!=null&&u.getDingdingUserid().equals(optional.get().getCorpwxUserid()))))).findFirst();
+                                        exception=1;
+                                    }else {
+                                        first= userList.stream().filter(u -> u.getName().equals(split[1])&&(u.getJobNumber()!=null&&u.getJobNumber().equals(split[1]))).findFirst();
+                                    }
+                                }
+                                Participation p = new Participation();
+                                if (first.isPresent()) {
+                                    if (!participationList.stream().anyMatch(partOne->partOne.getUserId().equals(first.get().getId()))) {
+                                        p.setUserId(first.get().getId());
+                                        p.setProjectId(project.getId());
+                                        participationList.add(p);
+                                    }
+                                } else {
+                                    switch (exception){
+                                        case 0:throw new Exception("["+split[0]+"]在系统中不存在");
+                                        case 1:throw new Exception("["+split[1]+"]在系统中不存在");
+                                    }
+                                }
+                            }
+                        }
+                    }
                     if (participationList.size() > 0) {
                         //批量保存
                         List<Participation> finalOldPartList = oldPartList;
@@ -6188,9 +6462,9 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                         if (!StringUtils.isEmpty(part)) {
                             String[] partSplit = part.split("\\,|\\,");
                             for (String str : partSplit) {
-                                if(str.equals(inchargerCell.getStringCellValue())){
-                                    continue;
-                                }
+//                                if(str.equals(inchargerCell.getStringCellValue())){
+//                                    continue;
+//                                }
                                 String s1;
                                 if(str.startsWith("/")){
                                     s1=str.substring(1,str.length());

+ 189 - 102
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -22,6 +22,7 @@ import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
 import org.apache.commons.io.FileUtils;
+import org.apache.logging.log4j.util.PropertySource;
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
@@ -47,6 +48,7 @@ import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.text.DecimalFormat;
+import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
@@ -4231,9 +4233,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         Company company = companyMapper.selectById(companyId);
         //准备部门数据
         List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
-        List<UserFvTime> userFvTimeList = userFvTimeMapper.selectList(new LambdaQueryWrapper<UserFvTime>().between(UserFvTime::getWorkDate, startDate, endDate));
-        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, company.getId()));
-        //处理特殊节假日设置
         //特殊节假日配置
         List<HolidaySetting> holidaySettingList = holidaySettingService.list(new LambdaQueryWrapper<HolidaySetting>().eq(HolidaySetting::getCompanyId, companyId));
         //针对全员生效的日期
@@ -4258,7 +4257,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             String[] userSplit = userIdStr.split(",");
             targetUserIds = Arrays.asList(userSplit);
         }
-        List<UserCustom> userCustomList = userCustomMapper.selectList(new LambdaQueryWrapper<UserCustom>().eq(UserCustom::getCompanyId, companyId));
         DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
         //String[] weekDayCHN = {"周一","周二","周三","周四","周五","周六","周日"};
         String[] weekDayCHN = {MessageUtils.message("week.Monday"),MessageUtils.message("week.Tuesday"),MessageUtils.message("week.Wednesday"),MessageUtils.message("week.Thursday"),MessageUtils.message("week.Friday"),MessageUtils.message("week.Saturday"),MessageUtils.message("week.Sunday")};
@@ -4273,12 +4271,12 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             //检查是不是部门负责人(含主要负责人和其他负责人)
             List<Integer> allDeptIds = getAllVisibleDeptIdList(user, allDeptList);
             if (allDeptIds.size() > 0) {
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, allDeptIds, null);
-                //部门负责人看自己部门相关的
-                allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("department_id", allDeptIds).orderByDesc("department_id"));
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, allDeptIds, null,user.getId());
+                //部门负责人看自己部门相关的 以及自己的
+                allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("department_id", allDeptIds).or().eq("id",user.getId()).orderByDesc("department_id"));
             } else {
                 //看自己的所负责的项目相关人员的
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId());
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId(),user.getId());
                 //项目相关的人员列表
                 List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("incharger_id", user.getId()));
                 List<Project> collect = projectList.stream().collect(Collectors.toList());
@@ -4286,12 +4284,12 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("project_id", collect));
                     List<String> collect1 = participationList.stream().map(Participation::getUserId).collect(Collectors.toList());
                     if (collect1.size() > 0) {
-                        allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("id", collect1).orderByDesc("department_id"));
+                        allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("id", collect1).or().eq("id",user.getId()).orderByDesc("department_id"));
                     }
                 }
             }
         } else {
-            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null);
+            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null,user.getId());
             //管理员看全公司所有人
             allRangeUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).orderByDesc("department_id"));
         }
@@ -4317,36 +4315,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             String date = new SimpleDateFormat("yyyy-MM-dd").format((Date)data.get("createDate"));
             map.put("workingTime", data.get("workingTime"));
             map.put("createDate", date);
-            //针对景昱 5978 校验填报工时是否超过考勤
-            if(user.getCompanyId().equals(5978)){
-                map.put("exceedCardTime",0);
-                Optional<UserFvTime> first = userFvTimeList.stream().filter(u -> u.getWorkDate().isEqual(LocalDate.parse(date, df)) && u.getUserId().equals(id)).findFirst();
-                if(first.isPresent()){
-                    if(first.get().getWorkHours()!=null){
-                        map.put("cardTime",first.get().getWorkHours());
-                        Optional<User> optional = userList.stream().filter(u -> u.getId().equals(id)).findFirst();
-                        User targetUser = optional.get();
-                        List<String> customList = userCustomList.stream().map(UserCustom::getName).collect(Collectors.toList());
-                        if(customList.size()>0&&customList.contains("是否有加班费")){
-                            int index = customList.indexOf("是否有加班费");
-                            targetUser.getPlate1();
-                            String getter="getPlate"+(index+1);
-                            Class<User> aClass = User.class;
-                            Method method = aClass.getMethod(getter);
-                            String invoke = (String) method.invoke(targetUser);
-                            if(invoke==null||invoke.equals("有加班费")){
-                                if(!(Double.valueOf(String.valueOf(data.get("workingTime"))).equals(Double.valueOf(first.get().getWorkHours())))){
-                                    map.put("exceedCardTime",1);
-                                }
-                            }else{
-                                if((Double.valueOf(String.valueOf(data.get("workingTime")))<first.get().getWorkHours())){
-                                    map.put("exceedCardTime",1);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
             if (id.equals(lastUserId)) {
                 //同一个用户的数据
                 lastUserData.worktimeList.add(map);
@@ -6106,12 +6074,12 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             List<Integer> allVisibleDeptIdList = getAllVisibleDeptIdList(user, null);
             //检查是不是部门负责人
             if (allVisibleDeptIdList.size() > 0) {
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, allVisibleDeptIdList, null);
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, allVisibleDeptIdList, null,user.getId());
                 //部门负责人看自己部门相关的
                 allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("department_id", allVisibleDeptIdList).eq("report_status",0));
             } else {
                 //看自己的所负责的项目相关人员的
-                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId());
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId(),user.getId());
                 //项目相关的人员列表
                 List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("incharger_id", user.getId()));
                 List<Project> collect = projectList.stream().collect(Collectors.toList());
@@ -6124,7 +6092,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
             }
         } else {
-            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null);
+            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null,user.getId());
             //管理员看全公司所有人
             allRangeUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).eq("report_status",0));
         }
@@ -6184,6 +6152,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     if (!workDay){
                         continue;
                     }
+                    //去掉非工作日
+                    boolean workDay1 = WorkDayCalculateUtils.isWorkDay(date);
+                    if(!workDay1){
+                        continue;
+                    }
                     //去掉设置了分摊比例的人员
                     if (setPercentUserIdList.contains(curUser.getId())) {
                         continue;
@@ -9051,36 +9024,33 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
         WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
         LocalDate now=LocalDate.now();
-        HttpRespMsg userDailyWorkTime = getUserDailyWorkTime(request,startDate,endDate);
+        HttpRespMsg userDailyWorkTime = getUserDailyWorkTimeReminder(request,startDate,endDate,null,0);
         Map<String, Object> map = (Map<String, Object>) userDailyWorkTime.getData();
-        List<UserMonthWork> userDailyWorkTimeData = (List<UserMonthWork>) map.get("list");
+        List<Map<String, Object>> mapList = (List<Map<String, Object>>) map.get("list");
         if(!StringUtils.isEmpty(userId)){
-            userDailyWorkTimeData=userDailyWorkTimeData.stream().filter(u->u.userId.equals(userId)).collect(Collectors.toList());
+            mapList=mapList.stream().filter(u->String.valueOf(u.get("userId")).equals(userId)).collect(Collectors.toList());
         }
         List<Information> informationList=new ArrayList<>();
-        userDailyWorkTimeData.forEach(u-> {
-            List<Map<String, Object>> worktimeList = u.worktimeList;
-            List<Map<String, Object>> exceedCardTimeList = worktimeList.stream().filter(w -> Integer.valueOf(String.valueOf(w.get("exceedCardTime"))).equals(1)).collect(Collectors.toList());
-            if(exceedCardTimeList.size()>0){
-                if(!StringUtils.isEmpty(createDate)){
-                    exceedCardTimeList=exceedCardTimeList.stream().filter(e->String.valueOf(e.get("createDate")).equals(createDate)).collect(Collectors.toList());
-                }
-                exceedCardTimeList.forEach(e->{
-                    Information information=new Information();
-                    information.setUserId(u.userId);
-                    information.setTime(LocalDateTime.now());
-                    information.setContent(String.valueOf(e.get("createDate")));
-                    information.setType(0);
-                    information.setMsg("您在"+String.valueOf(e.get("createDate"))+"的日报考勤填报异常,请完成填报变更");
-                    informationList.add(information);
-                    reportMapper.denyReportWithUserAndCreateDate(u.userId,String.valueOf(e.get("createDate")));
-                    //发送企业微信消息
-                    if(wxCorpInfo!=null&&u.corpwxUserId!=null){
-                        wxCorpInfoService.sendWXCorpMsg(wxCorpInfo, u.corpwxUserId, "您在"+String.valueOf(e.get("createDate"))+"的日报考勤填报异常,请完成填报变更", null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_DENY);
-                    }
-                });
-            }
-        });
+        List<Map<String, Object>> exceedCardTimeList = mapList.stream().filter(w -> Integer.valueOf(String.valueOf(w.get("exceedCardTime"))).equals(1)).collect(Collectors.toList());
+        if(exceedCardTimeList.size()>0){
+            if(!StringUtils.isEmpty(createDate)){
+                exceedCardTimeList=exceedCardTimeList.stream().filter(e->String.valueOf(e.get("createDate")).equals(createDate)).collect(Collectors.toList());
+            }
+            exceedCardTimeList.forEach(e->{
+                Information information=new Information();
+                information.setUserId(String.valueOf(e.get("userId")));
+                information.setTime(LocalDateTime.now());
+                information.setContent(String.valueOf(e.get("createDate")));
+                information.setType(0);
+                information.setMsg("您在"+String.valueOf(e.get("createDate"))+"的日报考勤填报异常,请完成填报变更");
+                informationList.add(information);
+                reportMapper.denyReportWithUserAndCreateDate(String.valueOf(e.get("userId")),String.valueOf(e.get("createDate")));
+                //发送企业微信消息
+                if(wxCorpInfo!=null&&e.get("corpwxUserId")!=null){
+                    wxCorpInfoService.sendWXCorpMsg(wxCorpInfo,String.valueOf(e.get("corpwxUserId")), "您在"+String.valueOf(e.get("createDate"))+"的日报考勤填报异常,请完成填报变更", null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_DENY);
+                }
+            });
+        }
         if(informationList.size()>0){
             informationService.saveBatch(informationList);
         }
@@ -9089,51 +9059,146 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception {
+    public HttpRespMsg getUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,Integer viewValue) throws Exception {
         HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
         DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
-        HttpRespMsg userDailyWorkTime = getUserDailyWorkTime(request, startDate, endDate);
         List<UserFvTime> userFvTimeList = userFvTimeMapper.selectList(new LambdaQueryWrapper<UserFvTime>().between(UserFvTime::getWorkDate, startDate, endDate));
-        Map<String, Object> objectMap = (Map<String, Object>) userDailyWorkTime.getData();
-        List<UserMonthWork > mapList = (List<UserMonthWork >) objectMap.get("list");
+        List<Department> departmentList = departmentMapper.selectList(new LambdaQueryWrapper<Department>().eq(Department::getCompanyId, user.getCompanyId()));
+        List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, user.getCompanyId()));
+        List<UserCustom> userCustomList = userCustomMapper.selectList(new LambdaQueryWrapper<UserCustom>().eq(UserCustom::getCompanyId, user.getCompanyId()));
+        List<User> allRangeUserList = new ArrayList<>();
+        List<Map<String, Object>> list = null;
+        //分角色权限:管理员看全部的,部门负责人看自己部门的,个人只能看自己的。
+        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全公司工时");
+        if (functionList.size() == 0) {
+            List<Department> allDeptList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", user.getCompanyId()));
+            //检查是不是部门负责人(含主要负责人和其他负责人)
+            List<Integer> allDeptIds = getAllVisibleDeptIdList(user, allDeptList);
+            if (allDeptIds.size() > 0) {
+                list = reportMapper.getUserDailyWorkTimeReminder(user.getCompanyId(), startDate, endDate, allDeptIds, deptId,null);
+                //部门负责人看自己部门相关的
+                allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("department_id", allDeptIds).eq(deptId!=null,"department_id",deptId).orderByDesc("department_id"));
+            } else {
+                //看自己的所负责的项目相关人员的
+                list = reportMapper.getUserDailyWorkTimeReminder(user.getCompanyId(), startDate, endDate, null,deptId, user.getId());
+                //项目相关的人员列表
+                List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("incharger_id", user.getId()));
+                List<Project> collect = projectList.stream().collect(Collectors.toList());
+                if (collect.size() > 0) {
+                    List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("project_id", collect));
+                    List<String> collect1 = participationList.stream().map(Participation::getUserId).collect(Collectors.toList());
+                    if (collect1.size() > 0) {
+                        allRangeUserList = userMapper.selectList(new QueryWrapper<User>().in("id", collect1).eq(deptId!=null,"department_id",deptId).orderByDesc("department_id"));
+                    }
+                }
+            }
+        } else {
+            list = reportMapper.getUserDailyWorkTimeReminder(user.getCompanyId(), startDate, endDate, null,deptId, null);
+            //管理员看全公司所有人
+            allRangeUserList = userMapper.selectList(new QueryWrapper<User>().eq(deptId!=null,"department_id",deptId).eq("company_id", user.getCompanyId()).orderByDesc("department_id"));
+        }
+        List<User> needRangeUserList=new ArrayList<>();
+        for (User u : allRangeUserList) {
+            if(u.getInactiveDate()==null||(u.getInactiveDate()!=null&&u.getInactiveDate().isAfter(LocalDate.parse(startDate))||u.getIsActive()==1)){
+                needRangeUserList.add(u);
+            }
+        }
         List<LocalDate> days = getDays(LocalDate.parse(startDate, df), LocalDate.parse(endDate, df));
-        List<Map<String, Object>> result=new ArrayList<>();
-        mapList.forEach(m->{
-            List<Map<String, Object>> worktimeList = (List<Map<String, Object>>) m.worktimeList;
-            for (LocalDate date : days) {
-                boolean match = worktimeList.stream().anyMatch(www -> String.valueOf(www.get("createDate")).equals(date.format(df)));
+        for (User item : needRangeUserList) {
+            for (LocalDate day : days) {
+                boolean workDay = WorkDayCalculateUtils.isWorkDay(day);
+                //去掉非工作日
+                if(!workDay){
+                    continue;
+                }
+                boolean match = list.stream().anyMatch(l -> l.get("createDate").equals(df.format(day)) && l.get("userId").equals(item.getId()));
                 if(!match){
-                    Map<String,Object> ww=new HashMap<>();
-                    ww.put("createDate",date.format(df));
-                    ww.put("exceedCardTime",0);
-                    ww.put("workingTime",0);
-                    worktimeList.add(ww);
-                }
-            }
-            if(worktimeList.size()>0){
-                worktimeList.forEach(w->{
-                    Optional<UserFvTime> first = userFvTimeList.stream().filter(u -> u.getWorkDate().isEqual(LocalDate.parse(String.valueOf(w.get("createDate")),df)) && u.getUserId().equals(m.userId)).findFirst();
-                    if(first.isPresent()){
-                        w.put("cardTime",first.get().getWorkHours());
-                    }
-                    w.put("name",m.name);
-                    w.put("corpwxDeptId",m.corpwxDeptId);
-                    w.put("corpwxUserId",m.corpwxUserId);
-                    w.put("departmentName",m.departmentName);
-                    w.put("userId",m.userId);
-                });
+                    Map<String,Object> map=new HashMap<>();
+                    map.put("userId",item.getId());
+                    map.put("corpwxUserId",item.getCorpwxUserid());
+                    map.put("createDate",day.format(df));
+                    Optional<Department> first = departmentList.stream().filter(d -> d.getDepartmentId().equals(item.getDepartmentId())).findFirst();
+                    map.put("departmentName",first.get().getDepartmentName());
+                    Optional<UserFvTime> time = userFvTimeList.stream().filter(u -> u.getWorkDate().isEqual(day) && u.getUserId().equals(item.getId())).findFirst();
+                    if(time.isPresent()){
+                        map.put("cardTime",time.get().getWorkHours());
+                        map.put("cancelNormal",time.get().getCancelNormal());
+                    }else {
+                        map.put("cardTime",0);
+                    }
+                    map.put("corpwxDeptId",first.get().getCorpwxDeptid());
+                    map.put("workingTime",0);
+                    map.put("name",item.getName());
+                    list.add(map);
+                }
             }
-            result.addAll(worktimeList);
-        });
+        }
+        // 进行排序 list:就是要排序的集合
+        Collections.sort(list, new Comparator<Map<String, Object>>() {
+            @Override
+            public int compare(Map<String, Object> o1, Map<String, Object> o2) {
+                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+                String userId1 = (String) o1.get("userId");
+                String userId2 = (String) o2.get("userId");
+//                    return date2.compareTo(date1);   //倒序
+                return userId1.compareTo(userId2);   // 正序
+            }
+        }.thenComparing(new Comparator<Map<String, Object>>() {
+            @Override
+            public int compare(Map<String, Object> o1, Map<String, Object> o2) {
+                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+                try {
+                    Date date1 = format.parse((String) o1.get("createDate"));
+                    Date date2 = format.parse((String) o2.get("createDate"));
+//                    return date2.compareTo(date1);   //倒序
+                    return date1.compareTo(date2);   // 正序
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+                return 0;
+            }
+        }));
+        for (Map<String, Object> map : list) {
+            //针对景昱 5978 校验填报工时是否超过考勤
+            if(user.getCompanyId().equals(5978)){
+                map.put("exceedCardTime",0);
+                Optional<User> optional = userList.stream().filter(u -> u.getId().equals(String.valueOf(map.get("userId")))).findFirst();
+                User targetUser = optional.get();
+                List<String> customList = userCustomList.stream().map(UserCustom::getName).collect(Collectors.toList());
+                if(customList.size()>0&&customList.contains("是否有加班费")){
+                    int index = customList.indexOf("是否有加班费");
+                    targetUser.getPlate1();
+                    String getter="getPlate"+(index+1);
+                    Class<User> aClass = User.class;
+                    Method method = aClass.getMethod(getter);
+                    String invoke = (String) method.invoke(targetUser);
+                    if(invoke==null||invoke.equals("有加班费")){
+                        if((!(Double.valueOf(String.valueOf(map.get("workingTime"))).equals(Double.valueOf(String.valueOf(map.get("cardTime"))))))&&(map.containsKey("cancelNormal")&&(Integer)map.get("cancelNormal")!=1)){
+                            map.put("exceedCardTime",1);
+                        }
+                        map.put("invokeValue","有加班费");
+                    }else{
+                        if(((Double.valueOf(String.valueOf(map.get("workingTime")))<Double.valueOf(String.valueOf(map.get("cardTime")))))&&(map.containsKey("cancelNormal")&&(Integer)map.get("cancelNormal")!=1)){
+                            map.put("exceedCardTime",1);
+                        }
+                        map.put("invokeValue","无加班费");
+                    }
+                }
+            }
+        }
+        if(viewValue==1){
+            list=list.stream().filter(l->Integer.valueOf(String.valueOf(l.get("exceedCardTime")))==1).collect(Collectors.toList());
+        }
         Map<String,Object> map=new HashMap<>();
-        map.put("list",result);
+        map.put("list",list);
         msg.setData(map);
         return msg;
     }
 
     @Override
-    public HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate) throws Exception {
-        HttpRespMsg timeReminder = getUserDailyWorkTimeReminder(request, startDate, endDate);
+    public HttpRespMsg exportUserDailyWorkTimeReminder(HttpServletRequest request, String startDate, String endDate,Integer deptId,Integer viewValue) throws Exception {
+        HttpRespMsg timeReminder = getUserDailyWorkTimeReminder(request, startDate, endDate,deptId,viewValue);
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
         WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
         CompanyDingding dingding = companyDingdingService.getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
@@ -9169,4 +9234,26 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         String fileName="工时异常表"+System.currentTimeMillis();
         return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,dingding,fileName,dataList,path);
     }
+
+    @Override
+    public HttpRespMsg cancelReminder(HttpServletRequest request, String createDate, String userId) {
+        HttpRespMsg msg=new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        LambdaQueryWrapper<UserFvTime> wrapper = new LambdaQueryWrapper<UserFvTime>().eq(UserFvTime::getCompanyId, companyId).eq(UserFvTime::getWorkDate, LocalDate.parse(createDate, df)).eq(UserFvTime::getUserId, userId);
+        UserFvTime userFvTime=new UserFvTime();
+        userFvTime.setCancelNormal(1);
+        if(userFvTimeMapper.update(userFvTime,wrapper)<=0){
+            msg.setError("操作失败");
+        }
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg cancelHasPushForSap(String uuids) {
+        HttpRespMsg msg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        cancelReportPushSap(uuids,user);
+        return msg;
+    }
 }

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

@@ -1070,7 +1070,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         try {
             LocalDateTime startDateTime = LocalDateTime.now();
             LocalDateTime endDateTime = LocalDateTime.now();
-            startDateTime = startDateTime.minusDays(5);
+            startDateTime = startDateTime.minusDays(7);
             endDateTime = endDateTime.minusDays(1);
 
             startDateTime = startDateTime.withHour(0).withMinute(0).withSecond(0).withNano(0);

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

@@ -178,6 +178,8 @@ public class TimingTask {
     private UserWithBeisenService userWithBeisenService;
     @Resource
     private BeisenConfigMapper beisenConfigMapper;
+    @Resource
+    private HolidaySettingService holidaySettingService;
 
 
     private static final List<Integer> VALID_TOKEN_CHARS = new ArrayList<>();
@@ -2106,16 +2108,18 @@ public class TimingTask {
         List<User> userList = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getCompanyId, 5978));
         List<UserWithBeisen> userWithBeisenList = userWithBeisenService.list(new LambdaQueryWrapper<UserWithBeisen>().eq(UserWithBeisen::getCompanyId, 5978));
         BeisenConfig beisenConfig = beisenConfigMapper.selectById(5978);
+        //获取特殊节假日设置
+        List<HolidaySetting> holidaySettings = holidaySettingService.list(new LambdaQueryWrapper<HolidaySetting>().eq(HolidaySetting::getCompanyId, 5978));
         if(beisenConfig==null){
             return;
         }
-
         //todo 获取到指定日期的加班数据
         List<LocalDate> workDaysListInRange = WorkDayCalculateUtils.getWorkDaysListInRange(startDate, endDate, 1);
         JSONArray allOverTimeList=new JSONArray();
         JSONArray attendanceStatistics=new JSONArray();
         JSONArray allVacationList=new JSONArray();
         List<UserFvTime> userFvTimeList=new ArrayList<>();
+        List<LeaveSheet> leaveSheetList=new ArrayList<>();
         for (LocalDate localDate : workDaysListInRange) {
             JSONArray overTimeList = BeiSenUtils.getOverTimeList(df.format(localDate), beisenConfig.getAppKey(), beisenConfig.getAppSecret(), 1, 100);
             //todo 获取到指定日期的考勤数据
@@ -2145,18 +2149,24 @@ public class TimingTask {
                 Optional<LocalTime> max = maxLocalTimeList.stream().max(LocalTime::compareTo);
                 //获取最晚下班时间
                 if(first.isPresent()){
-                    boolean workDay = WorkDayCalculateUtils.isWorkDay(localDate);
+                    boolean workDay = WorkDayCalculateUtils.isWorkDay(localDate)&&!holidaySettings.stream().anyMatch(h->h.getHolidayDate().isEqual(localDate));
                     //todo:针对景昱 工作日默认以8小时工作制度加上加班时长 非工作日以加班时长为准
                     Duration between = Duration.between(min.get(), max.get());
                     Double workTime;
-                    if(between.toHours()>8){
-                        workTime=8.0;
-                    }else if(between.toHours()<0){
-                        workTime=0.0;
+                    if(beisenConfig.getCompanyId()==5978){
+                        if(workDay){
+                            workTime=8.0;
+                        }else {
+                            workTime=0.0;
+                        }
                     }else {
-                        BigDecimal decimal = new BigDecimal(between.toMinutes());
-                        decimal=decimal.divide(new BigDecimal(60),1, RoundingMode.HALF_UP);
-                        workTime=decimal.doubleValue();
+                        if(between.toHours()<0){
+                            workTime=0.0;
+                        }else {
+                            BigDecimal decimal = new BigDecimal(between.toMinutes());
+                            decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
+                            workTime=decimal.doubleValue();
+                        }
                     }
                     Stream<JSONObject> overTimeStream = allOverTimeList.stream().map(elment -> (JSONObject) elment);
                     Stream<JSONObject> vacationStream = allVacationList.stream().map(elment -> (JSONObject) elment);
@@ -2167,11 +2177,7 @@ public class TimingTask {
                                 &&LocalDateTime.parse(a.getString("StartDate"),df1).toLocalDate().isEqual(localDate)).collect(Collectors.toList());
                         if(overTimeList.size()>0){
                             double actualOverTimeDuration = overTimeList.stream().mapToDouble(i -> i.getDouble("OverTimeDuration")).sum();
-                            if(workDay){
-                                workTime= workTime+actualOverTimeDuration;
-                            }else {
-                                workTime= actualOverTimeDuration;
-                            }
+                            workTime= workTime+actualOverTimeDuration;
                         }
                         List<JSONObject> vacationList = vacationStream.filter(a ->{
                             LocalDate vacationStartDate = LocalDateTime.parse(a.getString("VacationStartDateTime"), df3).toLocalDate();
@@ -2192,13 +2198,11 @@ public class TimingTask {
                             double vacationDuration = vacationList.stream().mapToDouble(i -> i.getDouble("VacationDuration")).sum();
                             BigDecimal decimal = new BigDecimal(vacationDuration);
                             decimal=decimal.divide(new BigDecimal(60),1,RoundingMode.HALF_UP);
-                            //考勤打卡区间大于8小时 但是存在休假数据
-                            if(between.toHours()>8){
-                                if(decimal.doubleValue()>8){
-                                    workTime= workTime-8;
-                                }else {
-                                    workTime= workTime-decimal.doubleValue();
-                                }
+//                            //可能存在休假多天 只减去一天
+                            if(decimal.doubleValue()>=8){
+                                workTime= workTime-8;
+                            }else {
+                                workTime= workTime-decimal.doubleValue();
                             }
                         }
                     }
@@ -2220,6 +2224,58 @@ public class TimingTask {
         if(userFvTimeList.size()>0){
             userFvTimeService.saveOrUpdateBatch(userFvTimeList);
         }
+        //同步休假数据到工时管家
+        for (int i = 0; i < allVacationList.size(); i++) {
+            JSONObject jsonObject = allVacationList.getJSONObject(i);
+            Optional<UserWithBeisen> beisen = userWithBeisenList.stream().filter(b -> b.getUserId().equals(jsonObject.getString("StaffId"))).findFirst();
+            if(beisen.isPresent()){
+                Optional<User> first = userList.stream().filter(u -> u.getJobNumber().equals(beisen.get().getJobNumber())).findFirst();
+                if(first.isPresent()){
+                    LeaveSheet leaveSheet=new LeaveSheet();
+                    leaveSheet.setCompanyId(beisenConfig.getCompanyId());
+                    leaveSheet.setStatus(0);
+                    leaveSheet.setOwnerId(first.get().getId());
+                    leaveSheet.setOwnerName(first.get().getName());
+                    leaveSheet.setStartDate(LocalDateTime.parse(String.valueOf(jsonObject.get("VacationStartDateTime")),df3).toLocalDate());
+                    leaveSheet.setEndDate(LocalDateTime.parse(String.valueOf(jsonObject.get("VacationStopDateTime")),df3).toLocalDate());
+                    leaveSheet.setTimeType(1);
+                    leaveSheet.setTimeDays(jsonObject.getFloatValue("DayValueOfDuration"));
+                    leaveSheet.setTimeHours(jsonObject.getFloatValue("VacationDuration")/60);
+                    Integer leaveType;
+                    switch (jsonObject.getString("VacationType")){
+                        case "事假":leaveType=0;
+                            break;
+                        case "病假":leaveType=1;
+                            break;
+                        case "年假":leaveType=2;
+                            break;
+                        case "产假":leaveType=3;
+                            break;
+                        case "婚假":leaveType=4;
+                            break;
+                        case "丧假":leaveType=5;
+                            break;
+                        case "调休":leaveType=6;
+                            break;
+                        case "陪产假":leaveType=7;
+                            break;
+                        default:leaveType=8;
+                    }
+                    leaveSheet.setLeaveType(leaveType);
+                    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));
+                    if(one!=null){
+                        leaveSheet.setId(one.getId());
+                    }
+                    leaveSheetList.add(leaveSheet);
+                }
+            }
+        }
+        if(leaveSheetList.size()>0){
+            leaveSheetService.saveOrUpdateBatch(leaveSheetList);
+        }
     }
 
 

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java

@@ -117,6 +117,8 @@ public class ExcelUtil {
             cellStyle.setBorderLeft(BorderStyle.THIN);//左边框
             cellStyle.setBorderTop(BorderStyle.THIN);//上边框
             cellStyle.setBorderRight(BorderStyle.THIN);//右边框
+            DataFormat dataFormat = workBook.createDataFormat();
+            cellStyle.setDataFormat(dataFormat.getFormat("@"));
 
             if(list.size() > 0) {
                 //标题(如果需要在EXCEL内容最上面加标题,请打开下面的注释,修改start)

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

@@ -1734,7 +1734,7 @@
         ,report.create_date BETWEEN user.induction_date AND #{endDate}
         ,report.create_date BETWEEN #{startDate} AND #{endDate} )
         and
-        IF(user.inactive_date AND user.`is_active`=0 is not null AND user.inactive_date &lt; #{endDate}
+        IF(user.inactive_date is not null AND user.`is_active`=0 AND user.inactive_date &lt; #{endDate}
         ,report.create_date BETWEEN #{startDate} AND user.inactive_date
         ,report.create_date BETWEEN #{startDate} AND #{endDate} )
         )

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

@@ -886,9 +886,10 @@
         WHERE (report.state = 0 or report.state = 1)
         and user.company_id = #{companyId} and (user.is_active=1 or(user.is_active=0 and user.inactive_date>=#{startDate}))
         <if test="deptIds != null">
-            AND user.department_id in <foreach collection="deptIds" separator="," index="index" item="item" close=")" open="(">
-            #{item}
-        </foreach>
+            AND ((user.department_id in
+            <foreach collection="deptIds" separator="," index="index" item="item" close=")" open="(">
+                #{item}
+            </foreach>) or (user.id = #{userId}))
         </if>
         <if test="startDate != null">
             AND report.create_date &gt;= #{startDate}
@@ -897,7 +898,8 @@
             AND report.create_date &lt;= #{endDate}
         </if>
         <if test="leaderId != null">
-            AND user.id in (select user_id from participation where project_id in (select id from project where incharger_id = #{leaderId}))
+            AND ((user.id in (select user_id from participation where project_id in (select id from project where incharger_id = #{leaderId})) )
+            or (user.id = #{userId}))
         </if>
         GROUP BY user.id, report.create_date;
     </select>
@@ -1245,4 +1247,31 @@
         </if>
         group by p.id
     </select>
+
+    <select id="getUserDailyWorkTimeReminder" resultType="java.util.Map">
+        SELECT date_format(r.create_date,'%Y-%m-%d') AS createDate,u.name AS name,u.id as userId,u.corpwx_userid as corpwxUserId,d.corpwx_deptid as corpwxDeptId,d.department_name AS departmentName,
+        IFNULL(SUM(r.working_time),0) AS  workingTime,IFNULL(ufv.work_hours,0) AS cardTime,ufv.cancel_normal as cancelNormal
+        FROM report  r
+        LEFT JOIN user u ON r.creator_id=u.id
+        LEFT JOIN department d ON d.department_id=u.department_id
+        LEFT JOIN user_fv_time ufv ON ufv.user_id=u.id AND ufv.work_date=r.create_date
+        WHERE r.company_id=#{companyId}
+        <if test="startDate!=null and startDate!='' and endDate!=null and endDate!=''">
+            AND r.create_date BETWEEN #{startDate} AND #{endDate}
+        </if>
+        <if test="deptId!=null">
+            AND d.department_id=#{deptId}
+        </if>
+        <if test="list != null and list.size()>0">
+            AND d.department_id in
+           <foreach collection="list" open="(" close=")" separator="," item="item">
+               #{item}
+           </foreach>
+        </if>
+        <if test="leaderId != null">
+            AND user.id in (select user_id from participation where project_id in (select id from project where incharger_id = #{leaderId}))
+        </if>
+        GROUP BY r.create_date,r.creator_id
+        order by u.id,r.create_date
+    </select>
 </mapper>

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

@@ -11,11 +11,12 @@
         <result column="start_time" property="startTime" />
         <result column="end_time" property="endTime" />
         <result column="work_hours" property="workHours" />
+        <result column="cancel_normal" property="cancelNormal" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, work_date, user_id, company_id, start_time, end_time, work_hours
+        id, work_date, user_id, company_id, start_time, end_time, work_hours, cancel_normal
     </sql>
 
 </mapper>

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

@@ -163,6 +163,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 return httpRespMsg;
             }
         }
+        if (plan.getStatus() == 2) {
+            httpRespMsg.setError("该计划已经停止,无法报工");
+            return httpRespMsg;
+        }
 
         //检查当天该员工的该工序是否已经有报工
         Report todayReport = reportMapper.selectOne(new QueryWrapper<Report>().eq("creator_id", token).eq("user_procedure_team_id", report.getUserProcedureTeamId()).eq("create_date", targetDate));

+ 25 - 20
fhKeeper/formulahousekeeper/plugIn/form-design-master/src/generate/GenerateFormItem.vue

@@ -188,29 +188,33 @@
     </template>
 
     <template v-if="element.type === 'time'">
-      <el-time-picker
-        v-model="data"
-        :placeholder="element.options.placeholder"
-        :readonly="element.options.readonly"
-        :editable="element.options.editable"
-        :clearable="element.options.clearable"
-        :format="element.options.format"
-        :disabled="disabled || element.options.disabled"
-        :style="{ width: element.options.width }"
-      />
+      <el-config-provider :locale="zhCn">
+        <el-time-picker
+          v-model="data"
+          :placeholder="element.options.placeholder"
+          :readonly="element.options.readonly"
+          :editable="element.options.editable"
+          :clearable="element.options.clearable"
+          :format="element.options.format"
+          :disabled="disabled || element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </el-config-provider>
     </template>
 
     <template v-if="element.type === 'date'">
-      <el-date-picker
-        v-model="data"
-        :placeholder="element.options.placeholder"
-        :readonly="element.options.readonly"
-        :editable="element.options.editable"
-        :clearable="element.options.clearable"
-        :format="element.options.format"
-        :disabled="disabled || element.options.disabled"
-        :style="{ width: element.options.width }"
-      />
+      <el-config-provider :locale="zhCn">
+        <el-date-picker
+          v-model="data"
+          :placeholder="element.options.placeholder"
+          :readonly="element.options.readonly"
+          :editable="element.options.editable"
+          :clearable="element.options.clearable"
+          :format="element.options.format"
+          :disabled="disabled || element.options.disabled"
+          :style="{ width: element.options.width }"
+        />
+      </el-config-provider>
     </template>
 
     <template v-if="element.type === 'rate'">
@@ -332,6 +336,7 @@
 
 <script lang="ts" setup>
 import type { WidgetForm } from '@/config'
+import zhCn from "element-plus/es/locale/lang/zh-cn";
 
 const props = defineProps<{
   config: WidgetForm['config']

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.css


Разница между файлами не показана из-за своего большого размера
+ 239 - 25
fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.es.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
fhKeeper/formulahousekeeper/plugIn/form-design-master/update/dist/index.es.js.map


+ 1 - 1
fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/views/workView/fillReport.vue

@@ -34,7 +34,7 @@
           </van-cell>
           <van-cell title="当日完成件数">
             <template>
-              <van-stepper v-model="reportForm.finishNum" step="0.1" :min="0" :max="reportForm.num" @change="onFinishNumChange"/>
+              <van-stepper v-model="reportForm.finishNum" step="0.1" :min="0" :max="reportForm.num" :decimal-length="2" @change="onFinishNumChange"/>
             </template>
           </van-cell>
           <van-cell title="完成全部工作" >

+ 6 - 6
fhKeeper/formulahousekeeper/timesheet/src/components/cascader.vue

@@ -232,11 +232,11 @@ export default {
         }
         if(this.subject) {
             this.options = JSON.parse(JSON.stringify(this.subject))
-            console.log(this.options, '我收到的数据')
+            // console.log(this.options, '我收到的数据')
         }
         if(this.subjectId) {
             this.optionsOId = JSON.parse(JSON.stringify(this.subjectId))
-            console.log(this.options, '过来的数据')
+            // console.log(this.options, '过来的数据')
             this.traverseArr(this.options, this.optionsOId)
             for(let i in this.options) {
                 if(this.options[i].id == this.optionsOId || this.options[i].auditorId == this.optionsOId) {
@@ -248,7 +248,7 @@ export default {
             }
         }
         this.dailyListIndex = this.idx
-        console.log(this.selectWidth, this.selectHeight)
+        // console.log(this.selectWidth, this.selectHeight)
         // this.moveIon = JSON.parse(JSON.stringify(this.clearable))
     },
     methods: {
@@ -262,7 +262,7 @@ export default {
             }
             if(arr) {
                 for(var i in arr) {
-                    console.log(arr[i].value, idd, id)
+                    // console.log(arr[i].value, idd, id)
                     if(arr[i].label != this.$t('lable.department') && arr[i].label != this.$t('designatedpersonnel')) {
                         if(arr[i].value == id) {
                             console.log('将要付给的值', arr[i])
@@ -295,7 +295,7 @@ export default {
                     that.show = !that.show
                     that.classDiv = false
                     that.move = false
-                }, 100)
+                }, 150)
             }
         },
         liMouseOver(index, item) {
@@ -348,7 +348,7 @@ export default {
             this.liClist(item)
         },
         moveIonDiv() {
-            console.log(this.selectName)
+            // console.log(this.selectName)
             if(this.clearable) {
                 if(this.selectName != this.$t('defaultText.pleaseChoose')) {
                     this.moveIon = true

+ 1 - 5
fhKeeper/formulahousekeeper/timesheet/src/components/cascaderOption.vue

@@ -126,11 +126,7 @@ export default {
         }
     },
     liClick(item) {
-        console.log('我是子组件')
-        if(!item.children) {
-            this.$emit("cascaderOptionClick", item);
-        }
-        if(this.radios) {
+        if(!item.children || this.radios) {
             this.$emit("cascaderOptionClick", item);
         }
     },

+ 24 - 3
fhKeeper/formulahousekeeper/timesheet/src/components/select.vue

@@ -33,8 +33,10 @@
             <i v-if="moveIon" class="el-icon-circle-close iostu" @click.stop="clearDelete"></i>
         </div>
     </div>
+    <div ref="selectRefs"></div>
     <transition name="el-zoom-in-top">
-      <div v-show="show" style="position: relative;z-index: 999;"> 
+      <!-- <div v-show="show" style="position: relative;z-index: 999;">  -->
+      <div v-show="show" class="selectBox" :style="`width:${selecsWths}px;top:${selectTop}px;left:${selectLeft}px;`"> 
         <!-- 搜索框 -->
         <div class="searchBox" v-if="filterable" :style="`top: ${searchBoxTop}px`">
             <el-input placeholder="请输入名称搜索" size="mini" v-model="searchTex" style="width: 100%" @input="searchLick()" @focus="selectCli()"></el-input>
@@ -184,6 +186,9 @@ export default {
             time: null,//防抖
             fistArrList: [], // 第一次进来的人员数组
             fistArrListOne: [], // 第一次进来的人员初始数组
+            selectTop: 0,
+            selectLeft: 0,
+            selecsWths: 0,
         };
     },
     computed: {},
@@ -203,13 +208,13 @@ export default {
         // 日报点的索引, 真对填写的日报
         idx: {
             handler(newValue, oldValue) {
-                console.log(newValue, oldValue)
+                // console.log(newValue, oldValue)
                 this.dailyListIndex = newValue
             },
         },
         subjectId: {
             handler(newValue, oldValue) {
-                console.log(newValue, oldValue)
+                // console.log(newValue, oldValue)
                 this.optionsOId = newValue
                 this.multiSelectList = []
                 if(!this.multiSelect) {
@@ -358,6 +363,18 @@ export default {
                     this.$set(this, 'options', JSON.parse(JSON.stringify(this.optionsCopy)))
                 }
             }
+
+            if(this.show) {
+                this.setSelectlocation()
+            }
+        },
+        setSelectlocation() {
+            let witdhs = this.$refs.selectRefs.offsetWidth
+            let tops = this.$refs.selectRefs.getBoundingClientRect().top
+            let lefts = this.$refs.selectRefs.getBoundingClientRect().left
+            this.selecsWths = witdhs
+            this.selectTop = tops + 4
+            this.selectLeft = lefts
         },
         selectClihide() {
             if(this.classDiv) {
@@ -553,6 +570,10 @@ export default {
 </script>
 
 <style scoped lang="scss">
+    .selectBox {
+        position: fixed;
+        z-index: 999;
+    }
     .selectDandu {
         font-size: 16px;
     }

Разница между файлами не показана из-за своего большого размера
+ 130 - 39
fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue


+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/components/translationOpenData.vue

@@ -75,7 +75,7 @@ export default {
     },
     methods: {
         dealWith() {
-            console.log(this.user)
+            // console.log(this.user)
             const { userNameNeedTranslate, dingdingUserid } = this.user
             if (userNameNeedTranslate) {
                 this.corporateWeChat = true

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/src/components/translationOpenDataText.vue

@@ -8,6 +8,7 @@
             <dt-open-data :open-type='dingdingOpenType[type]' :open-id='openIdValue'></dt-open-data>
         </span>
         <span v-else>{{ openIdValue }}</span>
+        <!-- <span>{{ openIdValue }}</span> -->
     </div>
 </template>
 

+ 61 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue

@@ -263,6 +263,18 @@
                 <el-button type="primary" @click="editPerfectJobNumber('perfectForm')">确 定</el-button>
             </span>
         </el-dialog>
+
+        <!-- 完善名称弹窗 -->
+        <el-dialog title="完善姓名" :visible.sync="perfectName" width="500px" :show-close="false">
+            <el-form :model="perfectFormName" :rules="rules" ref="perfectFormName" label-width="80px" class="demo-ruleForm">
+                <el-form-item label="姓名" prop="userName">
+                    <el-input v-model.trim="perfectFormName.userName"></el-input>
+                </el-form-item>
+            </el-form>
+            <span slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="editPerfectName('perfectFormName')">确 定</el-button>
+            </span>
+        </el-dialog>
     </el-row>
 </template>
 
@@ -279,6 +291,9 @@
                 perfectForm:{
                     jobNumber: '',
                 },
+                perfectFormName: {
+                    userName: '',
+                },
                 rules: {
                     name: [
                         { required: true, message: '请输入公司名称', trigger: 'blur' },
@@ -286,6 +301,9 @@
                     ],
                     jobNumber: [
                         { required: true, message: '请输入工号', trigger: 'blur' }
+                    ],
+                    userName: [
+                        { required: true, message: '请输入姓名', trigger: 'blur' }
                     ]
                 },
                 tourFlg: false,
@@ -402,6 +420,7 @@
 
                 
                 perfectJobNumber: false, // 完善工号弹窗
+                perfectName: false, // 完成名称弹窗
                 jobNumberCheckCompanyId: [936], // 定制需求,需要完善工号的公司id
             };
         },
@@ -863,6 +882,43 @@
                     }
                 });
             },
+            // 完善姓名
+            editPerfectName(perfectForm) {
+                this.$refs[perfectForm].validate((valid) => {
+                    if (valid) {
+                        this.http.post("/user/userRename", {
+                            newName: this.perfectFormName.userName,
+                        },
+                        res => {
+                            if (res.code == "ok") {
+                                this.perfectName = false
+                                let nerUser = {
+                                    ...this.user,
+                                    name: this.perfectFormName.userName
+                                }
+                                sessionStorage.setItem('user', JSON.stringify(nerUser));
+                                this.$message({
+                                    message: '操作成功',
+                                    type: "success"
+                                });
+                            } else {
+                                this.$message({
+                                    message: res.msg,
+                                    type: "error"
+                                });
+                            }
+                        },
+                        error => {
+                            this.$message({
+                                message: error,
+                                type: "error"
+                            });
+                        });
+                    } else {
+                        return false;
+                    }
+                });
+            },
 
             setDDOpenData() {
                 const ddUrl = new URL(window.location.href);
@@ -932,10 +988,14 @@
             }
 
             // 检查是否有工号
-            const { jobNumber, companyId } = this.user
+            const { jobNumber, companyId, userNameNeedTranslate, name, dingdingUserid } = this.user
             if(this.jobNumberCheckCompanyId.includes(companyId) && !jobNumber) {
                 this.perfectJobNumber = true
             }
+            // 检查是否需要完成姓名
+            if(userNameNeedTranslate == 0 && (name == dingdingUserid)) {
+                this.perfectName = true
+            }
             if(this.user.dingdingUserid) {
                 this.setDDOpenData()
             }

+ 3 - 3
fhKeeper/formulahousekeeper/timesheet/src/views/project/finance.vue

@@ -189,9 +189,9 @@
             <el-table-column prop="name" :label="$t('lable.name')" sortable width="150" fixed="left">
                 <template slot-scope="scope">
                     <div>
-                        <span v-if="user.userNameNeedTranslate == '1'"><TranslationOpenDataText type='userName' :openid='scope.row.name'></TranslationOpenDataText></span>
-                        <span v-if="user.userNameNeedTranslate != '1'">{{scope.row.name}}</span>
-                        <!-- {{scope.row.name}} -->
+                        <!-- <span v-if="user.userNameNeedTranslate == '1'"><TranslationOpenDataText type='userName' :openid='scope.row.name'></TranslationOpenDataText></span>
+                        <span v-if="user.userNameNeedTranslate != '1'">{{scope.row.name}}</span> -->
+                        {{scope.row.name}}
                     </div>
                 </template>
             </el-table-column>

+ 24 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue

@@ -231,7 +231,11 @@
                      </div>
                 </template>
             </el-table-column>
-            <el-table-column prop="departmentName" :label="$t('subordinatedepartments')" sortable="custom" width="200" v-if="user.timeType.projectWithDept"></el-table-column>
+            <el-table-column prop="departmentName" :label="$t('subordinatedepartments')" sortable="custom" width="200" v-if="user.timeType.projectWithDept">
+                <template slot-scope="scope">
+                    <TranslationOpenDataText type='departmentName' :openid='scope.row.departmentName'></TranslationOpenDataText>
+                </template>
+            </el-table-column>
             <el-table-column prop="projectMainName" :label="$t('zhu-xiang-mu')" sortable="custom" min-width="250" v-if="user.timeType.mainProjectState == '1'">
             </el-table-column>
             <el-table-column prop="inchargerName" :label="$t('projectmanager')" sortable="custom" min-width="150">
@@ -505,7 +509,9 @@
                     <el-form-item :label="$t('subordinatedepartments')" :prop="user.companyId == 936 ? 'deptId' : false" :class="title == $t('newproject') && user.companyId == 936 ? 'wpgCssClass' : ''" v-if="user.timeType.projectWithDept">
                         <el-cascader v-model="addForm.deptId" :options="departmentList" :placeholder="$t('defaultText.pleaseChoose')" :disabled="canOnlyModParticipator"
                             :props="{ checkStrictly: true, expandTrigger: 'hover' }" clearable filterable @change="cascaderChange" style="width: 100%"
+                            v-if="user.userNameNeedTranslate != 1">
                         ></el-cascader>
+                        <vueCascader :size="'medium'" :widthStr="'430'" :filterable="true" :clearable="true" :subject="departmentList" :subjectId="addForm.deptId" :radios="true" :distinction="'20'" :disabled="canOnlyModParticipator" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" ></vueCascader>
                     </el-form-item>
 
                     <!-- 供应商 -->
@@ -1777,10 +1783,12 @@ a {
     import projectgantt from "./project_gantt.vue"
     // 自定义select组件
     import selectCat from "@/components/select.vue"
+    import vueCascader from "@/components/cascader.vue"
     export default {
         components:{
             projectgantt,
-            selectCat
+            selectCat,
+            vueCascader,
         },
         data() {
             return {
@@ -5488,7 +5496,12 @@ a {
                         // }
                         if(this.user.timeType.projectWithDept) {
                             if(this.addForm.deptId != null && this.addForm.deptId != '') {
-                                formData.append("deptId", this.addForm.deptId[this.addForm.deptId.length - 1])
+                                // formData.append("deptId", this.addForm.deptId[this.addForm.deptId.length - 1])
+                                if(Array.isArray(this.addForm.deptId)) {
+                                    formData.append("deptId", this.addForm.deptId[this.addForm.deptId.length - 1])
+                                } else {
+                                    formData.append("deptId", this.addForm.deptId)
+                                }
                             }
                         }
                         formData.append("buId", this.addForm.bu ? this.addForm.bu : '');
@@ -5517,7 +5530,10 @@ a {
                          }
                         // formData.append("associateDegreeNames", listName)
                         //console.log("addform",this.addForm);
-                        //return
+                        // for (const key of formData.keys()) {
+                        //     console.log(key + ': ' + formData.get(key));
+                        // }
+                        // return
                         this.http.uploadFile(this.port.project.add,formData,
                         res => {
                             this.addLoading = false;
@@ -5735,11 +5751,15 @@ a {
                 }
             },
             vueCasader(obj) {
+                console.log(obj, '<-==== 接收的数据')
                 if(obj.distinction == '32') {
                     let arr = []
                     arr.push(obj.id)
                     this.addForm.bu = arr.join(",")
+                } else if(obj.distinction == '20') {
+                    this.addForm.deptId = obj.id
                 }
+                console.log(this.addForm)
             },
             changSubStatus(subProject) {
                 this.http.post('/sub-project/updateStatus',{ 

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/team/index.vue

@@ -533,7 +533,7 @@
                 </el-option>
             </el-select>
 
-            <selectCat v-if="user.userNameNeedTranslate == 1" :size="'small'" :subject="users" :widthStr="350" :subjectId="moveReportUserId" :distinction="'22'" :clearable="true" @selectCal="selectCal"></selectCat>
+            <selectCat v-if="user.userNameNeedTranslate == 1" :filterable="true" :size="'small'" :subject="users" :widthStr="350" :subjectId="moveReportUserId" :distinction="'22'" :clearable="true" @selectCal="selectCal"></selectCat>
 
           </div>
         </div>

+ 163 - 15
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -141,6 +141,7 @@
                                     <el-link type="primary" v-if="user.manageDeptId != 0" style="margin-right:10px;" :underline="false" @click="showExportTimeDialog">{{$t('textLink.exportingTimeStatistics')}}</el-link>
                                     <el-link type="primary" v-if="user.timeType.pushReportData == 1 && permissions.reportPush" :underline="false" @click="pushWorkTime">推送工时</el-link>
                                     <el-link type="primary" v-if="user.timeType.pushReportData == 1 && user.companyId==3092 && permissions.reportPush" :underline="false" @click="pushWorkTimeLogDig=true,getPushWorkLogData()">工时推送日志</el-link>
+                                    <el-link type="primary" v-if="user.roleName == '超级管理员' && user.companyId==839" :underline="false" @click="reportLogCheckDialog=true">日报审核修改</el-link>
                                     <!-- <el-button v-if="user.timeType.pushReportData == 1 && permissions.reportPush" style="margin-left:10px;" icon="iconfont firerock-icontuisong" size="mini" @click="pushWorkTime"></el-button> -->
 
                                 </span>
@@ -216,7 +217,7 @@
                                                         ]</span> 
                                                     <span style="margin-left:15px;color:#DAA520;" v-else-if="item2.state == -1">[ {{$t('other.importWaitingForReview')}} ]</span>
                                                     <span style="margin-left:15px;color:#32CD32;" v-else-if="item2.state == 1">[ {{$t('state.alreadyPassed')}} ]
-                                                        <span style="color:#c7e944" v-if="item2.reportAutoApprove == 1">{{$t('other.automaticReview')}}</span>
+                                                        <span style="color:#c7e944" v-if="item2.reportAutoApprove == 1 && user.companyId != '3511'">{{$t('other.automaticReview')}}</span>
                                                     </span>
                                                     
                                                     <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 2">[ {{$t('state.rejected')}} ] {{$t('other.reason')}}:{{item2.rejectReason}}</span>
@@ -1318,6 +1319,33 @@
             </div>
         </el-dialog>
 
+        <!-- 日报审核修改 -->
+        <el-dialog
+        title="日报审核修改"
+        :visible.sync="reportLogCheckDialog"
+        width="30%"
+        :before-close="handleClose">
+        <el-form ref="form3" :model="exportReportLogParam" >
+            <el-form-item prop="projectId" :label="$t('time.dateRange')">
+                    <el-date-picker
+                        v-model="exportReportLogParam.dateRange" :editable="false" 
+                        format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
+                        :range-separator="$t('other.to')"
+                        type="daterange" 
+                        :start-placeholder="$t('time.startDate')"
+                        :end-placeholder="$t('time.endDate')"
+                    ></el-date-picker>
+                </el-form-item>
+        </el-form>
+        <el-link type="primary" @click="exportReportLog">导出日报审核记录数据</el-link>
+        <br>
+        <el-upload ref="upload"  action="#" :limit="1" :http-request="importReportLog" :show-file-list="false">
+        <el-link type="primary" @click="importReportLog">导入日报审核记录修改数据</el-link></el-upload>
+        <span slot="footer" class="dialog-footer">
+            <el-button  type="primary" @click="reportLogCheckDialog = false">关闭</el-button>
+        </span>
+        </el-dialog>
+
         
         <!-- 按部门选择人员 -->
         <el-dialog :title="$t('defaultText.selectthepersonwhneedstofillinthereport')"  v-if="chooseParticipVisible" :visible.sync="chooseParticipVisible" :close-on-click-modal="false" customClass="customWidth" width="500px">
@@ -1652,10 +1680,15 @@
               <el-button size="small" @click="getThisWeek(2)">{{$t('time.nextWeek')}}</el-button>
             </div>
             <div>
-                <el-input style="float:left;width:22%" v-if="user.userNameNeedTranslate != '1'" v-model="searchKeyword" class="input-with-select" :placeholder="$t('defaultText.pleaseEnterNametoSearch')" clearable="true" size="small">
-                    <el-button slot="append" @click="searchScreen(0)" icon="el-icon-search"></el-button>
-                </el-input>
-                <selectCat v-if="user.userNameNeedTranslate == '1'" :filterable="true"  :searchBoxTop="'1'" :size="'small'" :subject="usersList" :subjectId="usersListId" :distinction="'12'" :clearable="true" @selectCal="selectCal"></selectCat>
+                <div style="margin-top:10px;">
+                    <el-input style="float:left;width:18%" v-if="user.userNameNeedTranslate != '1'" v-model="searchKeyword" class="input-with-select" :placeholder="$t('defaultText.pleaseEnterNametoSearch')" clearable="true" size="small">
+                        <el-button slot="append" @click="searchScreen(0)" icon="el-icon-search"></el-button>
+                    </el-input>
+                    <selectCat v-if="user.userNameNeedTranslate == '1'" :filterable="true"  :searchBoxTop="'1'" :size="'small'" :subject="usersList" :subjectId="usersListId" :distinction="'12'" :clearable="true" @selectCal="selectCal"></selectCat>
+                    <el-cascader v-if="user.userNameNeedTranslate != 1" :size="'small'" v-model="deptIdForReminder" placeholder="请选择部门" :options="departmentList" :props="{ checkStrictly: true, value: 'id' }" clearable style="width: 200px;" @change="showMonthWorkTimeReminder()"></el-cascader>
+                    <vueCascader :size="'small'" :widthStr="'200'" :clearable="true" :subject="departmentList" :radios="true" :distinction="'1'" @vueCasader="vueCasader" v-if="user.userNameNeedTranslate == 1" :selectNameChuan="$t('other.allDepartments')" @change="showMonthWorkTimeReminder()"></vueCascader>
+                    <el-checkbox v-model="isReminder" @change="showMonthWorkTimeReminder()">是否异常</el-checkbox>
+                </div>
             </div>
             <div style="float: right; vertical-align: middle;height:32px">
               <el-link
@@ -1682,19 +1715,24 @@
             v-loading="tbload"
             :lazy="true"
           >
-            <el-table-column width="180" type="index" fixed="left" :label="'日期'">
+            <el-table-column width="120" type="index" fixed="left" :label="'日期'">
               <template slot-scope="scope">
                 {{ scope.row.createDate}}
               </template>
             </el-table-column>
 
-            <el-table-column width="160" prop="name" fixed="left" :label="$t('lable.name')">
+            <el-table-column width="120" prop="name" fixed="left" :label="$t('lable.name')">
                 <template slot-scope="scope">
                     <span v-if="user.userNameNeedTranslate == 1"><TranslationOpenDataText type='userName' :openid='scope.row.name'></TranslationOpenDataText></span>
                     <span v-if="user.userNameNeedTranslate != 1">{{scope.row.name}}</span>
                         <!-- {{scope.row.name}} -->
                 </template>
             </el-table-column>
+            <el-table-column width="100" prop="invokeValue" fixed="left" :label="'费用类型'">
+                <template slot-scope="scope">
+                        {{scope.row.invokeValue}}
+                </template>
+            </el-table-column>
             <el-table-column prop="departmentName" width="170" fixed="left" :label="$t('lable.department')">
                 <template slot-scope="scope">
                     <span v-if="user.userNameNeedTranslate == 1"><TranslationOpenDataText type='departmentName' :openid='scope.row.departmentName'></TranslationOpenDataText></span>
@@ -1723,6 +1761,7 @@
             <el-table-column width="160" type="index" fixed="left" :label="'催办'">
               <template slot-scope="scope">
                 <el-link v-if="scope.row.exceedCardTime==1" type="primary" @click="changeReminder(scope.row)">{{'变更提醒'}}</el-link>
+                <el-link v-if="scope.row.exceedCardTime==1" type="primary" @click="cancelReminder(scope.row)">{{'取消异常'}}</el-link>
               </template>
             </el-table-column>
 
@@ -2207,6 +2246,7 @@
                 statusStyle:["waiting", "filledReportStyle", "RejectStyle", "waitSubmitStyle"],
                 fillStatusList: [],
                 exportParam:{projectId: null, dateRange:[], departmentId: null},
+                exportReportLogParam:{dateRange:[]},
                 exportDialog:false,
                 timeFields:['timeType', 'workingTime', 'startTime', 'progress'],
                 subProjectList:[],
@@ -2403,6 +2443,7 @@
                 weekIndex: 1,
                 weekParentData: {},
                 deptIdForNoReport:[],
+                deptIdForReminder:[],
 
                 nameAearch: '', // 企业微信姓名搜索
                 deptMembDataBackups: [], // 企业微信备份代填日报的树形结构
@@ -2418,7 +2459,9 @@
                 pushWorkTimeLogDig:false,
                 pushWorkTimeLogData:[],
 
-                userReportDeptList: []
+                userReportDeptList: [],
+                isReminder:true,
+                reportLogCheckDialog:false,
             };
         },
         watch: {
@@ -2461,6 +2504,7 @@
             this.today = t;
             var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
             this.exportParam.dateRange = [startStr,t];
+            this.exportReportLogParam.dateRange = [startStr,t];
             this.getAllDate(1);
             this.getReportList();
             this.getProjectList();
@@ -4004,6 +4048,8 @@
         parameter={
             startDate: this.WorktimeDatepickValue[0],
             endDate: this.WorktimeDatepickValue[1],
+            deptId:this.deptIdForReminder.length>0?this.deptIdForReminder[this.deptIdForReminder.length-1]:null,
+            viewValue:this.isReminder?1:0
         }
       this.http.post(
         "/report/getUserDailyWorkTimeReminder",
@@ -5697,7 +5743,12 @@
                 res => {
                     this.exportingData = false;
                     if (res.code == "ok") {
-                        location.href = res.data;
+                        var filePath = res.data;
+                        const a = document.createElement('a'); // 创建a标签
+                        a.setAttribute('download', this.$t('projectexport') + '.xlsx');// download属性
+                        a.setAttribute('href', filePath);// href链接
+                        a.click(); //自执行点击事件
+                        a.remove();
                         this.exportDialog = false;
                     } else {
                         this.$message({
@@ -5715,6 +5766,75 @@
                 });
             },
 
+             //导出日报审核记录
+             exportReportLog() {
+                this.exportingData = true;
+                var param = {};
+                if (this.exportReportLogParam.dateRange != null) {
+                    param = {startDate:this.exportReportLogParam.dateRange[0], endDate: this.exportReportLogParam.dateRange[1], exportType: this.exportType};
+                }
+                this.http.post('/report-log/exportReportLog', param,
+                res => {
+                    this.exportingData = false;
+                    if (res.code == "ok") {
+                        var filePath = res.data;
+                        const a = document.createElement('a'); // 创建a标签
+                        a.setAttribute('download','日报审核记录.xlsx');// download属性
+                        a.setAttribute('href', filePath);// href链接
+                        a.click(); //自执行点击事件
+                        a.remove();
+                        this.exportDialog = false;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.exportingData = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            importReportLog(item){
+                //首先判断文件类型
+                let str = item.file.name.split(".");
+                let format = str[str.length - 1];
+                if (format != "xls" && format != "xlsx") {
+                    this.$message({
+                        message: this.$t('other.PleaseselecttheXLSorXLSXfile'),
+                        type: "error"
+                    });
+                } else {
+                    let formData = new FormData();
+                    formData.append("multipartFile", item.file);
+                    this.http.uploadFile('/report-log/importReportLogChange', formData,
+                    res => {
+                        this.$refs.upload.clearFiles();
+                        if (res.code == "ok") {
+                            this.$message({
+                            message: res.msg,
+                            type: "success"
+                        });
+                        } else {
+                            this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                        }
+                    },
+                    error => {
+                        this.$refs.upload.clearFiles();
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+                }
+            },
             //获取项目列表
             getProjectList() {
                 this.listLoading = true;
@@ -8111,7 +8231,7 @@
                             }
 
                             if(this.user.timeType.userWithMultiDept == 1) {
-                                formData.append('reportTargetDeptId', this.workForm.domains[i].reportTargetDeptId||0);
+                                formData.append('reportTargetDeptId', this.workForm.domains[i].reportTargetDeptId||-1);
                             }
                         }
                         this.submitingReport = true;
@@ -8369,12 +8489,13 @@
             //景昱变更提醒
             changeReminder(item){
                 console.log("============",item)
-                let parameter={}
+                let parameter={
+                    startDate: this.WorktimeDatepickValue[0],
+                    endDate: this.WorktimeDatepickValue[1],
+                }
                 if(item){
-                    parameter={
-                        createDate:item.createDate,
-                        userId:item.userId,
-                    }
+                    parameter.createDate=item.createDate,
+                    parameter.userId=item.userId
                 }
                 this.http.post('/report/changeReminder',parameter,res => {
                     if(res.code == 'ok'){
@@ -8395,6 +8516,33 @@
                     })
                 })
             },
+            //景昱取消异常
+            cancelReminder(item){
+                let parameter={}
+                if(item){
+                    parameter.createDate=item.createDate,
+                    parameter.userId=item.userId
+                }
+                this.http.post('/report/cancelReminder',parameter,res => {
+                    if(res.code == 'ok'){
+                        this.$message({
+                            message: res.msg,
+                            type: 'success'
+                        })
+                    }else{
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.$message({
+                        message: err,
+                        type: 'error'
+                    })
+                })
+                this.showMonthWorkTimeReminder()
+            },
             // 重庆物奇私人定制的按周填报弹窗关闭事件
             fillWeekCustomClne() {
                 this.fillWeekDialogVisiCustom = false

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

@@ -71,7 +71,7 @@
                 <!-- <van-icon v-if="index>0&&canEdit" class="form_del" name="delete" @click="delPro(index)" /> -->
                 <van-cell-group :title="(user.companyId == 781 ? '任务' : '项目') + (index + 1)">
                     <van-field readonly name="userReportDeptName" v-if="user.timeType.userWithMultiDept == 1 && userReportDeptList.length > 0"
-                        :value="item.userReportDeptName" :label="'填报部门'" placeholder="请选择部门" @click="selectDeptPopup(index, item)">
+                        :value="item.userReportDeptName" :label="'填报部门'" placeholder="请选择部门" :disabled="!item.canEdit" @click="selectDeptPopup(index, item)">
                         <template #input>
                             <TranslationOpenDataText type='departmentName' :openid='item.userReportDeptName'></TranslationOpenDataText>
                         </template>
@@ -79,11 +79,11 @@
 
                     <!-- <div>请选择投入项目</div> -->
                     <van-field readonly name="projectId" clickable :value="item.projectName"
-                        :label="user.companyId == 781 ? '工作任务' : '投入项目'"
+                        :label="user.companyId == 781 ? '工作任务' : '投入项目'" :disabled="!item.canEdit"
                         :placeholder="user.companyId == 781 ? '请选择工作任务' : '请选择项目'" @click="clickPicker(index, item)"
                         :rules="[{ required: true, message: user.companyId == 781 ? '请选择任务' : '请选择项目' }]" />
                     <!--昱众不显示子项目-->
-                    <van-field readonly name="subProjectId"
+                    <van-field readonly name="subProjectId" :disabled="!item.canEdit"
                         v-if="user.companyId != yuzhongCompId && item.subProjectList != null && item.subProjectList.length > 0 && user.timeType.mainProjectState != 1"
                         clickable :value="item.subProjectName" label="子项目" placeholder="请选择子项目"
                         @click="clickPickSubProject(index, item)" />
@@ -92,7 +92,7 @@
                             @cancel="item.showPickerSubProject = false; $forceUpdate();" />
                     </van-popup>
                     <van-field readonly name="extraField1" v-if="user.companyId == yuzhongCompId" clickable
-                        :value="item.extraField1Name" label="角色选择" placeholder="请选择担任的角色"
+                        :value="item.extraField1Name" label="角色选择" placeholder="请选择担任的角色" :disabled="!item.canEdit"
                         @click="clickPickProjectRole(index, item)" />
                     <van-popup v-model="item.showPickerRole" position="bottom">
                         <van-picker show-toolbar :columns="roleList" value-key="label" @confirm="choseRole"
@@ -100,7 +100,7 @@
                     </van-popup>
 
                     <!--任务分组 -->
-                    <van-field readonly name="groupId"
+                    <van-field readonly name="groupId" :disabled="!item.canEdit"
                         v-if="user.company.packageProject == 1 && item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&& (item.projectId && projectss.filter(p => p.id == item.projectId)[0].isPublic!=1)))"
                         clickable :rules="[{ required: user.companyId == 3092 ? true : false, message: '请选择任务分组' }]"
                         :value="item.groupName" label="任务分组" placeholder="请选择任务分组"
@@ -120,7 +120,7 @@
 
                     <!--工作职责-->
                     <van-field readonly name="extraField2" v-if="user.companyId == yuzhongCompId" clickable
-                        :value="item.extraField2Name" label="工作职责" placeholder="请选择工作职责"
+                        :value="item.extraField2Name" label="工作职责" placeholder="请选择工作职责" :disabled="!item.canEdit"
                         @click="clickPickRespon(index, item)" />
                     <van-popup v-model="item.showPickerRespon" position="bottom">
                         <van-picker show-toolbar :columns="item.filteredRespList" value-key="jobRespon"
@@ -128,14 +128,14 @@
                     </van-popup>
                     <!--工作内容-->
                     <van-field readonly name="extraField3" v-if="user.companyId == yuzhongCompId" clickable
-                        :value="item.extraField3Name" label="工作内容" placeholder="请选择工作内容"
+                        :value="item.extraField3Name" label="工作内容" placeholder="请选择工作内容" :disabled="!item.canEdit"
                         @click="clickPickWorkContent(index, item)" />
                     <van-popup v-model="item.showPickerWorkContent" position="bottom">
                         <van-picker show-toolbar :columns="item.workContentList" value-key="workContext"
                             @confirm="choseWorkContent" @cancel="item.showPickerWorkContent = false; $forceUpdate();" />
                     </van-popup>
                     <!--任务阶段 -->
-                    <van-field readonly name="stage"
+                    <van-field readonly name="stage" :disabled="!item.canEdit"
                         v-if="user.companyId != yuzhongCompId && user.company.packageProject == 1 && !user.timeType.hideStages && item.stages != null && item.stages.length > 0"
                         clickable :value="item.stage" label="投入阶段" placeholder="请选择投入阶段"
                         @click="clickPickStage(index, item)" />
@@ -144,7 +144,7 @@
                             @cancel="item.showPickerStage = false; $forceUpdate();" />
                     </van-popup>
                     <!-- 预算来源 -->
-                    <van-field readonly name="basecostId"
+                    <van-field readonly name="basecostId" :disabled="!item.canEdit"
                         v-if="user.company.packageProject == 1 && reportBasecostList && reportBasecostList.length > 0"
                         :value="item.basecostName" label="预算来源" placeholder="请选择预算来源"
                         @click="clickPickCostId(index, item)" />
@@ -154,7 +154,7 @@
                     </van-popup>
                     <!-- 审核人 -->
                     <template v-if="user.timeType.reportAuditType != 3">
-                        <van-field readonly name="projectAuditorId"
+                        <van-field readonly name="projectAuditorId" :disabled="!item.canEdit"
                             v-if="item.auditUserList != null && item.auditUserList.length > 0" clickable
                             :value="item.projectAuditorName" :label="user.companyId == 781 ? '审核人' : '项目审核人'"
                             placeholder="请选择审核人" @click="clickPickAuditor(index, item)">
@@ -178,7 +178,7 @@
 
                     <!-- 自选审批人 -->
                     <template v-if="user.timeType.reportAuditType == 3">
-                        <van-field readonly clickable label="第一审核人" @click="auditorClick(index, 'auditorFirst')">
+                        <van-field readonly clickable label="第一审核人" :disabled="!item.canEdit" @click="auditorClick(index, 'auditorFirst')">
                             <template #input>
                                 <span v-if="!item.auditorFirst"></span>
                                 <span v-else-if="user.userNameNeedTranslate == '1'"><TranslationOpenDataText type='userName'
@@ -186,7 +186,7 @@
                                 <span v-else>{{ item.auditorFirst.name }}</span>
                             </template>
                         </van-field>
-                        <van-field readonly clickable label="第二审核人" @click="auditorClick(index, 'auditorSec')"
+                        <van-field readonly clickable label="第二审核人" :disabled="!item.canEdit" @click="auditorClick(index, 'auditorSec')"
                             v-if="user.timeType.auditLevel > 1">
                             <template #input>
                                 <span v-if="!item.auditorSec"></span>
@@ -195,7 +195,7 @@
                                 <span v-else>{{ item.auditorSec.name }}</span>
                             </template>
                         </van-field>
-                        <van-field readonly clickable label="第三审核人" @click="auditorClick(index, 'auditorThird')"
+                        <van-field readonly clickable label="第三审核人" :disabled="!item.canEdit" @click="auditorClick(index, 'auditorThird')"
                             v-if="user.timeType.auditLevel > 2">
                             <template #input>
                                 <span v-if="!item.auditorThird"></span>
@@ -235,7 +235,7 @@
                     <!-- 相关维度 -->
                     <van-field :value="item.weiduName" v-if="item.projectId && user.timeType.customDegreeActive == 1"
                         readonly name="id" clickable :label="user.timeType.customDegreeName" placeholder="请选择"
-                        @click="clickPickers(index, item)" />
+                        @click="clickPickers(index, item)" :disabled="!item.canEdit" />
 
                     <!-- <van-popup v-model="item.showPickDegree" position="bottom">
                         <van-picker show-toolbar :columns="item.wuduList" value-key="name" @confirm="choseProjects"
@@ -284,7 +284,7 @@
                         </template>
                     </van-field>
                     <van-field v-if="user.company.packageProject == 1 && !user.timeType.hideTask && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&& (item.projectId && projectss.filter(p => p.id == item.projectId)[0].isPublic!=1)))" readonly name="taskId" :value="item.taskName"
-                        :rules="[{ required: reportTimeType.taskRequired? true : false, message: '请选择任务/里程碑' }]" label="任务/里程碑"
+                        :rules="[{ required: reportTimeType.taskRequired? true : false, message: '请选择任务/里程碑' }]" label="任务/里程碑" :disabled="!item.canEdit"
                         placeholder="请选择任务/里程碑" @click="clickPickerTask(index, item)"></van-field>
                     <van-field v-if="user.companyId == 3092" readonly name="sapServiceId" :value="item.sapServiceName"
                         :rules="[{ required: true, message: '请选择服务' }]" label="服务" placeholder="请选择服务"
@@ -311,7 +311,7 @@
                     <!-- 常规选择时间的方式 -->
                     <!-- 全天上下午模式 -->
                     <div v-if="reportTimeType.multiWorktime == 0">
-                        <van-field v-if="reportTimeType.type < 2" readonly clickable
+                        <van-field v-if="reportTimeType.type < 2" readonly clickable :disabled="!item.canEdit"
                             :value="reportTimeType.type == 0 ? item.label : (parseFloat(item.workingTime).toFixed(1) + 'h')"
                             label="工作时长" placeholder="请选择工作时长(小时)" @click="clickTimePicker(index, item)"
                             :rules="[{ required: true, message: '请选择工作时长' }]" />
@@ -419,7 +419,7 @@
                                 style="width: 4.3rem;">含加班</van-checkbox>
                             <van-field v-model="item.overtimeHours" type="number"
                                 :disabled="!item.canEdit || item.isOvertime == null || item.isOvertime == 0 || !item.isOvertime"
-                                placeholder="请输入加班时长" style="width: 5.2rem"></van-field>
+                                placeholder="请输入加班时长" style="width: 5.2rem" @input="$forceUpdate()"></van-field>
                             <span :class="item.canEdit ? 'overListTime' : 'overListTime hoveOver'">小时</span>
                         </div>
                         <van-tag style="position:absolute;right:10px;" v-if="isCorpWX && item.canEdit" type="primary"
@@ -456,7 +456,7 @@
                 </van-cell-group>
 
             </div>
-            <div style="text-align:center;">
+            <div style="text-align:center;" v-if="canEdit">
                 <van-tag size="large"
                     style="text-align:center;margin:10px;padding:12px;margin-bottom:120px;border: 1px solid #20a0ff;"
                     @click="addNewPro" icon="plus" color="#ffffff"><span
@@ -2541,7 +2541,7 @@ export default {
 
                 if(this.user.timeType.userWithMultiDept == 1 && this.userReportDeptList.length > 0) {
                     if(this.form.domains[i].reportTargetDeptId) {
-                        formData.append('reportTargetDeptId', this.form.domains[i].reportTargetDeptId || '');
+                        formData.append('reportTargetDeptId', this.form.domains[i].reportTargetDeptId || -1);
                     } else {
                         this.$toast.fail("请选择[" + this.form.domains[i].projectName + "]填报部门");
                         return;

+ 52 - 8
fhKeeper/formulahousekeeper/timesheet_h5/src/views/editPerfect/editPerfect.vue

@@ -1,11 +1,23 @@
 <template>
-    <div class='perfect'>
-        <div class="perfectTitle">完善工号</div>
-        <div class="perfectNumber">
-            <input type="text" v-model.trim="jobNumber" placeholder="请输入工号">
+    <div class="perfectBox">
+        <div class='perfect' v-if="showjobNumber">
+            <div class="perfectTitle">完善工号</div>
+            <div class="perfectNumber">
+                <input type="text" v-model.trim="jobNumber" placeholder="请输入工号">
+            </div>
+            <div class="perfectBtn">
+                <van-button type="info" round :disabled="!jobNumber" @click="editPerfectJobNumber">确定</van-button>
+            </div>
         </div>
-        <div class="perfectBtn">
-            <van-button type="info" round :disabled="!jobNumber" @click="editPerfectJobNumber">确定</van-button>
+
+        <div class='perfect' v-if="!showjobNumber">
+            <div class="perfectTitle">完善姓名</div>
+            <div class="perfectNumber">
+                <input type="text" v-model.trim="userName" placeholder="请输入姓名">
+            </div>
+            <div class="perfectBtn">
+                <van-button type="info" round :disabled="!userName" @click="editPerfectName">确定</van-button>
+            </div>
         </div>
     </div>
 </template>
@@ -18,13 +30,21 @@ export default {
     data() {
         return {
             jobNumber: '',
-            user: JSON.parse(localStorage.userInfo)
+            userName: '',
+            showjobNumber: true,
+            user: JSON.parse(localStorage.userInfo),
         }
     },
     computed: {},
     watch: {},
     created() { },
-    mounted() { },
+    mounted() { 
+        console.log(this.$route)
+        const { showjobNumber } = this.$route.query
+        if (showjobNumber == 'false') {
+            this.showjobNumber = false
+        }
+    },
     methods: {
         editPerfectJobNumber() {
             const { id } = this.user
@@ -47,6 +67,26 @@ export default {
             }).catch(err => {
                 this.$toast.fail(err);
             });
+        },
+        editPerfectName() {
+            this.$axios.post("/user/userRename", {
+                newName: this.userName
+            })
+            .then(res => {
+                if (res.code == "ok") {
+                    this.$toast.success('操作成功');
+                    let newUserInfo = {
+                        ...this.user,
+                        name: this.userName
+                    }
+                    localStorage.setItem('userInfo', JSON.stringify(newUserInfo))
+                    this.$router.go(-1);
+                } else {
+                    this.$toast.fail(res.msg);
+                }
+            }).catch(err => {
+                this.$toast.fail(err);
+            });
         }
     },
 }
@@ -82,6 +122,10 @@ export default {
         }
     }
 
+.perfectBox {
+    width: 100vw;
+    height: 100vh;
+}
     .perfectBtn {
         width: 80%;
         margin: 0 auto;

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

@@ -103,9 +103,24 @@
             }
 
             // 检查是否有工号
-            const { companyId, jobNumber } = this.user
+            const { companyId, jobNumber, userNameNeedTranslate, name, dingdingUserid } = this.user
             if(this.jobNumberCheckCompanyId.includes(companyId) && !jobNumber) {
-                this.$router.push("/editPerfect");
+                this.$router.push({
+                    path: '/editPerfect',
+                    query: {
+                        showjobNumber: 'true'
+                    }
+                });
+            }
+
+            // 检查是否需要完成姓名
+            if(userNameNeedTranslate == 0 && (name == dingdingUserid)) {
+                this.$router.push({
+                    path: '/editPerfect',
+                    query: {
+                        showjobNumber: 'false'
+                    }
+                });
             }
 
             if(this.user.dingdingUserid) {

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue

@@ -20,6 +20,7 @@
                 <div style="height: 20px;background: #f4f4f4"></div>
                 <!-- </div> -->
                 <van-cell title="账号" v-if="userInfo.userNameNeedTranslate != '1'" :title-style="'flex: 0.5;'" :value="userInfo.phone"></van-cell>
+                <van-cell title="角色" :title-style="'flex: 0.5;'" :value="userInfo.roleName"></van-cell>
                 <van-cell title="工号" v-if="userInfo.jobNumber" :title-style="'flex: 0.5;'" :value="userInfo.jobNumber"></van-cell>
 
                 <div style="height: 20px;background: #f4f4f4"></div>