Showing
6 changed files
with
278 additions
and
53 deletions
@@ -75,7 +75,11 @@ interface TrendParamsType { | @@ -75,7 +75,11 @@ interface TrendParamsType { | ||
75 | startTs: number; | 75 | startTs: number; |
76 | endTs: number; | 76 | endTs: number; |
77 | interval: number; | 77 | interval: number; |
78 | - trend: 'CUSTOMER_TREND' | 'TENANT_TREND'; | 78 | + trend: |
79 | + | 'CUSTOMER_TREND' | ||
80 | + | 'TENANT_TREND' | ||
81 | + | 'CUSTOMER_MESSAGE_STATISTICAL' | ||
82 | + | 'CUSTOMER_MESSAGE_STATISTICAL'; | ||
79 | } | 83 | } |
80 | // 获取租户趋势或者客户趋势数据 | 84 | // 获取租户趋势或者客户趋势数据 |
81 | export const getTrendData = (params: TrendParamsType) => { | 85 | export const getTrendData = (params: TrendParamsType) => { |
@@ -8,6 +8,7 @@ import { useMessage } from '/@/hooks/web/useMessage'; | @@ -8,6 +8,7 @@ import { useMessage } from '/@/hooks/web/useMessage'; | ||
8 | * hasBatchDelete: 是否可以删除 | 8 | * hasBatchDelete: 是否可以删除 |
9 | * selectionOptions 表格复选框配置项 | 9 | * selectionOptions 表格复选框配置项 |
10 | * handleDeleteOrBatchDelete 删除方法,适用单一删除和批量删除。参数为null为批量删除 | 10 | * handleDeleteOrBatchDelete 删除方法,适用单一删除和批量删除。参数为null为批量删除 |
11 | + * resetSelectedRowKeys 重置选中的数据 | ||
11 | * } | 12 | * } |
12 | * | 13 | * |
13 | */ | 14 | */ |
@@ -49,6 +50,10 @@ export const useBatchDelete = ( | @@ -49,6 +50,10 @@ export const useBatchDelete = ( | ||
49 | } | 50 | } |
50 | }; | 51 | }; |
51 | 52 | ||
53 | + const resetSelectedRowKeys = () => { | ||
54 | + selectedRowIds.value = []; | ||
55 | + }; | ||
56 | + | ||
52 | const selectionOptions: selectionOptions = { | 57 | const selectionOptions: selectionOptions = { |
53 | rowKey: 'id', | 58 | rowKey: 'id', |
54 | clickToRowSelect: false, | 59 | clickToRowSelect: false, |
@@ -57,5 +62,5 @@ export const useBatchDelete = ( | @@ -57,5 +62,5 @@ export const useBatchDelete = ( | ||
57 | type: 'checkbox', | 62 | type: 'checkbox', |
58 | }, | 63 | }, |
59 | }; | 64 | }; |
60 | - return { hasBatchDelete, selectionOptions, handleDeleteOrBatchDelete }; | 65 | + return { hasBatchDelete, selectionOptions, handleDeleteOrBatchDelete, resetSelectedRowKeys }; |
61 | }; | 66 | }; |
1 | +<!-- 客户消息量或告警统计 --> | ||
2 | +<template> | ||
3 | + <div ref="chartRef" :style="{ height, width }"></div> | ||
4 | +</template> | ||
5 | +<script lang="ts" setup> | ||
6 | + import { ref, Ref, withDefaults, onMounted, watch } from 'vue'; | ||
7 | + import { useECharts } from '/@/hooks/web/useECharts'; | ||
8 | + import { getTrendData } from '/@/api/dashboard'; | ||
9 | + | ||
10 | + interface Props { | ||
11 | + width?: string; | ||
12 | + height?: string; | ||
13 | + customerList?: Array<[number, string]>; | ||
14 | + type: 'CUSTOMER_ALARM_STATISTICAL' | 'CUSTOMER_MESSAGE_STATISTICAL'; | ||
15 | + } | ||
16 | + const props = withDefaults(defineProps<Props>(), { | ||
17 | + width: '100%', | ||
18 | + height: '320px', | ||
19 | + customerList: () => [], | ||
20 | + type: '', | ||
21 | + }); | ||
22 | + watch( | ||
23 | + () => props.customerList, | ||
24 | + (newValue) => { | ||
25 | + const transferResult = newValue.map((item) => [item.ts, item.value]); | ||
26 | + | ||
27 | + setOptions({ | ||
28 | + tooltip: { | ||
29 | + trigger: 'axis', | ||
30 | + axisPointer: { | ||
31 | + lineStyle: { | ||
32 | + width: 1, | ||
33 | + color: '#019680', | ||
34 | + }, | ||
35 | + }, | ||
36 | + }, | ||
37 | + xAxis: { | ||
38 | + type: 'time', | ||
39 | + splitLine: { | ||
40 | + show: true, | ||
41 | + lineStyle: { | ||
42 | + width: 1, | ||
43 | + type: 'solid', | ||
44 | + color: 'rgba(226,226,226,0.5)', | ||
45 | + }, | ||
46 | + }, | ||
47 | + axisTick: { | ||
48 | + show: false, | ||
49 | + }, | ||
50 | + }, | ||
51 | + yAxis: { | ||
52 | + splitArea: { | ||
53 | + show: true, | ||
54 | + areaStyle: { | ||
55 | + color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'], | ||
56 | + }, | ||
57 | + }, | ||
58 | + }, | ||
59 | + grid: { | ||
60 | + left: '1%', | ||
61 | + right: '2%', | ||
62 | + bottom: 0, | ||
63 | + containLabel: true, | ||
64 | + }, | ||
65 | + series: [ | ||
66 | + { | ||
67 | + smooth: true, | ||
68 | + name: '当前趋势', | ||
69 | + type: 'line', | ||
70 | + data: transferResult, | ||
71 | + areaStyle: {}, | ||
72 | + itemStyle: { | ||
73 | + color: '#5ab1ef', | ||
74 | + }, | ||
75 | + }, | ||
76 | + ], | ||
77 | + animationDuration: 1500, | ||
78 | + }); | ||
79 | + } | ||
80 | + ); | ||
81 | + | ||
82 | + const chartRef = ref<HTMLDivElement | null>(null); | ||
83 | + const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); | ||
84 | + onMounted(async () => { | ||
85 | + const endTs = Date.now(); | ||
86 | + const res = await getTrendData({ | ||
87 | + startTs: endTs - 2592000000, | ||
88 | + endTs, | ||
89 | + interval: 86400000, | ||
90 | + trend: props.type, | ||
91 | + }); | ||
92 | + | ||
93 | + const transferResult = res.map((item) => [item.ts, item.value]); | ||
94 | + setOptions({ | ||
95 | + tooltip: { | ||
96 | + trigger: 'axis', | ||
97 | + axisPointer: { | ||
98 | + lineStyle: { | ||
99 | + width: 1, | ||
100 | + color: '#019680', | ||
101 | + }, | ||
102 | + }, | ||
103 | + }, | ||
104 | + xAxis: { | ||
105 | + type: 'time', | ||
106 | + splitLine: { | ||
107 | + show: true, | ||
108 | + lineStyle: { | ||
109 | + width: 1, | ||
110 | + type: 'solid', | ||
111 | + color: 'rgba(226,226,226,0.5)', | ||
112 | + }, | ||
113 | + }, | ||
114 | + axisTick: { | ||
115 | + show: false, | ||
116 | + }, | ||
117 | + }, | ||
118 | + | ||
119 | + yAxis: { | ||
120 | + splitArea: { | ||
121 | + show: true, | ||
122 | + areaStyle: { | ||
123 | + color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'], | ||
124 | + }, | ||
125 | + }, | ||
126 | + }, | ||
127 | + grid: { | ||
128 | + left: '1%', | ||
129 | + right: '2%', | ||
130 | + bottom: 0, | ||
131 | + containLabel: true, | ||
132 | + }, | ||
133 | + series: [ | ||
134 | + { | ||
135 | + smooth: true, | ||
136 | + name: '当前趋势', | ||
137 | + type: 'line', | ||
138 | + data: transferResult, | ||
139 | + areaStyle: {}, | ||
140 | + itemStyle: { | ||
141 | + color: '#5ab1ef', | ||
142 | + }, | ||
143 | + }, | ||
144 | + ], | ||
145 | + animationDuration: 1500, | ||
146 | + }); | ||
147 | + }); | ||
148 | +</script> |
@@ -10,21 +10,40 @@ | @@ -10,21 +10,40 @@ | ||
10 | <div class="extra-date"> | 10 | <div class="extra-date"> |
11 | <template v-for="(item, index) in dateList" :key="item.value"> | 11 | <template v-for="(item, index) in dateList" :key="item.value"> |
12 | <span | 12 | <span |
13 | - @click="quickQueryDate(index, item.value)" | 13 | + @click="quickQueryDate(index, item.value, role === 'CUSTOMER_USER' && 'customer')" |
14 | :class="{ active: index === activeIndex }" | 14 | :class="{ active: index === activeIndex }" |
15 | >{{ item.label }}</span | 15 | >{{ item.label }}</span |
16 | > | 16 | > |
17 | </template> | 17 | </template> |
18 | - <DatePicker @change="onDateChange" v-model:value="dateValue" /> | 18 | + <DatePicker |
19 | + @change=" | ||
20 | + (_, DateString) => onDateChange(_, DateString, role === 'CUSTOMER_USER' && 'customer') | ||
21 | + " | ||
22 | + v-model:value="dateValue" | ||
23 | + /> | ||
19 | </div> | 24 | </div> |
20 | </template> | 25 | </template> |
21 | <div v-if="activeKey === '1'"> | 26 | <div v-if="activeKey === '1'"> |
22 | <!-- 折线图 --> | 27 | <!-- 折线图 --> |
23 | - <VisitAnalysis :alarmList="state.alarmList" /> | 28 | + <CustomerAlarmMessage |
29 | + v-if="role === 'CUSTOMER_USER'" | ||
30 | + type="CUSTOMER_ALARM_STATISTICAL" | ||
31 | + :customerList="customerAlarmList" | ||
32 | + /> | ||
33 | + <VisitAnalysis v-else :alarmList="state.alarmList" /> | ||
24 | </div> | 34 | </div> |
25 | <div v-if="activeKey === '2'"> | 35 | <div v-if="activeKey === '2'"> |
26 | <!-- 柱形图 --> | 36 | <!-- 柱形图 --> |
27 | - <VisitAnalysisBar :dataPointList="state.dataPointList" :messageList="state.messageList" /> | 37 | + <CustomerAlarmMessage |
38 | + v-if="role === 'CUSTOMER_USER'" | ||
39 | + type="CUSTOMER_MESSAGE_STATISTICAL" | ||
40 | + :customerList="customerMessageList" | ||
41 | + /> | ||
42 | + <VisitAnalysisBar | ||
43 | + v-else | ||
44 | + :dataPointList="state.dataPointList" | ||
45 | + :messageList="state.messageList" | ||
46 | + /> | ||
28 | </div> | 47 | </div> |
29 | </Card> | 48 | </Card> |
30 | <div v-if="isAdmin(role)"> | 49 | <div v-if="isAdmin(role)"> |
@@ -62,7 +81,7 @@ | @@ -62,7 +81,7 @@ | ||
62 | <div class="extra-date"> | 81 | <div class="extra-date"> |
63 | <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> | 82 | <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> |
64 | <span | 83 | <span |
65 | - @click="quickQueryTenantOrCustomerTime(index, item.value, 'customer')" | 84 | + @click="quickQueryTenantOrCustomerTime(index, item.value)" |
66 | :class="{ active: index === activeCustomerIndex }" | 85 | :class="{ active: index === activeCustomerIndex }" |
67 | >{{ item.label }}</span | 86 | >{{ item.label }}</span |
68 | > | 87 | > |
@@ -79,7 +98,7 @@ | @@ -79,7 +98,7 @@ | ||
79 | </div> | 98 | </div> |
80 | </template> | 99 | </template> |
81 | <script lang="ts" setup> | 100 | <script lang="ts" setup> |
82 | - import { ref, reactive } from 'vue'; | 101 | + import { ref, reactive, onMounted } from 'vue'; |
83 | import { Card, DatePicker } from 'ant-design-vue'; | 102 | import { Card, DatePicker } from 'ant-design-vue'; |
84 | import VisitAnalysis from './VisitAnalysis.vue'; | 103 | import VisitAnalysis from './VisitAnalysis.vue'; |
85 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; | 104 | import VisitAnalysisBar from './VisitAnalysisBar.vue'; |
@@ -91,7 +110,10 @@ | @@ -91,7 +110,10 @@ | ||
91 | import { getEntitiesId } from '/@/api/dashboard/index'; | 110 | import { getEntitiesId } from '/@/api/dashboard/index'; |
92 | import CustomerTrend from './CustomerTrend.vue'; | 111 | import CustomerTrend from './CustomerTrend.vue'; |
93 | import TenantTrend from './TenantTrend.vue'; | 112 | import TenantTrend from './TenantTrend.vue'; |
113 | + import CustomerAlarmMessage from './CustomerAlarmMessage.vue'; | ||
94 | import { useDate } from '../hooks/useDate'; | 114 | import { useDate } from '../hooks/useDate'; |
115 | + import { getTrendData } from '/@/api/dashboard'; | ||
116 | + | ||
95 | defineExpose({ | 117 | defineExpose({ |
96 | isAdmin, | 118 | isAdmin, |
97 | }); | 119 | }); |
@@ -330,32 +352,56 @@ | @@ -330,32 +352,56 @@ | ||
330 | send(sendValue); | 352 | send(sendValue); |
331 | } | 353 | } |
332 | // 选择日期 | 354 | // 选择日期 |
333 | - function onDateChange(_, dateString) { | 355 | + async function onDateChange(_, dateString, roleType = '') { |
356 | + console.log(_, dateString, roleType); | ||
334 | activeIndex.value = -1; | 357 | activeIndex.value = -1; |
335 | const dateTime = Number(formatToDateTime(dateString, 'x')); | 358 | const dateTime = Number(formatToDateTime(dateString, 'x')); |
336 | - // 动态发送ws数据 | ||
337 | - const sendValue = JSON.stringify({ | ||
338 | - entityDataCmds: [ | ||
339 | - { | ||
340 | - cmdId: activeKey.value, | ||
341 | - historyCmd: { | ||
342 | - keys: | ||
343 | - activeKey.value === '1' | ||
344 | - ? ['createdAlarmsCountHourly'] | ||
345 | - : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | ||
346 | - startTs: dateTime, | ||
347 | - endTs: dateTime + 86400000, | ||
348 | - interval: 7200000, | ||
349 | - limit: 12, | ||
350 | - agg: 'SUM', | 359 | + if (!dateString) return; |
360 | + if (roleType === 'customer') { | ||
361 | + if (activeKey.value === '1') { | ||
362 | + const data = await getTrendData({ | ||
363 | + startTs: dateTime, | ||
364 | + endTs: dateTime + 86400000, | ||
365 | + interval: 7200000, | ||
366 | + trend: 'CUSTOMER_ALARM_STATISTICAL', | ||
367 | + }); | ||
368 | + customerAlarmList.value = data; | ||
369 | + } else if (activeKey.value === '2') { | ||
370 | + const data = await getTrendData({ | ||
371 | + startTs: dateTime, | ||
372 | + endTs: dateTime + 86400000, | ||
373 | + interval: 7200000, | ||
374 | + trend: 'CUSTOMER_MESSAGE_STATISTICAL', | ||
375 | + }); | ||
376 | + customerMessageList.value = data; | ||
377 | + } | ||
378 | + } else { | ||
379 | + // 动态发送ws数据 | ||
380 | + const sendValue = JSON.stringify({ | ||
381 | + entityDataCmds: [ | ||
382 | + { | ||
383 | + cmdId: activeKey.value, | ||
384 | + historyCmd: { | ||
385 | + keys: | ||
386 | + activeKey.value === '1' | ||
387 | + ? ['createdAlarmsCountHourly'] | ||
388 | + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | ||
389 | + startTs: dateTime, | ||
390 | + endTs: dateTime + 86400000, | ||
391 | + interval: 7200000, | ||
392 | + limit: 12, | ||
393 | + agg: 'SUM', | ||
394 | + }, | ||
351 | }, | 395 | }, |
352 | - }, | ||
353 | - ], | ||
354 | - }); | ||
355 | - send(sendValue); | 396 | + ], |
397 | + }); | ||
398 | + send(sendValue); | ||
399 | + } | ||
356 | } | 400 | } |
401 | + const customerAlarmList = ref([]); | ||
402 | + const customerMessageList = ref([]); | ||
357 | // 快速选择时间 | 403 | // 快速选择时间 |
358 | - function quickQueryDate(index: number, value: number) { | 404 | + async function quickQueryDate(index: number, value: number, roleType = '') { |
359 | if (activeIndex.value === index) return; | 405 | if (activeIndex.value === index) return; |
360 | activeIndex.value = index; | 406 | activeIndex.value = index; |
361 | dateValue.value = ''; | 407 | dateValue.value = ''; |
@@ -365,26 +411,49 @@ | @@ -365,26 +411,49 @@ | ||
365 | } else if (value === 604800000 || value === 2592000000) { | 411 | } else if (value === 604800000 || value === 2592000000) { |
366 | interval = 86400000; | 412 | interval = 86400000; |
367 | } | 413 | } |
368 | - // 动态发送ws数据 | ||
369 | - const sendValue = JSON.stringify({ | ||
370 | - entityDataCmds: [ | ||
371 | - { | ||
372 | - cmdId: activeKey.value, | ||
373 | - historyCmd: { | ||
374 | - keys: | ||
375 | - activeKey.value === '1' | ||
376 | - ? ['createdAlarmsCountHourly'] | ||
377 | - : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | ||
378 | - startTs: Date.now() - value, | ||
379 | - endTs: Date.now(), | ||
380 | - interval, | ||
381 | - agg: 'SUM', | 414 | + if (roleType === 'customer') { |
415 | + if (activeKey.value === '1') { | ||
416 | + const data = await getTrendData({ | ||
417 | + startTs: Date.now() - value, | ||
418 | + endTs: Date.now(), | ||
419 | + interval, | ||
420 | + trend: 'CUSTOMER_ALARM_STATISTICAL', | ||
421 | + }); | ||
422 | + customerAlarmList.value = data; | ||
423 | + } else if (activeKey.value === '2') { | ||
424 | + const data = await getTrendData({ | ||
425 | + startTs: Date.now() - value, | ||
426 | + endTs: Date.now(), | ||
427 | + interval, | ||
428 | + trend: 'CUSTOMER_MESSAGE_STATISTICAL', | ||
429 | + }); | ||
430 | + customerMessageList.value = data; | ||
431 | + } | ||
432 | + } else { | ||
433 | + // 动态发送ws数据 | ||
434 | + const sendValue = JSON.stringify({ | ||
435 | + entityDataCmds: [ | ||
436 | + { | ||
437 | + cmdId: activeKey.value, | ||
438 | + historyCmd: { | ||
439 | + keys: | ||
440 | + activeKey.value === '1' | ||
441 | + ? ['createdAlarmsCountHourly'] | ||
442 | + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | ||
443 | + startTs: Date.now() - value, | ||
444 | + endTs: Date.now(), | ||
445 | + interval, | ||
446 | + agg: 'SUM', | ||
447 | + }, | ||
382 | }, | 448 | }, |
383 | - }, | ||
384 | - ], | ||
385 | - }); | ||
386 | - send(sendValue); | 449 | + ], |
450 | + }); | ||
451 | + send(sendValue); | ||
452 | + } | ||
387 | } | 453 | } |
454 | + onMounted(() => { | ||
455 | + console.log(props.role); | ||
456 | + }); | ||
388 | 457 | ||
389 | const { | 458 | const { |
390 | tenantDateValue, | 459 | tenantDateValue, |
@@ -70,10 +70,8 @@ | @@ -70,10 +70,8 @@ | ||
70 | name: 'LinkEdge', | 70 | name: 'LinkEdge', |
71 | components: { BasicTable, SceneLinkAgeDrawer, TableAction, Switch }, | 71 | components: { BasicTable, SceneLinkAgeDrawer, TableAction, Switch }, |
72 | setup() { | 72 | setup() { |
73 | - const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions } = useBatchDelete( | ||
74 | - screenLinkPageDeleteApi, | ||
75 | - handleSuccess | ||
76 | - ); | 73 | + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } = |
74 | + useBatchDelete(screenLinkPageDeleteApi, handleSuccess); | ||
77 | selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { | 75 | selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { |
78 | // Demo:status为1的选择框禁用 | 76 | // Demo:status为1的选择框禁用 |
79 | if (record.status === 1) { | 77 | if (record.status === 1) { |
@@ -87,7 +85,7 @@ | @@ -87,7 +85,7 @@ | ||
87 | const userId = userInfo.userId; | 85 | const userId = userInfo.userId; |
88 | const role: string = userInfo.roles[0]; | 86 | const role: string = userInfo.roles[0]; |
89 | const [registerDrawer, { openDrawer }] = useDrawer(); | 87 | const [registerDrawer, { openDrawer }] = useDrawer(); |
90 | - const [registerTable, { reload, setProps }] = useTable({ | 88 | + const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({ |
91 | title: '场景联动列表', | 89 | title: '场景联动列表', |
92 | api: screenLinkPageGetApi, | 90 | api: screenLinkPageGetApi, |
93 | columns, | 91 | columns, |
@@ -136,6 +134,8 @@ | @@ -136,6 +134,8 @@ | ||
136 | setProps({ | 134 | setProps({ |
137 | loading: true, | 135 | loading: true, |
138 | }); | 136 | }); |
137 | + setSelectedRowKeys([]); | ||
138 | + resetSelectedRowKeys(); | ||
139 | const newStatus = checked ? 1 : 0; | 139 | const newStatus = checked ? 1 : 0; |
140 | const { createMessage } = useMessage(); | 140 | const { createMessage } = useMessage(); |
141 | try { | 141 | try { |
@@ -87,7 +87,6 @@ | @@ -87,7 +87,6 @@ | ||
87 | const { setLoginState, getLoginState } = useLoginState(); | 87 | const { setLoginState, getLoginState } = useLoginState(); |
88 | const { getFormRules } = useFormRules(); | 88 | const { getFormRules } = useFormRules(); |
89 | const storage = createLocalStorage(); | 89 | const storage = createLocalStorage(); |
90 | - const visible = ref(false); | ||
91 | const formRef = ref(); | 90 | const formRef = ref(); |
92 | const loading = ref(false); | 91 | const loading = ref(false); |
93 | const rememberMe = ref(false); | 92 | const rememberMe = ref(false); |