|
|
@@ -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();
|
|
|
},
|
|
|
},
|
|
|
};
|