Browse Source

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

QuYueTing 1 week ago
parent
commit
1e0d098cf0

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

@@ -3649,6 +3649,13 @@ public class ReportController {
         return reportService.getTop10ProjectReport(user.getCompanyId(), ymonth);
     }
 
+    //个人参与项目数前十
+    @RequestMapping("/getUserProjectTop10")
+    public HttpRespMsg getUserProjectTop10(String ymonth) {
+        User user = userMapper.selectById(request.getHeader("TOKEN"));
+        return reportService.getUserProjectTop10(user.getCompanyId(), ymonth);
+    }
+
     //项目工时排名前三部门分配表
     @RequestMapping("/getTop3ProjectReportGroupByDept")
     public HttpRespMsg getTop3ProjectReportGroupByDept(String ymonth) {

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskGroupController.java

@@ -434,7 +434,7 @@ public class TaskGroupController {
      * @return
      */
     @RequestMapping("/listMyJoinGroup")
-    public HttpRespMsg listMyJoinGroup(TaskGroup item, @RequestParam(required = false, defaultValue = "0") Integer isSubstitude,@RequestParam String workDate) {
+    public HttpRespMsg listMyJoinGroup(TaskGroup item, @RequestParam(required = false, defaultValue = "0") Integer isSubstitude, String workDate) {
         HttpRespMsg msg = new HttpRespMsg();
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);

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

@@ -233,6 +233,8 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg getTop10ProjectReport(Integer companyId, String ymonth);
 
+    HttpRespMsg getUserProjectTop10(Integer companyId, String ymonth);
+
     HttpRespMsg getTop3ProjectReportGroupByDept(Integer companyId, String ymonth);
 
     HttpRespMsg getProjectReportGroupByDept(Integer companyId, String ymonth);

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

@@ -16150,6 +16150,42 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         return msg;
     }
 
+    @Override
+    public HttpRespMsg getUserProjectTop10(Integer companyId, String ymonth) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String month = normalizeYearMonth(ymonth);
+        List<Map<String, Object>> userProjectList = reportMapper.selectMaps(new QueryWrapper<Report>()
+                .select("creator_id as userId", "count(distinct project_id) as projectCount")
+                .eq("company_id", companyId)
+                .eq("state", 1)
+                .apply("date_format(create_date, '%Y-%m') = {0}", month)
+                .groupBy("creator_id")
+                .orderByDesc("count(distinct project_id)")
+                .last("limit 10"));
+        List<String> userIds = userProjectList.stream()
+                .map(row -> String.valueOf(row.get("userId")))
+                .filter(id -> !StringUtils.isEmpty(id))
+                .collect(Collectors.toList());
+        Map<String, User> userMap = userIds.isEmpty() ? new HashMap<>() : userMapper.selectList(new QueryWrapper<User>()
+                .select("id, name, corpwx_userid")
+                .in("id", userIds))
+                .stream()
+                .collect(Collectors.toMap(User::getId, Function.identity(), (left, right) -> left));
+        List<HashMap> resultList = new ArrayList<>();
+        userProjectList.forEach(row -> {
+            String userId = String.valueOf(row.get("userId"));
+            User user = userMap.get(userId);
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("userId", userId);
+            map.put("userName", user == null ? userId : user.getName());
+            map.put("corpwxUserid", user == null ? null : user.getCorpwxUserid());
+            map.put("projectCount", toInteger(row.get("projectCount")));
+            resultList.add(map);
+        });
+        msg.data = resultList;
+        return msg;
+    }
+
     @Override
     public HttpRespMsg getTop3ProjectReportGroupByDept(Integer companyId, String ymonth) {
         HttpRespMsg msg = new HttpRespMsg();

+ 6 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/DataCollectTask.java

@@ -178,10 +178,10 @@ public class DataCollectTask {
     /**
      * 同步销售订单数据
      */
-    @Scheduled(cron ="0 00 17 * * ?")
+    @Scheduled(cron ="0 30 3 * * ?")
     public void saleOrderData(){
-//        if(isDev){return;}
-//        if(isPrivateDeploy) return;
+        if(isDev){return;}
+        if(isPrivateDeploy) return;
         LocalDate nowDate = LocalDate.now();
         RestTemplate restTemplate = new RestTemplate();
         String getUrl = PREFIX_URL+"/dataCollect/getSaleOrderData";
@@ -212,10 +212,10 @@ public class DataCollectTask {
     /**
      * 同步销售订单数据
      */
-    @Scheduled(cron ="0 00 17 * * ?")
+    @Scheduled(cron ="0 00 3 * * ?")
     public void prdOrderData(){
-//        if(isDev){return;}
-//        if(isPrivateDeploy) return;
+        if(isDev){return;}
+        if(isPrivateDeploy) return;
         LocalDate nowDate = LocalDate.now();
         RestTemplate restTemplate = new RestTemplate();
         String getUrl = PREFIX_URL+"/dataCollect/getProdOrderData";

+ 151 - 50
fhKeeper/formulahousekeeper/timesheet/src/views/dashboard/index.vue

@@ -280,6 +280,27 @@
           class="chart-body project-rank-chart"
         ></div>
       </div>
+
+      <!-- 图表9:个人参与项目数 TOP10 -->
+      <div class="chart-card project-rank-card">
+        <div class="chart-title">
+          <span>个人参与项目数 TOP10</span>
+        </div>
+        <div v-if="loading9" class="chart-loading project-rank-loading">
+          <i class="el-icon-loading"></i> 加载中...
+        </div>
+        <div
+          v-else-if="userProjectTop10Data.length === 0"
+          class="chart-empty project-rank-loading"
+        >
+          暂无数据
+        </div>
+        <div
+          v-show="!loading9 && userProjectTop10Data.length > 0"
+          ref="chart9"
+          class="chart-body project-rank-chart"
+        ></div>
+      </div>
     </div>
     <div
       class="analysis-section"
@@ -369,7 +390,10 @@ export default {
       chart6: null,
       chart7: null,
       chart8: null,
+      chart9: null,
       top3PieMode: "working",
+      userProjectTop10Data: [],
+      loading9: false,
     };
   },
   mounted() {
@@ -379,7 +403,7 @@ export default {
   beforeDestroy() {
     window.removeEventListener("resize", this.handleResize);
     this.removeWorkedProjectScrollListener();
-    for (let i = 1; i <= 8; i++) {
+    for (let i = 1; i <= 9; i++) {
       const chart = this[`chart${i}`];
       if (chart) {
         chart.dispose();
@@ -642,6 +666,7 @@ export default {
       this.loadDeptHours();
       this.loadDeptProjectCount();
       this.loadDashboardAnalysis();
+      this.loadUserProjectTop10();
     },
 
     loadTop10Project() {
@@ -785,6 +810,36 @@ export default {
       );
     },
 
+    loadUserProjectTop10() {
+      this.loading9 = true;
+      this.userProjectTop10Data = [];
+      this.http.post(
+        "/report/getUserProjectTop10",
+        { ymonth: this.selectedMonth },
+        (res) => {
+          this.loading9 = false;
+          if (res.code === "ok") {
+            const data = res.data || [];
+            const processData = (translated) => {
+              this.userProjectTop10Data = translated;
+              this.$nextTick(() => this.renderChart9());
+            };
+            if (this.needTranslateUserName && data.length > 0) {
+              this.translateUserNames(data, "userName", processData);
+            } else {
+              processData(data);
+            }
+          } else {
+            this.$message({ message: res.msg, type: "error" });
+          }
+        },
+        (err) => {
+          this.loading9 = false;
+          this.$message({ message: err, type: "error" });
+        },
+      );
+    },
+
     // ========== 企业微信 openData 处理 ==========
     translateDeptNames(dataList, nameKey, callback) {
       this.translateOpenDataNames(
@@ -1192,60 +1247,35 @@ export default {
       this.chart2 = echarts.init(el, null, { width, height: 380 });
 
       const isOvertime = this.top3PieMode === "overtime";
-      const seriesName = isOvertime ? "部门加班工时分配" : "部门总工时分配";
-      const valueLabel = isOvertime ? "加班工时" : "总工时";
-
-      const pieData = this.top3DeptData.map((item) => ({
-        name: item.departmentName,
-        openId: item._deptOpenId || null,
-        value: isOvertime
-          ? Number(item.overtimeHours || 0)
-          : Number(item.workingTime || 0),
-      }));
+      const names = this.top3DeptData.map((item) => item.departmentName);
+      const workingTimes = this.top3DeptData.map((item) =>
+        Number(item.workingTime || 0)
+      );
+      const overtimeTimes = this.top3DeptData.map((item) =>
+        Number(item.overtimeHours || 0)
+      );
 
-      const deptNameHtml = this.deptNameHtml.bind(this);
-      const needTranslate = this.needWxOpenData;
-      const option = {
-        tooltip: {
-          trigger: "item",
-          enterable: needTranslate,
-          formatter: (params) => {
-            const openId =
-              params.data && params.data.openId ? params.data.openId : null;
-            return (
-              deptNameHtml(params.name, openId) +
-              "<br/>" +
-              valueLabel +
-              ": " +
-              params.value +
-              " h<br/>占比: " +
-              params.percent +
-              "%"
-            );
-          },
-        },
-        legend: { orient: "vertical", left: "left", top: "center" },
-        series: [
+      const deptOpenIdMap2 = {};
+      this.top3DeptData.forEach((item) => {
+        deptOpenIdMap2[item.departmentName] = item._deptOpenId || null;
+      });
+
+      const option = this.buildBarLineOption({
+        names,
+        barSeries: [
           {
-            name: seriesName,
-            type: "pie",
-            radius: ["35%", "65%"],
-            center: ["60%", "50%"],
-            avoidLabelOverlap: true,
-            itemStyle: { borderRadius: 6, borderColor: "#fff", borderWidth: 2 },
-            label: {
-              show: true,
-              formatter: (params) => params.name + "\n" + params.percent + "%",
-            },
-            emphasis: {
-              label: { show: true, fontSize: 14, fontWeight: "bold" },
-            },
-            data: pieData,
+            name: isOvertime ? "加班工时" : "总工时",
+            data: isOvertime ? overtimeTimes : workingTimes,
+            color: isOvertime ? "#FF7F0E" : "#5470C6",
           },
         ],
-      };
+        lineSeries: [],
+        yAxisNames: [isOvertime ? "加班工时" : "总工时", ""],
+        visibleSize: Number.MAX_SAFE_INTEGER,
+        deptOpenIdMap: deptOpenIdMap2,
+      });
       this.chart2.setOption(option);
-      this.chart2.resize();
+      this.chart2.resize({ width, height: 380 });
     },
 
     renderChart3() {
@@ -1636,6 +1666,76 @@ export default {
       this.chart8.resize({ width, height });
     },
 
+    renderChart9() {
+      if (!this.$refs.chart9) return;
+      if (this.chart9) {
+        this.chart9.dispose();
+        this.chart9 = null;
+      }
+      const el = this.$refs.chart9;
+      const parentEl = el.parentElement;
+      const width = parentEl ? parentEl.clientWidth - 32 : el.clientWidth;
+      const height = 380;
+      this.chart9 = echarts.init(el, null, { width, height });
+      const data = this.userProjectTop10Data.slice();
+      const names = data.map(
+        (item) => item.userName || item.userId || "未知人员",
+      );
+      const projectCounts = data.map((item) => Number(item.projectCount || 0));
+      const yAxisMax = Math.ceil(Math.max(...projectCounts, 0) * 1.3) || 10;
+      const userOpenIdMap = {};
+      data.forEach((item) => {
+        userOpenIdMap[item.userName || item.userId] = item.corpwxUserid;
+      });
+      const needTranslate = this.needTranslateUserName;
+
+      const option = {
+        tooltip: {
+          trigger: "axis",
+          axisPointer: { type: "shadow" },
+          enterable: needTranslate,
+          formatter: (params) => {
+            const name = params[0].name;
+            const openId = userOpenIdMap[name];
+            const userName =
+              needTranslate && openId
+                ? `<ww-open-data type="userName" openid="${openId}"></ww-open-data>`
+                : name;
+            return userName + "<br/>参与项目数: " + params[0].value + " 个";
+          },
+        },
+        grid: { left: 64, right: 28, bottom: 120, top: 48 },
+        xAxis: {
+          type: "category",
+          data: names,
+          axisLabel: {
+            rotate: 38,
+            interval: 0,
+            fontSize: 11,
+            margin: 14,
+          },
+        },
+        yAxis: {
+          type: "value",
+          name: "参与项目数(个)",
+          max: yAxisMax,
+          axisLabel: { formatter: "{value} 个" },
+        },
+        series: [
+          {
+            name: "参与项目数",
+            type: "bar",
+            data: projectCounts,
+            barMaxWidth: 32,
+            itemStyle: { color: "#5470C6" },
+            label: this.getBarLabel("个"),
+          },
+        ],
+      };
+      this.chart9.setOption(option);
+      this.chart9.resize({ width, height });
+    },
+
     handleResize() {
       if (this.top10Data.length > 0) this.renderChart1();
       if (this.top3DeptData.length > 0) this.renderChart2();
@@ -1645,6 +1745,7 @@ export default {
       if (this.top3OvertimeProjectDept.length > 0) this.renderChart6();
       if (this.deptOvertimeSummary.length > 0) this.renderChart7();
       if (this.userOvertimeTop10.length > 0) this.renderChart8();
+      if (this.userProjectTop10Data.length > 0) this.renderChart9();
     },
   },
 };

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

@@ -4222,7 +4222,8 @@
                 domain.groupId=null;
                 this.http.post('/task-group/listMyJoinGroup',{ 
                     projectId: domain.projectId,
-                    isSubstitude: this.isSubstitude ? 1 : 0
+                    isSubstitude: this.isSubstitude ? 1 : 0,
+                    workDate: this.workForm.createDate
                 },
                 res => {
                     if (res.code == "ok") {

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

@@ -753,7 +753,7 @@ export default {
       let { data } = await this.getData("/task-group/listMyJoinGroup", {
         projectId,
         isSubstitude: isSubstitude ? 1 : 0,
-        // workDate: this.weekTableData[index].dateTime,
+        workDate: this.weekTableData[index].dateTime,
       });
       if (data.length == 1) {
         this.$set(this.weekTableData[index], "groupId", data[0].id);

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

@@ -398,7 +398,7 @@ export default {
       let { data } = await this.postData("/task-group/listMyJoinGroup", {
         projectId,
         isSubstitude: 1,
-        // workDate: this.filterCriteriaForm.workDate[0],
+        workDate: this.filterCriteriaForm.workDate[0],
       });
       this.taskGroupingList = data;
     },

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

@@ -1447,7 +1447,7 @@ export default {
         getTaskGroups(domainItem, index) {
             domainItem.groupId = null;
             domainItem.groupName = null;
-            this.$axios.post("/task-group/listMyJoinGroup", { projectId: domainItem.projectId })
+            this.$axios.post("/task-group/listMyJoinGroup", { projectId: domainItem.projectId,workDate: this.form.createDate })
                 .then(res => {
                     if (res.code == "ok") {
                         domainItem.taskGroups = res.data;

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

@@ -853,7 +853,7 @@
             //获取项目下的任务分组
             getTaskGroups(domainItem, index) {
                 domainItem.groupId=null;
-                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId})
+                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId,workDate: this.currentForm.createDate})
                     .then(res => {
                         if(res.code == "ok") {
                             domainItem.taskGroups = res.data;

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

@@ -993,7 +993,7 @@
             //获取项目下的任务分组
             getTaskGroups(domainItem, index) {
                 domainItem.groupId=null;
-                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId})
+                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId,workDate: this.currentForm.createDate})
                     .then(res => {
                         if(res.code == "ok") {
                             domainItem.taskGroups = res.data;

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

@@ -1046,7 +1046,7 @@
             //获取项目下的任务分组
             getTaskGroups(domainItem, index) {
                 domainItem.groupId=null;
-                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId})
+                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId,workDate: this.currentForm.createDate})
                     .then(res => {
                         if(res.code == "ok") {
                             domainItem.taskGroups = res.data;

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

@@ -1404,7 +1404,7 @@
             //获取项目下的任务分组
             getTaskGroups(domainItem, index) {
                 domainItem.groupId=null;
-                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId})
+                this.$axios.post("/task-group/listMyJoinGroup", {projectId: domainItem.projectId,workDate: this.currentForm.createDate})
                     .then(res => {
                         if(res.code == "ok") {
                             domainItem.taskGroups = res.data;