Browse Source

新增项目统计分析图表

余思翰 6 months ago
parent
commit
7c4a6b48c3

+ 10
- 0
oa-ui/src/api/oa/contract/contract.js View File

@@ -42,3 +42,13 @@ export function delContract(contractId) {
42 42
     method: 'delete'
43 43
   })
44 44
 }
45
+
46
+
47
+// 合同统计
48
+export function getContractStatistic(query) {
49
+  return request({
50
+    url: '/oa/contract/statistic',
51
+    method: 'get',
52
+    params: query
53
+  })
54
+}

+ 1
- 0
oa-ui/src/utils/echarts.js View File

@@ -24,4 +24,5 @@ export function ehcartsInit(myCharts,id,options){
24 24
   myCharts = echarts.init(chartDom);
25 25
   let option = options;
26 26
   echartsSetoption(myCharts, option);
27
+  return myCharts
27 28
 }

+ 8
- 4
oa-ui/src/views/flowable/form/finance/borrowForm.vue View File

@@ -615,6 +615,8 @@ export default {
615 615
       if (this.formTotal == 0) {
616 616
         this.form.borrowId = this.taskForm.formId;
617 617
         await addBorrow(this.form)
618
+        // 更新借款明细项
619
+        await delBorrowDetail(this.taskForm.formId)
618 620
         for (let detail of this.detailList) {
619 621
           detail.borrowId = this.taskForm.formId;
620 622
           await addBorrowDetail(detail);
@@ -701,10 +703,12 @@ export default {
701 703
               }
702 704
             })
703 705
           } else {
704
-            for (let detail of this.detailList) {
705
-              detail.borrowId = this.taskForm.formId;
706
-              addBorrowDetail(detail);
707
-            }
706
+            delBorrowDetail(this.taskForm.formId).then(() => {
707
+              for (let detail of this.detailList) {
708
+                detail.borrowId = this.taskForm.formId;
709
+                addBorrowDetail(detail);
710
+              }
711
+            })
708 712
             this.form.borrowId = this.taskForm.formId;
709 713
             addBorrow(this.form).then(response => {
710 714
               this.$modal.msgSuccess("新增成功");

+ 0
- 21
oa-ui/src/views/statistics/components/content.vue View File

@@ -1,21 +0,0 @@
1
-<!--
2
- * @Author: ysh
3
- * @Date: 2024-10-11 16:41:17
4
- * @LastEditors: 
5
- * @LastEditTime: 2024-10-11 16:41:28
6
--->
7
-<template>
8
-  <div>
9
-    
10
-  </div>
11
-</template>
12
-
13
-<script>
14
-  export default {
15
-    
16
-  }
17
-</script>
18
-
19
-<style lang="scss" scoped>
20
-
21
-</style>

+ 396
- 0
oa-ui/src/views/statistics/components/projectStatistics.vue View File

@@ -0,0 +1,396 @@
1
+<!--
2
+ * @Author: ysh
3
+ * @Date: 2024-10-11 16:41:17
4
+ * @LastEditors: Please set LastEditors
5
+ * @LastEditTime: 2024-10-12 17:00:05
6
+-->
7
+<template>
8
+  <div style="width:100%;" v-loading="loading">
9
+    <div class="titles">项目统计</div>
10
+    <div class="warpper">
11
+      <div class="left">
12
+        <div class="bar" id="yearNumber"></div>
13
+        <div class="pie-box" v-loading="dataLoading">
14
+          <div class="sourcePie" id="sourceNumber"></div>
15
+          <div class="deptPie" id="deptNumber"></div>
16
+        </div>
17
+      </div>
18
+      <div class="middle" v-loading="dataLoading">
19
+        <div class="typeBar" id="typeNumber"></div>
20
+      </div>
21
+      <div class="right">
22
+      </div>
23
+    </div>
24
+  </div>
25
+</template>
26
+
27
+<script>
28
+import * as echarts from 'echarts'
29
+import { disposeCharts, echartsSetoption, ehcartsInit } from '@/utils/echarts'
30
+import { getProjectStatistic } from '@/api/oa/project/project'
31
+let barYearChart, sourceChart, deptChart, typeChart
32
+export default {
33
+  data() {
34
+    return {
35
+      year: {},
36
+      type: {},
37
+      source: {},
38
+      dept: {},
39
+      loading: true,
40
+      activeYear: '',
41
+      dataLoading: false,
42
+    }
43
+  },
44
+  watch: {
45
+    year() {
46
+      this.initChartBar();
47
+    },
48
+    source() {
49
+      this.initChartSource(this.activeYear);
50
+    },
51
+    dept() {
52
+      this.initChartDept(this.activeYear);
53
+    },
54
+    type() {
55
+      this.initTypeBar(this.activeYear);
56
+    }
57
+  },
58
+  created() {
59
+    this.initDatas();
60
+  },
61
+  mounted() {
62
+
63
+  },
64
+  methods: {
65
+    async initDatas() {
66
+      this.loading = true;
67
+      let res = await getProjectStatistic();
68
+      if (res.data) {
69
+        this.year = res.data.year[0];
70
+        this.type = res.data.type[0];
71
+        this.source = res.data.source[0];
72
+        this.dept = res.data.dept[0];
73
+        this.loading = false;
74
+      }
75
+    },
76
+    initChartBar() {
77
+      let option = {
78
+        title: {
79
+          text: '每年项目个数统计',
80
+          subtext: '共计项目:' + Object.values(this.year).reduce((accumulator, currentValue) => accumulator + currentValue, 0) + '个'
81
+        },
82
+        grid: {
83
+          left: '1%',
84
+          right: '1%',
85
+          bottom: '0%',
86
+          height: '60%',
87
+          containLabel: true
88
+        },
89
+        graphic: [
90
+          {
91
+            type: 'text',
92
+            name: '',
93
+            right: '0',
94
+            top: '20%',
95
+            style: {
96
+              text: '全部年份',
97
+              fill: '#000', // 文本颜色  
98
+              fontSize: 12, // 文本大小  
99
+              fontWeight: '' // 文本粗细  
100
+            }
101
+          }
102
+        ],
103
+        tooltip: {
104
+          trigger: 'axis',
105
+          axisPointer: {
106
+            type: 'none',
107
+            label: {
108
+              backgroundColor: '#6a7985'
109
+            }
110
+          }
111
+        },
112
+        xAxis: {
113
+          name: '年',
114
+          type: 'category',
115
+          data: Object.keys(this.year),
116
+        },
117
+        yAxis: {
118
+          type: 'value',
119
+          name: '单位:个'
120
+        },
121
+        series: [
122
+          {
123
+            data: Object.values(this.year),
124
+            type: 'bar',
125
+            smooth: true,
126
+            label: {
127
+              show: true,
128
+              position: 'top'
129
+            },
130
+            itemStyle: {
131
+              color: '#1C3F95'
132
+            }
133
+          },
134
+          {
135
+            type: 'line',
136
+            data: Object.values(this.year),
137
+            smooth: true,
138
+            itemStyle: {
139
+              color: '#F89B30'
140
+            }
141
+          }
142
+        ]
143
+      };
144
+      let charts = ehcartsInit(barYearChart, 'yearNumber', option);
145
+      this.clickYear(charts)
146
+    },
147
+    clickYear(charts) {
148
+      let that = this;
149
+      charts.on('click', function (event) {
150
+        console.log(event);
151
+
152
+        if (event) {
153
+          that.activeYear = event.name;
154
+          let projectNumber = event.name
155
+          if (event.name == "") {
156
+            projectNumber = ''
157
+          }
158
+          that.dataLoading = true
159
+          getProjectStatistic({ projectNumber }).then(res => {
160
+            that.type = res.data.type[0];
161
+            that.source = res.data.source[0];
162
+            that.dept = res.data.dept[0];
163
+            that.dataLoading = false
164
+          })
165
+        }
166
+      })
167
+    },
168
+    initChartSource(year) {
169
+      let option = {
170
+        title: {
171
+          text: year + '项目来源分布',
172
+        },
173
+        tooltip: {
174
+          trigger: 'item',
175
+          formatter: '{a} <br/>{b} : {c} ({d}%)'
176
+        },
177
+        legend: {
178
+          top: 'bottom'
179
+        },
180
+        series: [
181
+          {
182
+            name: '占比',
183
+            type: 'pie',
184
+            radius: '40%',
185
+            avoidLabelOverlap: false,
186
+            data: Object.entries(this.source).map(([key, value]) => {
187
+              return { name: key, value: value }
188
+            }),
189
+            emphasis: {
190
+              itemStyle: {
191
+                shadowBlur: 10,
192
+                shadowOffsetX: 0,
193
+                shadowColor: 'rgba(0, 0, 0, 0.5)'
194
+              }
195
+            },
196
+            label: {
197
+              alignTo: 'none',
198
+              formatter: '{name|{b}}\n{value|{c}个}',
199
+              minMargin: 5,
200
+              edgeDistance: 8,
201
+              lineHeight: 15,
202
+              rich: {
203
+                time: {
204
+                  fontSize: 10,
205
+                  color: '#999'
206
+                }
207
+              }
208
+            },
209
+          }
210
+        ]
211
+      };
212
+      ehcartsInit(sourceChart, 'sourceNumber', option);
213
+    },
214
+    initChartDept(year) {
215
+      let option = {
216
+        title: {
217
+          text: year + '各部门承担项目情况',
218
+        },
219
+        tooltip: {
220
+          trigger: 'item',
221
+          formatter: '{a} <br/>{b} : {c} ({d}%)'
222
+        },
223
+        legend: {
224
+          bottom: '0',
225
+          textStyle: {
226
+            fontSize: 9
227
+          }
228
+        },
229
+        series: [
230
+          {
231
+            name: '占比',
232
+            type: 'pie',
233
+            radius: '40%',
234
+            center: ['50%', '40%'],
235
+            data: Object.entries(this.dept).map(([key, value]) => {
236
+              return { name: key, value: value }
237
+            }),
238
+            emphasis: {
239
+              itemStyle: {
240
+                shadowBlur: 10,
241
+                shadowOffsetX: 0,
242
+                shadowColor: 'rgba(0, 0, 0, 0.5)'
243
+              }
244
+            },
245
+            label: {
246
+              alignTo: 'edge',
247
+              formatter: '{name|{b}}\n{value|{c}个}',
248
+              minMargin: 5,
249
+              edgeDistance: 3,
250
+              lineHeight: 15,
251
+              rich: {
252
+                time: {
253
+                  fontSize: 10,
254
+                  color: '#999'
255
+                }
256
+              }
257
+            },
258
+          }
259
+        ]
260
+      };
261
+      ehcartsInit(deptChart, 'deptNumber', option);
262
+    },
263
+    initTypeBar(year) {
264
+      let option = {
265
+        title: {
266
+          text: year + '项目类型统计'
267
+        },
268
+        tooltip: {
269
+          trigger: 'axis',
270
+          axisPointer: {
271
+            type: 'shadow'
272
+          }
273
+        },
274
+        toolbox: {
275
+          feature: {
276
+            dataView: {
277
+              show: true,
278
+              optionToContent: function (opt) {
279
+                var axisData = opt.yAxis[0].data;
280
+                var series = opt.series;
281
+                var table = '<table border="1" style="width:100%;text-align:center;border-collapse: collapse;"><tbody><tr>'
282
+                  + '<td>项目类型</td>'
283
+                  + '<td>数量</td>'
284
+                  + '</tr>';
285
+                for (var i = 0, l = axisData.length; i < l; i++) {
286
+                  table += '<tr>'
287
+                    + '<td>' + axisData[i] + '</td>'
288
+                    + '<td>' + series[0].data[i] + '</td>'
289
+                    + '</tr>';
290
+                }
291
+                table += '</tbody></table>';
292
+                return table;
293
+              }
294
+            }
295
+          }
296
+        },
297
+        dataZoom: [
298
+          {
299
+            type: 'inside',
300
+            start: 0,
301
+            end: 100,
302
+            show: true,
303
+            yAxisIndex: 0,
304
+          }
305
+        ],
306
+        grid: {
307
+          left: '3%',
308
+          right: '8%',
309
+          bottom: '3%',
310
+          containLabel: true
311
+        },
312
+        xAxis: {
313
+          type: 'value',
314
+        },
315
+        yAxis: {
316
+          type: 'category',
317
+          data: Object.keys(this.type),
318
+          axisLabel: {
319
+            fontSize: 9,
320
+            width: 50,
321
+            overflow: 'truncate',
322
+          }
323
+        },
324
+        series: [
325
+          {
326
+            type: 'bar',
327
+            data: Object.values(this.type)
328
+          }
329
+        ]
330
+      };
331
+      ehcartsInit(typeChart, 'typeNumber', option);
332
+    },
333
+  },
334
+}
335
+</script>
336
+
337
+<style lang="scss" scoped>
338
+.titles {
339
+  font-family: 'puhuiti';
340
+  font-size: 16px;
341
+  margin-bottom: 16px;
342
+}
343
+
344
+.warpper {
345
+  width: 100%;
346
+  height: 100%;
347
+  display: flex;
348
+
349
+  .left {
350
+    flex: 1.5;
351
+
352
+    .bar {
353
+      width: 100%;
354
+      height: 300px;
355
+      padding: 10px;
356
+      border: 1px solid #ececec;
357
+    }
358
+
359
+    .pie-box {
360
+      width: 100%;
361
+      height: 300px;
362
+      display: flex;
363
+    }
364
+
365
+    .sourcePie {
366
+      flex: 1;
367
+      height: 300px;
368
+      padding: 10px;
369
+      border: 1px solid #ececec;
370
+    }
371
+
372
+    .deptPie {
373
+      flex: 2;
374
+      height: 300px;
375
+      padding: 10px;
376
+      border: 1px solid #ececec;
377
+    }
378
+  }
379
+
380
+  .middle {
381
+    flex: 1;
382
+
383
+    .typeBar {
384
+      width: 100%;
385
+      height: 600px;
386
+      padding: 10px;
387
+      border: 1px solid #ececec;
388
+    }
389
+  }
390
+
391
+  .right {
392
+    flex: 1;
393
+    display: flex;
394
+  }
395
+}
396
+</style>

+ 22
- 11
oa-ui/src/views/statistics/components/topHead.vue View File

@@ -1,8 +1,8 @@
1 1
 <template>
2
-  <div>
2
+  <div v-loading="loading">
3 3
     <div class="titles">整体统计</div>
4 4
     <div class="item-wapper">
5
-      <div class="one item-box">
5
+      <div class="one item-box" @click="$emit('handleClick', 'project')">
6 6
         <div class="data-left">
7 7
           <div class="data-title">{{ thisYear }}年项目总数</div>
8 8
           <div class="number">{{ projectNum.toLocaleString('en-US') }}</div>
@@ -68,6 +68,7 @@ import downSrc from '@/assets/icons/down.png'
68 68
 import * as echarts from 'echarts'
69 69
 import { disposeCharts, echartsSetoption, ehcartsInit } from '@/utils/echarts'
70 70
 import { getProjectStatistic } from '@/api/oa/project/project'
71
+import { getContractStatistic } from '@/api/oa/contract/contract'
71 72
 let thisYearChart
72 73
 export default {
73 74
   data() {
@@ -81,7 +82,8 @@ export default {
81 82
       type: {},
82 83
       source: {},
83 84
       dept: {},
84
-      subYear: 0
85
+      subYear: 0,
86
+      loading: true
85 87
     }
86 88
   },
87 89
   watch: {
@@ -97,16 +99,22 @@ export default {
97 99
   },
98 100
   methods: {
99 101
     async initDatas() {
100
-      let res = await getProjectStatistic();
101
-      console.log(res);
102
-      if (res.data) {
103
-        this.year = res.data.year[0];
104
-        this.type = res.data.type[0];
105
-        this.source = res.data.source[0];
106
-        this.dept = res.data.dept[0];
102
+      this.loading = true
103
+      let res1 = await getProjectStatistic();
104
+      if (res1.data) {
105
+        this.year = res1.data.year[0];
106
+        this.type = res1.data.type[0];
107
+        this.source = res1.data.source[0];
108
+        this.dept = res1.data.dept[0];
107 109
         // this.initChartOne();
108 110
         this.getProjectYearSub(this.year);
109 111
       }
112
+      let res2 = await getContractStatistic();
113
+      if(res2.data){
114
+        console.log(res2.data);
115
+        
116
+      }
117
+      this.loading = false
110 118
     },
111 119
     initChartOne() {
112 120
       let option = {
@@ -168,14 +176,17 @@ export default {
168 176
   width: 100%;
169 177
   display: flex;
170 178
 }
179
+
171 180
 .titles {
172 181
   font-family: 'puhuiti';
173 182
   font-size: 16px;
174 183
   margin-bottom: 16px;
175 184
 }
176
-.item-box:hover{
185
+
186
+.item-box:hover {
177 187
   box-shadow: 0 0 15px #e7e5e5;
178 188
 }
189
+
179 190
 .item-box {
180 191
   flex: 1;
181 192
   margin: 0 10px;

+ 19
- 4
oa-ui/src/views/statistics/index.vue View File

@@ -2,31 +2,37 @@
2 2
  * @Author: ysh
3 3
  * @Date: 2024-10-11 09:23:15
4 4
  * @LastEditors: Please set LastEditors
5
- * @LastEditTime: 2024-10-11 16:30:17
5
+ * @LastEditTime: 2024-10-12 09:38:18
6 6
 -->
7 7
 <template>
8 8
   <div class="app-container bg">
9 9
     <div class="head">
10
-      <top-head></top-head>
10
+      <top-head @handleClick="changeClick"></top-head>
11 11
     </div>
12 12
     <div class="content">
13
-      
13
+      <project-statistics v-if="isActive = 'project'"></project-statistics>
14 14
     </div>
15 15
   </div>
16 16
 </template>
17 17
 
18 18
 <script>
19
+import ProjectStatistics from './components/projectStatistics.vue'
19 20
 import topHead from './components/topHead.vue'
20 21
 export default {
21
-  components: { topHead },
22
+  components: { topHead, ProjectStatistics },
22 23
   data() {
23 24
     return {
25
+      isActive: 'project'
24 26
     }
25 27
   },
26 28
   mounted() {
27 29
   },
28 30
   methods: {
31
+    changeClick(val) {
32
+      this.isActive = val
33
+      console.log(val);
29 34
 
35
+    }
30 36
   },
31 37
 }
32 38
 </script>
@@ -42,4 +48,13 @@ export default {
42 48
   border-radius: 4px;
43 49
   padding: 20px;
44 50
 }
51
+
52
+.content {
53
+  margin-top: 20px;
54
+  background-color: #fff;
55
+  min-height: 570px;
56
+  border-radius: 4px;
57
+  padding: 20px;
58
+}
59
+
45 60
 </style>

Loading…
Cancel
Save