Преглед изворни кода

Merge remote-tracking branch 'origin/master'

yusm пре 1 година
родитељ
комит
6a80e4f8f1
38 измењених фајлова са 1275 додато и 88 уклоњено
  1. 13 6
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/header/header.vue
  2. 0 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/api.ts
  3. 9 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/constant.ts
  4. 76 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/attachment.vue
  5. 90 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/information.vue
  6. 58 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/operationRecord.vue
  7. 84 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/relatedTasks.vue
  8. 41 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/index.vue
  9. 201 5
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/index.vue
  10. 13 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/type.d.ts
  11. 9 6
      fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts
  12. 3 1
      fhKeeper/formulahousekeeper/customerBuler-crm/src/type.d.ts
  13. 67 7
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/tools.ts
  14. 3 6
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/controller/SysFormController.java
  15. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/entity/SysForm.java
  16. 1 1
      fhKeeper/formulahousekeeper/management-crm/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  17. 63 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CostProjectSettingController.java
  18. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  19. 128 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserWithBeisenController.java
  20. 57 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/CostProjectSetting.java
  21. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/CostProjectSettingMapper.java
  22. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/CostProjectSettingService.java
  23. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/CostProjectSettingServiceImpl.java
  24. 44 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FinanceServiceImpl.java
  25. 102 9
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/BeiSenUtils.java
  26. 19 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/CostProjectSettingMapper.xml
  27. 2 2
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/controller/ReportController.java
  28. 1 1
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/mapper/ReportMapper.java
  29. 2 2
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/ReportService.java
  30. 8 5
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  31. 5 2
      fhKeeper/formulahousekeeper/management-workshop/src/main/resources/mapper/ReportMapper.xml
  32. 1 0
      fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json
  33. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json
  34. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue
  35. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/project/cost.vue
  36. 91 10
      fhKeeper/formulahousekeeper/timesheet/src/views/project/finance.vue
  37. 15 4
      fhKeeper/formulahousekeeper/timesheet/src/views/project/summary.vue
  38. 3 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

+ 13 - 6
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/header/header.vue

@@ -7,10 +7,10 @@
     <div v-for="(routerItem, routerItemIdex) in routerList"
       :class="`border-b-2 border-transparent hover:border-white p-2 mr-4 cursor-pointer multipleyHeader ${activeRouter?.path === routerItem.path ? 'border-white' : ''}`"
       :key="routerItem.path" ref="childDivs" v-show="visibleItems.includes(routerItemIdex)">
-      <div v-if="routerItem.children && routerItem.children.length <= 0" @click="setCurrentRouter(routerItem)" class="text-nowrap">
+      <div v-if="routerItem.children && routerItem.children.length <= 0 && routerItem?.isMenu" @click="setCurrentRouter(routerItem)" class="text-nowrap">
         {{ routerItem.name }}
       </div>
-      <div v-else class="flex justify-center items-center">
+      <div v-if="routerItem.children && routerItem.children.length > 0" class="flex justify-center items-center">
         <el-dropdown>
           <div class="text-white w-full h-full headerText">
             {{ routerItem.name }}
@@ -43,14 +43,16 @@
 </template>
   
 <script lang="ts" setup>
-import { onMounted, ref, watchEffect } from 'vue';
-import { RouteRecordRaw, useRouter } from 'vue-router';
+import { onMounted, ref, watchEffect, watch } from 'vue';
+import { RouteRecordRaw, useRouter, useRoute } from 'vue-router';
 import { useStore } from "../../store/index"
 import defaultCover from "../../assets/defaultCover.png";
 import loginLogin from '../../assets/login/login_logo.png'
 const { routers, clearStore } = useStore()
 const router = useRouter();
-const routerList = ref<RouteRecordRaw[]>([]);
+const route = useRoute()
+// const routerList = ref<RouteRecordRaw[]>([]);
+const routerList = ref<any[]>([]);
 const activeRouter = ref<RouteRecordRaw>();
 
 const visibleItems = ref<number[]>([]);
@@ -103,7 +105,6 @@ const logout = () => {
 onMounted(() => {
   routerList.value = routers;
   activeRouter.value = routerList.value.find((item) => item.path === router.currentRoute.value.path);
-  //console.log("routerList", routerList);
 
   window.addEventListener('resize', updateVisibleItems);
   setTimeout(() => {
@@ -112,6 +113,12 @@ onMounted(() => {
 })
 watchEffect(() => {
   updateVisibleItems();
+  watch(() => route.path, (newPath, _oldPath) => {  
+      activeRouter.value = routerList.value.find((item) => item.path === newPath);
+    }, { 
+      immediate: false 
+    }
+  );  
 });
 </script>
   

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

@@ -1 +0,0 @@
-export const TEST_API = "/api/test"; //发送验证码

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

@@ -0,0 +1,9 @@
+export const MOD = '/thread'
+export const prefix = '/clue'
+export const GETSYSFILED = '/sys-dict/getListByCode'
+export const GETPERSONNEL = '/user/getSimpleActiveUserList'
+export const GETTABLE = `${prefix}/listClue`
+export const GETDETAIL = `${prefix}/getDetail`
+export const UNDATECLAIM = `${prefix}/claim`
+export const UNDATEFORM = `${prefix}/insertAndUpdate`
+export const DELTEROW = `${prefix}/delete`

+ 76 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/attachment.vue

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

Разлика између датотеке није приказан због своје велике величине
+ 90 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/information.vue


+ 58 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/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: 100%;">
+                <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>

+ 84 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/components/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>

+ 41 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/thread/detail/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <div class="h-full threadDetail">
+    <div class="layout p-3">
+      <div class="bg-white w-1/2 shadow-md rounded-md">
+        <Information></Information>
+      </div>
+      <div class="bg-white w-1/2 ml-3 shadow-md rounded-md">
+        <Attachment></Attachment>
+      </div>
+    </div>
+    <div class="layout pl-3 pr-3 pb-3">
+      <div class="bg-white w-2/3 shadow-md rounded-md">
+        <RelatedTasks></RelatedTasks>
+      </div>
+      <div class="bg-white w-1/3 ml-3 shadow-md rounded-md">
+        <OperationRecord></OperationRecord>
+      </div>
+    </div>
+  </div>
+</template>
+  
+<script lang="ts" setup>
+
+import Information from './components/information.vue'
+import Attachment from './components/attachment.vue'
+import RelatedTasks from './components/relatedTasks.vue';
+import OperationRecord from './components/operationRecord.vue';
+</script>
+  
+<style lang="scss" scoped>
+.threadDetail {
+  display: flex;
+  flex-direction: column;
+  .layout {
+    width: 100%;
+    height: 50%;
+    display: flex;
+    justify-content: space-between;
+  }
+}
+</style>

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

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

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

@@ -0,0 +1,13 @@
+interface filterCriteriaFormType { // 线索筛选条件类型
+  clueName: string,
+  clueSourceId: string | number,
+  phone: string,
+  email: string,
+  customerIndustryId: string | number,
+  customerLevelId: string | number,
+  inchargerId: string | number,
+  startTime: string | number,
+  endTime: string | number,
+  pageIndex: string | number,
+  pageFrom: string | number
+}

+ 9 - 6
fhKeeper/formulahousekeeper/customerBuler-crm/src/router/index.ts

@@ -64,25 +64,28 @@ router.beforeEach((to, _from, next) => {
         const addNewRouter = newRouters.find(
           (item: any) => item.path == "/home"
         );
+      
+        let modules = import.meta.glob("@/pages/**/*.vue");
+        console.log(modules);
         routerList.forEach((item: any) => {
-          let filePath = item.path.split("/")[1];
+          let filePath = item.path.replace("/", "")
           if (item.children && item.children.length > 0) {
             item.children.forEach((child: any) => {
-              let childFilePath = child.path.split("/")[1];
+              let childFilePath = child.path.replace("/", "");
               addNewRouter?.children.push({
                 path: child.path,
                 name: child.name,
                 meta: {},
-                component: () =>
-                  import(`@/pages/${filePath}/${childFilePath}/index.vue`),
+                component: modules[`/src/pages/${childFilePath}/index.vue`]
               });
             });
           } else {
+            console.log(`/src/pages/${filePath}/index.vue`)
             addNewRouter?.children.push({
               path: item.path,
               name: item.name,
               meta: {},
-              component: () => import(`@/pages/${filePath}/index.vue`),
+              component: modules[`/src/pages/${filePath}/index.vue`],
             });
           }
         });
@@ -94,6 +97,6 @@ router.beforeEach((to, _from, next) => {
       next(`/login`);
     }
   }
-  //console.log(routerList);
+  console.log(routerList);
 });
 export default router;

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

@@ -8,4 +8,6 @@ interface GlobalPopup extends Notify {
 interface Tree {
   label: string
   children?: Tree[]
-}
+}
+
+type ListByCodeType = ('线索来源'|'客户级别'|'客户行业'|'客户来源'|'商机阶段'|'产品类型'|'产品单位'|'订单类型')[] 

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

@@ -40,19 +40,20 @@ export function getFromValue<T>(formData: T): T {
   }
   return result;
 }
-export function resetFromValue<T>(formData: T) {
+export function resetFromValue<T>(formData: T, resetForm: any = {}) {
   const result: any = {};
   for (const key in formData) {
-    result[key] = '';
+    result[key] = "";
   }
-  return result;
+  // return result;
+  return { ...result, ...resetForm }
 }
 
 /**
  * 更新部门数据
  * @param arr 部门数据源
  * @param flag 是否需要添加全部人员和未分配
- * @returns 
+ * @returns
  */
 export function updateDepTreeData(arr: any, flag: boolean = false) {
   const result = []; // 创建一个新数组来存储结果
@@ -65,16 +66,75 @@ export function updateDepTreeData(arr: any, flag: boolean = false) {
       result.push(arr[i]); // 将更新后的节点添加到结果数组
     }
   }
-  if(flag) {
+  if (flag) {
     result.splice(0, 0, {
       id: -1,
-      label: '全部人员',
+      label: "全部人员",
     });
     result.push({
       id: 0,
-      label: '未分配',
+      label: "未分配",
     });
     return result;
   }
   return result; // 返回更新后的数组,不包含id为-1或0的节点
 }
+
+const listByCode = [
+  { name: "线索来源", id: "ClueSources" },
+  { name: "客户级别", id: "CustomLevel" },
+  { name: "客户行业", id: "CustomIndustry" },
+  { name: "客户来源", id: "CustomSources" },
+  { name: "商机阶段", id: "BusinessStage" },
+  { name: "产品类型", id: "ProductType" },
+  { name: "产品单位", id: "ProductUnit" },
+  { name: "订单类型", id: "OrderType" },
+];
+
+/**
+ * 获取系统字段的数据
+ * @param arr 需要获取字典的中文名称
+ * @returns 接口所需要的id
+ */
+export function getAllListByCode(arr: ListByCodeType) {
+  const result = arr
+    .map((item) => {
+      const found = listByCode.find((obj) => obj.name === item);
+      return found ? found.id : null;
+    })
+    .filter(Boolean);
+
+  return result;
+}
+
+/**
+ * 获取当月第一天
+ * @param date 日期 new Date()
+ * @returns 
+ */
+export function getFirstDayOfMonth(date: Date) {  
+  const firstDay = new Date(date.getFullYear(), date.getMonth(), 1);  
+  return formatDate(firstDay);  
+}  
+
+/**
+ * 获取当月最后一天
+ * @param date 日期 new Date()
+ * @returns 
+ */
+export function getLastDayOfMonth(date: Date) {  
+  const nextMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0);  
+  return formatDate(nextMonth);  
+}  
+
+/**
+ * 将 Date 对象格式化为 "YYYY-MM-DD" 的形式
+ * @param date 日期 new Date()
+ * @returns 
+ */
+export function formatDate(date: Date) {  
+  const year = date.getFullYear();  
+  const month = (1 + date.getMonth()).toString().padStart(2, '0');  
+  const day = date.getDate().toString().padStart(2, '0');  
+  return `${year}-${month}-${day}`;  
+}  

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

@@ -6,10 +6,7 @@ import com.management.platform.entity.SysForm;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.SysFormService;
 import com.management.platform.util.HttpRespMsg;
-import org.springframework.web.bind.annotation.RequestMapping;
-
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Priority;
 import javax.annotation.Resource;
@@ -44,8 +41,8 @@ public class SysFormController {
         return msg;
     }
 
-    @RequestMapping("/getListByCode")
-    public HttpRespMsg getListByCode(@RequestParam String code){
+    @GetMapping("/getListByCode/{code}")
+    public HttpRespMsg getListByCode(@PathVariable(name="code") String code){
         HttpRespMsg msg=new HttpRespMsg();
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
         List<SysForm> sysFormList = sysFormService.list(new LambdaQueryWrapper<SysForm>().eq(SysForm::getCompanyId, companyId).eq(SysForm::getCode, code));

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

@@ -67,4 +67,4 @@ public class SysForm extends Model<SysForm> {
         return this.id;
     }
 
-}
+}  

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

@@ -594,7 +594,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         List<SysModule> moduleList = sysModuleMapper.selectList(queryWrapper);
 
         //过滤一下,这个角色选中的模块
-        moduleList = moduleList.stream().filter(m->ids.contains(m.getId())).collect(Collectors.toList());
+        moduleList = moduleList.stream().filter(m->ids.contains(m.getId())||m.getIsMenu()==false).collect(Collectors.toList());
         if (company.getPackageEngineering() == 1) {
             //生成虚拟的两个审核放进去
             SysModule proModule = new SysModule();

+ 63 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CostProjectSettingController.java

@@ -0,0 +1,63 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.CostProjectSetting;
+import com.management.platform.entity.Project;
+import com.management.platform.service.CostProjectSettingService;
+import com.management.platform.service.ProjectService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-14
+ */
+@RestController
+@RequestMapping("/cost-project-setting")
+public class CostProjectSettingController {
+
+    @Resource
+    private CostProjectSettingService costProjectSettingService;
+    @Resource
+    private ProjectService projectService;
+
+    @RequestMapping("/get")
+    public HttpRespMsg getCostProjectSetting(Integer companyId, String ymonth){
+        HttpRespMsg msg = new HttpRespMsg();
+        List<CostProjectSetting> list  = costProjectSettingService.list(new QueryWrapper<CostProjectSetting>().eq("company_id",companyId).eq("ymonth",ymonth));
+        HashMap ret = new HashMap();
+        if (list.size() > 0){
+            CostProjectSetting item = list.get(0);
+            ret.put("setting", item);
+        }
+        List<Project> allProjectList = projectService.list(new QueryWrapper<Project>().select("id", "project_code", "project_name").eq("company_id", companyId));
+
+        ret.put("allProjectList", allProjectList);
+        msg.data = ret;
+        return msg;
+    }
+
+    @RequestMapping("/save")
+    public HttpRespMsg saveCostProjectSetting(CostProjectSetting costProjectSetting){
+        HttpRespMsg msg = new HttpRespMsg();
+        if (costProjectSetting.getId() == null) {
+            costProjectSettingService.save(costProjectSetting);
+        } else {
+            costProjectSettingService.updateById(costProjectSetting);
+        }
+        return msg;
+    }
+
+}
+

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

@@ -1503,5 +1503,13 @@ public class ProjectController {
     public HttpRespMsg exportUserTaskProcessList(Integer deptId,String userId,Integer projectId,String startDate,String endDate){
         return projectService.exportUserTaskProcessList(deptId,userId,projectId,startDate,endDate);
     }
+
+    @RequestMapping("/getSimpleProjectList")
+    public HttpRespMsg getSimpleProjectList(Integer companyId){
+        HttpRespMsg msg = new HttpRespMsg();
+        //只获取id,编号,名称
+        msg.data = projectService.list(new QueryWrapper<Project>().select("id","project_code", "project_name").eq("company_id", companyId));
+        return msg;
+    }
 }
 

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

@@ -4,14 +4,18 @@ package com.management.platform.controller;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.management.platform.entity.BeisenConfig;
-import com.management.platform.entity.UserWithBeisen;
+import com.google.gson.JsonObject;
+import com.management.platform.entity.*;
 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.UserWithBeisenService;
 import com.management.platform.util.BeiSenUtils;
 import com.management.platform.util.HttpRespMsg;
+import com.management.platform.util.WorkDayCalculateUtils;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
 
@@ -19,9 +23,16 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * <p>
@@ -43,6 +54,10 @@ public class UserWithBeisenController {
     private UserWithBeisenService userWithBeisenService;
     @Resource
     private BeisenConfigMapper beisenConfigMapper;
+    @Resource
+    private UserFvTimeService userFvTimeService;
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
 
     @RequestMapping("/getByTimeWindow")
     public HttpRespMsg getByTimeWindow(String startTime,String stopTime){
@@ -53,6 +68,10 @@ public class UserWithBeisenController {
             httpRespMsg.setError("北森基础数据配置未完成,请联系服务商完成配置");
             return httpRespMsg;
         }
+//        if(companyId==5978){
+//            beisenConfig.setAppKey("70FD83474FB946E5A6A122BB2989E8D9");
+//            beisenConfig.setAppSecret("F494856D0BCC49D18C63429D4F2CB42EDE9480D5C075449E9C97E7AEA5C7D9E1");
+//        }
         List<UserWithBeisen> allBeisenList = userWithBeisenService.list(new LambdaQueryWrapper<UserWithBeisen>().eq(UserWithBeisen::getCompanyId, companyId));
         List<UserWithBeisen> userWithBeisenList=new ArrayList<>();
         List<JSONArray> byTimeWindow = BeiSenUtils.getByTimeWindow("",startTime,stopTime,beisenConfig.getAppKey(),beisenConfig.getAppSecret());
@@ -67,11 +86,11 @@ public class UserWithBeisenController {
                 userWithBeisen.setMobilePhone(employeeInfo.getString("mobilePhone"));
                 userWithBeisen.setName(employeeInfo.getString("name"));
                 userWithBeisen.setUserId(employeeInfo.getString("userID"));
-                Optional<UserWithBeisen> first = allBeisenList.stream().filter(a -> a.getUserId().equals(employeeInfo.getString("userID"))).findFirst();
+                Optional<UserWithBeisen> first = allBeisenList.stream().filter(a ->a.getJobNumber()!=null&& a.getJobNumber().equals(recordInfo.getString("jobNumber"))).findFirst();
                 if(first.isPresent()){
                     userWithBeisen.setId(first.get().getId());
                 }
-                boolean anyMatch = userWithBeisenList.stream().anyMatch(u -> u.getUserId().equals(userWithBeisen.getUserId()));
+                boolean anyMatch = userWithBeisenList.stream().anyMatch(u ->u.getJobNumber()!=null&&u.getJobNumber().equals(userWithBeisen.getJobNumber()));
                 if(anyMatch){
                     continue;
                 }
@@ -98,7 +117,7 @@ public class UserWithBeisenController {
         }
         List<UserWithBeisen> allBeisenList = userWithBeisenService.list(new LambdaQueryWrapper<UserWithBeisen>().eq(UserWithBeisen::getCompanyId, companyId));
         List<UserWithBeisen> userWithBeisenList=new ArrayList<>();
-        JSONArray swipingCards = BeiSenUtils.getSwipingCards(createDate,beisenConfig.getAppKey(),beisenConfig.getAppSecret());
+        JSONArray swipingCards = BeiSenUtils.getSwipingCards(createDate,beisenConfig.getAppKey(),beisenConfig.getAppSecret(),1,300);
         httpRespMsg.setData(swipingCards);
         return httpRespMsg;
     }
@@ -106,12 +125,114 @@ public class UserWithBeisenController {
     @RequestMapping("/getSwipingCardsTest")
     public HttpRespMsg getSwipingCardsTest(String createDate){
         HttpRespMsg httpRespMsg=new HttpRespMsg();
-        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
-        JSONArray swipingCards = BeiSenUtils.getSwipingCards(createDate,"70FD83474FB946E5A6A122BB2989E8D9","F494856D0BCC49D18C63429D4F2CB42EDE9480D5C075449E9C97E7AEA5C7D9E1");
+        JSONArray swipingCards = BeiSenUtils.getSwipingCards(createDate,"97BF1F7DF8314F268D91E09C12772CAD","D52BE666683C408087063B422275C289DE09488E28D64B7FB4E5190C43F8AA66",1,300);
         System.out.println("获取到的打卡数据====>"+swipingCards.toJSONString());
         httpRespMsg.setData(swipingCards);
         return httpRespMsg;
     }
 
+    @RequestMapping("/getOverTimeListTest")
+    public HttpRespMsg getOverTimeListTest(String createDate){
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        JSONArray swipingCards = BeiSenUtils.getOverTimeList(createDate,"97BF1F7DF8314F268D91E09C12772CAD","D52BE666683C408087063B422275C289DE09488E28D64B7FB4E5190C43F8AA66",1,100);
+        System.out.println("获取到的加班那数据====>"+swipingCards.toJSONString());
+        httpRespMsg.setData(swipingCards);
+        return httpRespMsg;
+    }
+
+    @RequestMapping("/getAttendanceStatisticsTest")
+    public HttpRespMsg getAttendanceStatisticsTest(String startDate,String endDate){
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        JSONArray swipingCards = BeiSenUtils.getAttendanceStatistics(startDate,endDate,"97BF1F7DF8314F268D91E09C12772CAD","D52BE666683C408087063B422275C289DE09488E28D64B7FB4E5190C43F8AA66",1,100);
+        System.out.println("获取到的考勤数据====>"+swipingCards.toJSONString());
+        httpRespMsg.setData(swipingCards);
+        return httpRespMsg;
+    }
+
+
+    @RequestMapping("/syncAttendanceFromBeisen")
+    @Transactional(rollbackFor = Exception.class)
+    public HttpRespMsg syncAttendanceFromBeisen(String startDate,String endDate){
+        HttpRespMsg msg=new HttpRespMsg();
+        DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        DateTimeFormatter df1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        DateTimeFormatter df2=DateTimeFormatter.ofPattern("HH:mm");
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        TimeType timeType = timeTypeMapper.selectById(companyId);
+        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);
+        if(beisenConfig==null){
+            msg.setError("北森基础数据配置未完成,请联系服务商完成配置");
+            return msg;
+        }
+        //todo 获取到指定日期的考勤数据
+        JSONArray attendanceStatistics = BeiSenUtils.getAttendanceStatistics(startDate, endDate, beisenConfig.getAppKey(), beisenConfig.getAppSecret(), 1, 100);
+        //todo 获取到指定日期的加班数据
+        List<LocalDate> workDaysListInRange = WorkDayCalculateUtils.getWorkDaysListInRange(startDate, endDate, 1);
+        JSONArray allOverTimeList=new JSONArray();
+        List<UserFvTime> userFvTimeList=new ArrayList<>();
+        for (LocalDate localDate : workDaysListInRange) {
+            JSONArray overTimeList = BeiSenUtils.getOverTimeList(df.format(localDate), beisenConfig.getAppKey(), beisenConfig.getAppSecret(), 1, 100);
+            allOverTimeList.addAll(overTimeList);
+        }
+        for (LocalDate localDate : workDaysListInRange) {
+            Stream<JSONObject> swipingCardsStream = attendanceStatistics.stream().map(item -> (JSONObject) item);
+            //todo: 获取当天的考勤数据
+            List<JSONObject> swipingCardDateList = swipingCardsStream.filter(i -> LocalDateTime.parse(i.getString("SwipingCardDate"),df1).toLocalDate().equals(localDate)).collect(Collectors.toList());
+            for (JSONObject item : swipingCardDateList) {
+                //获取当前数据下的人员工号对应到工时管家
+                String cardNumber = item.getString("CardNumber");
+                Optional<User> first = userList.stream().filter(f -> f.getJobNumber().equals(cardNumber)).findFirst();
+                //todo: 获取考勤打卡时间集合
+                JSONArray times = item.getJSONArray("Times");
+                Stream<JSONObject> timeStream = times.stream().map(time -> (JSONObject) time);
+                Stream<JSONObject> timeStream1 = times.stream().map(time -> (JSONObject) time);
+                //获取最早上班打卡时间
+                List<LocalTime> minLocalTimeList = timeStream.filter(t -> t.getIntValue("Type") == 1).map(i -> LocalDateTime.parse(i.getString("ActualTime"),df1).toLocalTime()).collect(Collectors.toList());
+                Optional<LocalTime> min = minLocalTimeList.stream().min(LocalTime::compareTo);
+                //获取最早上班打卡时间
+                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);
+                    //todo:针对景昱 工作日默认以8小时工作制度加上加班时长 非工作日以加班时长为准
+                    Double workTime=8.0;
+                    Stream<JSONObject> overTimeStream = allOverTimeList.stream().map(elment -> (JSONObject) elment);
+                    Optional<UserWithBeisen> beisen = userWithBeisenList.stream().filter(u -> u.getJobNumber() != null && u.getJobNumber().equals(first.get().getJobNumber())).findFirst();
+                    List<JSONObject> overTimeList = overTimeStream.filter(a -> a.getString("StaffId").equals(beisen.get().getUserId())
+                            && a.getIntValue("ApproveStatus") == 2).collect(Collectors.toList());
+                    if(overTimeList.size()>0){
+                        double actualOverTimeDuration = overTimeList.stream().mapToDouble(i -> i.getDouble("ActualOverTimeDuration")).sum();
+                        if(workDay){
+                            workTime= workTime+actualOverTimeDuration;
+                        }else {
+                            workTime= actualOverTimeDuration;
+                        }
+                    }
+                    UserFvTime userFvTime=new UserFvTime();
+                    userFvTime.setWorkDate(localDate);
+                    userFvTime.setStartTime(min.isPresent()?df2.format(min.get()):"08:30");
+                    userFvTime.setEndTime(max.isPresent()?df2.format(max.get()):"17:30");
+                    userFvTime.setUserId(first.get().getId());
+                    userFvTime.setCompanyId(companyId);
+                    userFvTime.setWorkHours(workTime.floatValue());
+                    UserFvTime one = userFvTimeService.getOne(new LambdaQueryWrapper<UserFvTime>().eq(UserFvTime::getCompanyId, companyId).eq(UserFvTime::getUserId, first.get().getId()).eq(UserFvTime::getWorkDate, localDate));
+                    if(one!=null){
+                        userFvTime.setId(one.getId());
+                    }
+                    userFvTimeList.add(userFvTime);
+                }
+            }
+        }
+        if(userFvTimeList.size()>0){
+            if(!userFvTimeService.saveOrUpdateBatch(userFvTimeList)){
+                msg.setError("同步验证失败");
+            }
+        }
+        return msg;
+    }
+
 }
 

+ 57 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/CostProjectSetting.java

@@ -0,0 +1,57 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2024-03-14
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class CostProjectSetting extends Model<CostProjectSetting> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("company_id")
+    private Integer companyId;
+
+    /**
+     * 所属年月
+     */
+    @TableField("ymonth")
+    private String ymonth;
+
+    /**
+     * 0-全部项目,1-选择部分项目,2-排除部分项目
+     */
+    @TableField("setting_type")
+    private Integer settingType;
+
+    /**
+     * 选择的项目列表
+     */
+    @TableField("project_json")
+    private String projectJson;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

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

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

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

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

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

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

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

@@ -55,6 +55,8 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
     @Resource
     private DepartmentMapper departmentMapper;
     @Resource
+    private CostProjectSettingMapper costProjectSettingMapper;
+    @Resource
     private TimeTypeMapper timeTypeMapper;
     @Resource
     private FinanceService financeService;
@@ -707,7 +709,27 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             endDate = endDate.plusMonths(1);
 
             List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
-
+            //如果定义了可分摊项目的过滤,按照数据处理一下
+            CostProjectSetting setting = costProjectSettingMapper.selectOne(new QueryWrapper<CostProjectSetting>().eq("company_id", companyId));
+            if (setting != null && setting.getSettingType() > 0) {
+                List<Map<String, Object>> newProjectTimeList = new ArrayList<>();
+                for (Map<String, Object> map : projectTimeList) {
+                    Integer projectId = ((Long) map.get("projectId")).intValue();
+                    JSONArray chosenPList = JSONArray.parseArray(setting.getProjectJson());
+                    if (setting.getSettingType() == 1) {
+                        //设置了可分摊的项目
+                        if (chosenPList.contains(projectId)) {
+                            newProjectTimeList.add(map);
+                        }
+                    } else {
+                        //设置了不可分摊的项目,要排除
+                        if (!chosenPList.contains(projectId)) {
+                            newProjectTimeList.add(map);
+                        }
+                    }
+                }
+                projectTimeList = newProjectTimeList;
+            }
 
             //计算每个项目的时间和成本
             ProjectSumItem item = new ProjectSumItem();
@@ -1077,7 +1099,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             //按项目名称分组
             DecimalFormat workTimeFormatter = new DecimalFormat("0.00");
             if (groupByCategory == 0) {
-                pList.forEach(p->{
+                for (ProjectSumItem p : pList) {
                     p.cost = p.cost.setScale(2, BigDecimal.ROUND_HALF_UP);
                     p.salary = p.salary.setScale(2, BigDecimal.ROUND_HALF_UP);
                     p.bonus = p.bonus.setScale(2, BigDecimal.ROUND_HALF_UP);
@@ -1343,7 +1365,7 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
                             }
                         }
                     }
-                });
+                }
             } else {
                 //按项目分类分组,需要重组pList为一个项目分组一条
                 Map<String, List<ProjectSumItem>> categoryMap = pList.stream().collect(Collectors.groupingBy(ProjectSumItem::getCategoryName));
@@ -1526,11 +1548,26 @@ public class FinanceServiceImpl extends ServiceImpl<FinanceMapper, Finance> impl
             endDate = endDate.plusMonths(1);
 
             List<Map<String, Object>> projectTimeList = reportMapper.getRealProjectTime(startDate, endDate, companyId);
-            for (Map<String, Object> map : projectTimeList) {
-                String p = (String) map.get("project");
-                if (p.equals("10G OLT Driver-XGs combo")) {
-                    System.out.println("找到项目 10G OLT Driver-XGs combo");
+            //如果定义了可分摊项目的过滤,按照数据处理一下
+            CostProjectSetting setting = costProjectSettingMapper.selectOne(new QueryWrapper<CostProjectSetting>().eq("company_id", companyId));
+            if (setting != null && setting.getSettingType() > 0) {
+               List<Map<String, Object>> newProjectTimeList = new ArrayList<>();
+                for (Map<String, Object> map : projectTimeList) {
+                    Integer projectId = ((Long) map.get("projectId")).intValue();
+                    JSONArray chosenPList = JSONArray.parseArray(setting.getProjectJson());
+                    if (setting.getSettingType() == 1) {
+                        //设置了可分摊的项目
+                        if (chosenPList.contains(projectId)) {
+                            newProjectTimeList.add(map);
+                        }
+                    } else {
+                        //设置了不可分摊的项目,要排除
+                        if (!chosenPList.contains(projectId)) {
+                            newProjectTimeList.add(map);
+                        }
+                    }
                 }
+                projectTimeList = newProjectTimeList;
             }
             BigDecimal totalMoneyCost = BigDecimal.valueOf(0);
             //计算每个项目的时间和成本

+ 102 - 9
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/BeiSenUtils.java

@@ -80,6 +80,9 @@ public class BeiSenUtils {
         return result;
     }
 
+    /**
+     * 获取人员数据
+     * */
     public static List<JSONArray> getByTimeWindow(String scrollId,String startTime,String stopTime,String appkey,String appSecret){
         List<JSONArray> resultList=new ArrayList<>();
         String url = "https://openapi.italent.cn/TenantBaseExternal/api/v5/Employee/GetByTimeWindow";
@@ -111,13 +114,15 @@ public class BeiSenUtils {
                     List<JSONArray> byTimeWindow = getByTimeWindow(nextScrollId,startTime,stopTime,appkey,appSecret);
                     resultList.addAll(byTimeWindow);
                 }
-                return resultList;
             }
         }
         return resultList;
     }
 
-    public static JSONArray getSwipingCards(String createDate,String appkey,String appSecret){
+    /**
+     * 获取打卡数据
+     * */
+    public static JSONArray getSwipingCards(String createDate,String appkey,String appSecret,Integer pageIndex,Integer pageSize){
         String url = "https://openapi.italent.cn/AttendanceOpen/api/v1/SwipingCardData/GetSwipingCards";
         HttpHeaders headers = new HttpHeaders();
         RestTemplate restTemplate = new RestTemplate();
@@ -128,25 +133,113 @@ public class BeiSenUtils {
         headers.add("Authorization","Bearer "+accessToken);
         JSONObject requestMap = new JSONObject();
         requestMap.put("PunchCardDate",createDate);
-        requestMap.put("pageIndex","1");
-        requestMap.put("pageSize","300");
+        requestMap.put("PageIndex",pageIndex+"");
+        requestMap.put("PageSize",pageSize+"");
         System.out.println("--------headers请求头数据-------"+headers);
         System.out.println("--------requestMap请求参数-------"+requestMap);
         HttpEntity<JSONObject> entity = new HttpEntity<>(requestMap, headers);
         ResponseEntity<String> ResponseEntity = restTemplate.postForEntity(url, entity, String.class);
+        JSONArray lastJSONArray=new JSONArray();
+        JSONArray target=new JSONArray();
         if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
             String resp= ResponseEntity.getBody();
             JSONObject respJson = JSONObject.parseObject(resp);
-            if(respJson.getIntValue("code")==200){
+            if(respJson.getIntValue("Code")==200){
                 JSONObject data = respJson.getJSONObject("Data");
-                JSONArray resultData = data.getJSONArray("SwipingCardDetails");
-                return resultData;
+                target = data.getJSONArray("SwipingCardDetails");
+                lastJSONArray.addAll(target);
+                if (target.size()>0){
+                    pageIndex++;
+                    JSONArray swipingCardDetails = getSwipingCards(createDate, appkey, appSecret, pageIndex, pageSize);
+                    lastJSONArray.addAll(swipingCardDetails);
+                }
             }
         }
-        //todo:景昱 计算工作时长 默认工作日 8小时+当天人员加班(审核状态:通过)时长 非工作日考勤数据以加班数据为准
+        return lastJSONArray;
+    }
 
+
+    /**
+     * 获取加班数据
+     * */
+    public static JSONArray getOverTimeList(String createDate,String appkey,String appSecret,Integer pageIndex,Integer pageSize){
+        String url = "https://openapi.italent.cn/AttendanceOpen/api/v1/AttendanceOvertime/GetOverTimeList";
+        HttpHeaders headers = new HttpHeaders();
+        RestTemplate restTemplate = new RestTemplate();
+        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+        headers.setContentType(type);
+        String accessToken = getToken(appkey,appSecret);
+        System.out.println("--------Bearer TOKEN--------"+accessToken);
+        headers.add("Authorization","Bearer "+accessToken);
+        JSONObject requestMap = new JSONObject();
+        requestMap.put("OverTimeDate",createDate);
+        requestMap.put("PageIndex",pageIndex+"");
+        requestMap.put("PageSize",pageSize+"");
+        System.out.println("--------headers请求头数据-------"+headers);
+        System.out.println("--------requestMap请求参数-------"+requestMap);
+        HttpEntity<JSONObject> entity = new HttpEntity<>(requestMap, headers);
+
+
+        ResponseEntity<String> ResponseEntity = restTemplate.postForEntity(url, entity, String.class);
+        JSONArray lastJSONArray=new JSONArray();
+        if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+            String resp= ResponseEntity.getBody();
+            JSONObject respJson = JSONObject.parseObject(resp);
+            if(respJson.getIntValue("Code")==200){
+                JSONObject data = respJson.getJSONObject("Data");
+                JSONArray target = data.getJSONArray("OverTimeList");
+                lastJSONArray.addAll(target);
+                if (target.size()>0){
+                    pageIndex++;
+                    JSONArray overTimeList = getOverTimeList(createDate, appkey, appSecret, pageIndex, pageSize);
+                    lastJSONArray.addAll(overTimeList);
+                }
+            }
+        }
+        //todo:景昱 计算工作时长 默认工作日 8小时+当天人员加班(审核状态:通过)时长 非工作日考勤数据以加班数据为准
         //todo:如何分页 循环调用接口 当天接口返回数据为空 取消调用
-        return new JSONArray();
+        return lastJSONArray;
+    }
+
+
+
+    /**
+     * 获取考勤数据
+     * */
+    public static JSONArray getAttendanceStatistics(String startDate,String endDate,String appkey,String appSecret,Integer pageIndex,Integer pageSize){
+        String url = "https://openapi.italent.cn/AttendanceOpen/api/v1/AttendanceStatistics/get";
+        HttpHeaders headers = new HttpHeaders();
+        RestTemplate restTemplate = new RestTemplate();
+        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+        headers.setContentType(type);
+        String accessToken = getToken(appkey,appSecret);
+        System.out.println("--------Bearer TOKEN--------"+accessToken);
+        headers.add("Authorization","Bearer "+accessToken);
+        JSONObject requestMap = new JSONObject();
+        requestMap.put("StartDate",startDate);
+        requestMap.put("StopDate",endDate);
+        requestMap.put("PageIndex",pageIndex+"");
+        requestMap.put("PageSize",pageSize+"");
+        System.out.println("--------headers请求头数据-------"+headers);
+        System.out.println("--------requestMap请求参数-------"+requestMap);
+        HttpEntity<JSONObject> entity = new HttpEntity<>(requestMap, headers);
+        ResponseEntity<String> ResponseEntity = restTemplate.postForEntity(url, entity, String.class);
+        JSONArray lastJSONArray=new JSONArray();
+        if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+            String resp= ResponseEntity.getBody();
+            JSONObject respJson = JSONObject.parseObject(resp);
+            if(respJson.getIntValue("Code")==200){
+                JSONObject data = respJson.getJSONObject("Data");
+                JSONArray target = data.getJSONArray("Items");
+                lastJSONArray.addAll(target);
+                if (target.size()>0){
+                    pageIndex++;
+                    JSONArray overTimeList = getAttendanceStatistics(startDate,endDate, appkey, appSecret, pageIndex, pageSize);
+                    lastJSONArray.addAll(overTimeList);
+                }
+            }
+        }
+        return lastJSONArray;
     }
 
 }

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

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.CostProjectSettingMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.CostProjectSetting">
+        <id column="id" property="id" />
+        <result column="company_id" property="companyId" />
+        <result column="ymonth" property="ymonth" />
+        <result column="setting_type" property="settingType" />
+        <result column="project_json" property="projectJson" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, company_id, ymonth, setting_type, project_json
+    </sql>
+
+</mapper>

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

@@ -1524,7 +1524,7 @@ public class ReportController {
 
     //人员工时工价表
     @RequestMapping("getPersonWorkHoursWagesList")
-    public HttpRespMsg getPersonWorkHoursWagesList(String deptId,String userId,String startDate,String endDate,Integer pageIndex,Integer pageSize){
+    public HttpRespMsg getPersonWorkHoursWagesList(Integer deptId,String userId,String startDate,String endDate,Integer pageIndex,Integer pageSize){
         return reportService.getPersonWorkHoursWagesList(deptId,userId,startDate,endDate,pageIndex,pageSize);
     }
 
@@ -1534,7 +1534,7 @@ public class ReportController {
     }
 
     @RequestMapping("exportPersonWorkHoursWorkTime")
-    public HttpRespMsg exportPersonWorkHoursWorkTime(String deptId,String userId,String startDate,String endDate){
+    public HttpRespMsg exportPersonWorkHoursWorkTime(Integer deptId,String userId,String startDate,String endDate){
         return reportService.exportPersonWorkHoursWorkTime(deptId,userId,startDate,endDate);
     }
 

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

@@ -182,7 +182,7 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     List<Map<String, Object>> getCcReportByDate(@Param("date") String date, @Param("id") String id, @Param("state") Integer state);
 
-    List<Map<String, Object>> getPersonWorkHoursWagesList(Integer companyId, String startDate, String endDate, String deptId, String userId);
+    List<Map<String, Object>> getPersonWorkHoursWagesList(Integer companyId, String startDate, String endDate,@Param("list") List<Integer> deptId, String userId);
 
     List<Map<String, Object>> getPersonWorkHoursWagesDetail(String date, String userId, Integer companyId,String startDate,String endDate,Integer checkStatus,Integer detailStatus);
 

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

@@ -117,11 +117,11 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg getChekerList(Integer checkType, Integer deptId);
 
-    HttpRespMsg getPersonWorkHoursWagesList(String deptId, String userId, String startDate, String endDate, Integer pageIndex, Integer pageSize);
+    HttpRespMsg getPersonWorkHoursWagesList(Integer deptId, String userId, String startDate, String endDate, Integer pageIndex, Integer pageSize);
 
     HttpRespMsg getPersonWorkHoursWagesDetail(String date, String userId,String startDate,String endDate,Integer checkStatus,Integer detailStatus);
 
-    HttpRespMsg exportPersonWorkHoursWorkTime(String deptId, String userId, String startDate, String endDate);
+    HttpRespMsg exportPersonWorkHoursWorkTime(Integer deptId, String userId, String startDate, String endDate);
 
     HttpRespMsg getProcedureRealTimeProgressList(String deptId, String userId, String startDate, String endDate, Integer pageIndex, Integer pageSize);
 

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

@@ -4014,7 +4014,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 //    }
 
     @Override
-    public HttpRespMsg getPersonWorkHoursWagesList(String deptId, String userId, String startDate, String endDate, Integer pageIndex, Integer pageSize) {
+    public HttpRespMsg getPersonWorkHoursWagesList(Integer deptId, String userId, String startDate, String endDate, Integer pageIndex, Integer pageSize) {
         HttpRespMsg httpRespMsg=new HttpRespMsg();
         HashMap resultMap=new HashMap();
         DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -4031,13 +4031,16 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         boolean canViewAll = sysFunctionService.hasPriviledge(user.getRoleId(), "查看全部人员工时工价");
         QueryWrapper<User> queryWrapper=new QueryWrapper();
         queryWrapper.eq("company_id",companyId);
-        if(!StringUtils.isEmpty(deptId)){
-            queryWrapper.eq("department_id",deptId);
+        List<Integer> deptIds=new ArrayList<>();
+        if(deptId!=null){
+            deptIds = getBranchDepartment(Integer.valueOf(deptId), departmentList);
+            deptIds.add(Integer.valueOf(deptId));
+            queryWrapper.in("department_id",deptIds);
         }
         if(!StringUtils.isEmpty(userId)){
             queryWrapper.eq("id",userId);
         }
-        List<Map<String,Object>> personWorkHoursWagesList=reportMapper.getPersonWorkHoursWagesList(companyId,startDate,endDate,deptId,userId);
+        List<Map<String,Object>> personWorkHoursWagesList=reportMapper.getPersonWorkHoursWagesList(companyId,startDate,endDate,deptIds,userId);
         if(!canViewAll){
             /*作为工长看到的数据*/
             List<Plan> plans = planMapper.selectList(new QueryWrapper<Plan>().eq("foreman_id", user.getId()));
@@ -4236,7 +4239,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg exportPersonWorkHoursWorkTime(String deptId, String userId, String startDate, String endDate) {
+    public HttpRespMsg exportPersonWorkHoursWorkTime(Integer deptId, String userId, String startDate, String endDate) {
         HttpRespMsg respMsg = getPersonWorkHoursWagesList(deptId, userId, startDate, endDate, -1, -1);
         Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
         HashMap resultMap= (HashMap) respMsg.data;

+ 5 - 2
fhKeeper/formulahousekeeper/management-workshop/src/main/resources/mapper/ReportMapper.xml

@@ -140,8 +140,11 @@
         left join user b on a.creator_id=b.id
         left join department c on c.department_id=b.department_id
         where a.company_id=#{companyId}
-        <if test="deptId!=null and deptId!=''">
-            and c.department_id=#{deptId}
+        <if test="list!=null and list.size()>0">
+            and c.department_id in
+            <foreach collection="list" close=")" open="(" item="item" separator=",">
+                #{item}
+            </foreach>
         </if>
         <if test="userId!=null and userId!=''">
             and b.id=#{userId}

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json

@@ -371,6 +371,7 @@
   "Selectmonth": "select month",
   "projectworker": "Personnel without project hours",
   "Apportionmentratesetting": "Apportionment ratio settings",
+  "ApportionmentProjsetting": "Apportionment project settings",
   "Downloadthetemplate": "Template download",
   "Dataupload": "data upload",
   "Exportdata": "Data output",

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

@@ -372,7 +372,8 @@
   "Revocationofsuccess": "撤销成功",
   "Selectmonth": "选择月份",
   "projectworker": "无项目工时人员",
-  "Apportionmentratesetting": "分摊比例设置",
+  "Apportionmentratesetting": "无工时人员分摊比例设置",
+  "ApportionmentProjsetting": "分摊项目设置",
   "Downloadthetemplate": "模板下载",
   "Dataupload": "数据上传",
   "Exportdata": "数据导出",

+ 2 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue

@@ -1881,7 +1881,8 @@ export default {
       this.http.post(this.port.project.list, {},
         res => {
           if (res.code == "ok") {
-            this.projectList = res.data;
+            //费用报销界面选择项目只针对进行中的
+            this.projectList = res.data.filter(i =>i.status==1);
           } else {
             this.$message({
               message: res.msg,

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

@@ -101,7 +101,7 @@
                 </el-form-item>
                 <el-form-item prop="projectId" :label="$t('defaultText.selectProject')" v-if="radio != $t('ren-yuan') && radio != $t('projectclassification') && radio != $t('lable.department') && radio != $t('zhu-xiang-mu')">
                     <el-select v-model="exportParam.projectId" :placeholder="$t('other.allProject')"  clearable style="width:350px;" filterable="true" popper-class="projectSelectPopperClass">
-                        <el-option v-for="item in projectList"  :key="item.id" :label="item.projectName + item.projectCode" :value="item.id">
+                        <el-option v-for="item in projectList"  :key="item.id" :label="item.projectName + '/'+ item.projectCode" :value="item.id">
                             <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
                             <span style="float: right;font-size: 13px;">{{ item.projectName }}</span>
                         </el-option>

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

@@ -14,7 +14,8 @@
                 <el-radio-button :label="$t('projectworker')">{{$t('projectworker')}}({{noReportUserList.length}})</el-radio-button>
             </el-radio-group>
             <el-form-item v-if="permissions.financialProportion" style="margin-left:5px;">
-                <el-link type="primary" :underline="false" @click="showSettingDialog()" >{{ $t('Apportionmentratesetting') }}</el-link>
+                <el-link type="primary" :underline="false" @click="showProjSettingDialog()" v-if="user.companyId == 88">{{ $t('ApportionmentProjsetting')}}</el-link>
+                <el-link type="primary" :underline="false" @click="showSettingDialog()" style="margin-left:50px;">{{ $t('Apportionmentratesetting') }}</el-link>
             </el-form-item>
 
             <el-form-item style="float:right;" v-if="permissions.financialUpload">
@@ -496,6 +497,24 @@
                 <el-button type="primary" @click="saveProjectSetting()" >{{ $t('btn.determine') }}</el-button>
             </div>
         </el-dialog>
+        <el-dialog :title="proSetting.ymonth + $t('projectstobe assessed')" show-header="false" v-if="projSettingDialog" :visible.sync="projSettingDialog" 
+            :close-on-click-modal="false" customClass="customWidth" width="1000px">
+            <el-radio-group v-model="proSetting.settingType" style="margin-top:15px;">
+                <el-radio :label=0 >全部已填报项目</el-radio>
+                <el-radio :label=1 >选择部分项目</el-radio>
+                <el-radio :label=2 >排除部分项目</el-radio>
+            </el-radio-group>
+            <el-select v-model="proSettingChosenProjects" multiple filterable clearable style="width:100%;margin-top:10px;" v-show="proSetting.settingType > 0" @change="updateChosenView()">
+                <el-option v-for="item in allProjectList"  :key="item.id" :label="item.projectName + '/'+ item.projectCode" :value="item.id">
+                    <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
+                    <span style="float: right;font-size: 13px;">{{ item.projectName }}</span>
+                </el-option>
+            </el-select>
+            <div slot="footer" class="dialog-footer">
+                <el-button type="default" @click="projSettingDialog = false" >{{ $t('Shutdown') }}</el-button>
+                <el-button type="primary" @click="saveCostProjectSetting()" >{{ $t('btn.determine') }}</el-button>
+            </div>
+        </el-dialog>
         <el-dialog :title="$t('allocationdata')" v-if="intoAmortizationDialog" :visible.sync="intoAmortizationDialog" customClass="customWidth" width="500px">
             <p>{{'1.' + $t('other.download')}}
             <el-link v-if="financialFlg" type="primary" style="margin-left:5px;" :underline="false" :href="'./upload/'+$t('allocationImporttemplates')+'.xlsx'" :download="$t('allocationImporttemplates') + '.xlsx'">{{$t('allocationImporttemplates')+'.xlsx'}}</el-link>
@@ -593,6 +612,11 @@ import { error } from 'dingtalk-jsapi';
         },
         data() {
             return {
+                proSettingChosenProjects:[],
+                proSetting: {
+                    ymonth:null,settingType:0,
+                },
+                projSettingDialog: false,
                 operating: false,
                 showMissingDialog: false,
                 missingFinanceUserList: [],
@@ -668,6 +692,58 @@ import { error } from 'dingtalk-jsapi';
             };
         },
         methods: {
+            getMonthProjSetting() {
+                this.http.post('/cost-project-setting/get',{companyId: this.user.companyId, ymonth: this.date},res => {
+                    if(res.code == 'ok'){
+                        if (res.data.setting != null) {
+                            this.proSetting = res.data.setting;
+                            this.proSettingChosenProjects = JSON.parse(this.proSetting.projectJson);
+                        }
+                        if (this.allProjectList == null || this.allProjectList.length == 0) {
+                            this.allProjectList = res.data.allProjectList;
+                        }
+                    }else{
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.$message({
+                        message: err,
+                        type: 'error'
+                    })
+                })
+            },
+            saveCostProjectSetting() {
+                //处理选中的项目
+                if (this.proSetting.settingType == 0) {
+                    this.proSetting.projectJson = '[]';
+                } else {
+                    this.proSetting.projectJson = JSON.stringify(this.proSettingChosenProjects);
+                }
+                this.http.post('/cost-project-setting/save',this.proSetting,res => {
+                    if(res.code == 'ok'){
+                        this.$message({
+                            message: '保存成功',
+                            type: 'success'
+                        })
+                        this.projSettingDialog = false;
+                        //刷新分摊数据显示
+                        this.assignToProject();
+                    }else{
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.$message({
+                        message: er,
+                        type: 'error'
+                    })
+                })
+            },
             uploadTest(){
                 this.http.post('/report/uploadThirdReportData',{
                     yearMonth: this.date
@@ -852,6 +928,7 @@ import { error } from 'dingtalk-jsapi';
                     this.assignToProject();
                 }
             },
+
             getLastMonthSetting() {
                 this.costSettingLoading = true;
                 var dataArr = this.date.split('-');
@@ -1067,6 +1144,12 @@ import { error } from 'dingtalk-jsapi';
                 this.settingDialog = true;
                 this.getMonthSetting();
             },
+            showProjSettingDialog() {
+                this.projSettingDialog = true;
+                this.proSetting.ymonth = this.date;
+                this.proSetting.companyId = this.user.companyId;
+                this.getMonthProjSetting();
+            },
             handleSelectionChange(val) {
                 this.multipleSelection = val;
             },
@@ -1194,15 +1277,14 @@ import { error } from 'dingtalk-jsapi';
             
 
             // },
-            // getProjects() {
-            //     this.http.post('/finance/getProjects', {companyId: this.user.companyId, yearMonth: this.date},
-            //         res => {
-            //             if (res.code == "ok") {
-            //                 this.projectCols = res.data.financeProjects;
-            //                 this.allProjectList = res.data.allProjectList;
-            //             }});
+            getProjects() {
+                this.http.post('/project/getSimpleProjectList', {companyId: this.user.companyId},
+                    res => {
+                        if (res.code == "ok") {
+                            this.allProjectList = res.data.allProjectList;
+                        }});
                 
-            // },
+            },
             // showNoProjectUsers() {
             //     this.showNPDialog = true;
             //     this.http.post('/finance/getNoProjectUsers', {yearMonth: this.date},
@@ -1775,7 +1857,6 @@ import { error } from 'dingtalk-jsapi';
             };
             
             this.getCustomColumn();
-            // this.getProjects();
             this.addreviewer();
             this.arrter()
             this.loadMonthData()

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

@@ -698,10 +698,21 @@ export default {
                 tooltip: {
                     trigger: 'axis',
                     formatter: function (params, ticket, callback) {
-                        var res = params[0].name + "" + " : " + params[0].data.value
-                            + (_this.sumListRadio == _this.$t('plantime') ? _this.$t('time.hour') : _this.$t('ge'));
-                        _this.params = params;
-                        return res;
+                        let res = ''
+                        if(_this.user.userNameNeedTranslate == 1) {
+                            res = params[0].data.value
+                                + (_this.sumListRadio == _this.$t('plantime') ? _this.$t('time.hour') : _this.$t('ge'));
+                            _this.params = params;
+                        } else {
+                            res = params[0].name + "" + " : " + params[0].data.value
+                                + (_this.sumListRadio == _this.$t('plantime') ? _this.$t('time.hour') : _this.$t('ge'));
+                            _this.params = params;
+                        }
+                        return res
+                        // var res = params[0].name + "" + " : " + params[0].data.value
+                        //     + (_this.sumListRadio == _this.$t('plantime') ? _this.$t('time.hour') : _this.$t('ge'));
+                        // _this.params = params;
+                        // return res;
                     }
                 },
                 xAxis: {

+ 3 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -5385,6 +5385,7 @@
                 param.stateKey = this.stateKey
                 // param.departmentId = this.user.departmentId
                 console.log(param, '《=== 导出传的参数')
+                return
                 this.http.post( this.port.report.export, param,
                 res => {
                     this.exportingData = false;
@@ -7893,10 +7894,10 @@
                 this.workForm.domains[obj.idx].projectAuditorId = obj.id
             },
             vueCasader(obj) {
-                // console.log(obj, '看看值')
+                console.log(obj, '看看值')
                 if(obj.distinction == 1 && obj.item) {
                     let arr = []
-                    arr.push(obj.item.id)
+                    arr.push(obj.item.value)
                     this.exportParam.departmentId = arr
                 }
             },