Commit 9dbe312e79de5d361c0ff67445176acfe12454de
Merge branch 'local_dev_ft' into 'main'
feat:首页新增饼状图和调整部分样式,待联调服务端。 See merge request huang/yun-teng-iot-front!444
Showing
8 changed files
with
855 additions
and
12 deletions
src/assets/images/product.png
0 → 100644
3.99 KB
... | ... | @@ -131,6 +131,62 @@ |
131 | 131 | 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div |
132 | 132 | > |
133 | 133 | </Card> |
134 | + | |
135 | + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-4" style="color: #666"> | |
136 | + <div class="flex" style="height: 100px"> | |
137 | + <div class="mr-4"> | |
138 | + <img | |
139 | + v-if="!isAdmin(role)" | |
140 | + src="/src/assets/images/product.png" | |
141 | + style="width: 5.625rem; height: 5.625rem" | |
142 | + /> | |
143 | + <img | |
144 | + v-else | |
145 | + src="/src/assets/images/product.png" | |
146 | + style="width: 5.625rem; height: 5.625rem" | |
147 | + /> | |
148 | + </div> | |
149 | + <div class="flex-auto"> | |
150 | + <div class="flex justify-between" style="align-items: center"> | |
151 | + <div v-if="!isAdmin(role)" style="font-size: 1.625rem; color: #333; font-weight: bold"> | |
152 | + <CountTo | |
153 | + v-if="growCardList?.messageInfo?.messageCount" | |
154 | + :end-val="growCardList.messageInfo.messageCount" | |
155 | + /> | |
156 | + <CountTo v-else :end-val="0" /> | |
157 | + </div> | |
158 | + <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else> | |
159 | + <CountTo | |
160 | + v-if="growCardList?.customerInfo?.sumCount" | |
161 | + :end-val="growCardList.customerInfo.sumCount" | |
162 | + /> | |
163 | + <CountTo v-else :end-val="0" /> | |
164 | + </div> | |
165 | + <Tooltip> | |
166 | + <template #title> | |
167 | + {{ | |
168 | + !isAdmin(role) | |
169 | + ? `产品数:${growCardList?.messageInfo?.messageCount} 今日新增 ${toThousands( | |
170 | + growCardList?.messageInfo?.todayMessageAdd | |
171 | + )}` | |
172 | + : `产品数:${growCardList?.customerInfo?.sumCount} 今日新增 ${toThousands( | |
173 | + growCardList?.messageInfo?.todayMessageAdd | |
174 | + )}` | |
175 | + }} | |
176 | + </template> | |
177 | + <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" /> | |
178 | + </Tooltip> | |
179 | + </div> | |
180 | + <div> {{ !isAdmin(role) ? `产品数` : '产品数' }}</div> | |
181 | + </div> | |
182 | + </div> | |
183 | + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5"> | |
184 | + 今日新增 {{ toThousands(growCardList?.messageInfo?.todayMessageAdd) }}</div | |
185 | + > | |
186 | + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5"> | |
187 | + 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div | |
188 | + > | |
189 | + </Card> | |
134 | 190 | </div> |
135 | 191 | </template> |
136 | 192 | <script lang="ts" setup> | ... | ... |
1 | +<template> | |
2 | + <div class="md:flex mt-4"> | |
3 | + <Card | |
4 | + size="small" | |
5 | + class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4" | |
6 | + style="color: #666; width: 50%" | |
7 | + > | |
8 | + <div class="flex container"> | |
9 | + <div class="mr-4 flex chart-top"> | |
10 | + <PieChartDeviceSub | |
11 | + :legendData="legendData" | |
12 | + :seriesData="seriesData" | |
13 | + :radisData="radisData" | |
14 | + /> | |
15 | + </div> | |
16 | + <div class="ml-20 flex justify-around right-text"> | |
17 | + <div class="text"> 直连设备:4个 </div> | |
18 | + <div class="text"> 网关设备:1个 </div> | |
19 | + <div class="text"> 网关子设备:6个 </div> | |
20 | + </div> | |
21 | + </div> | |
22 | + </Card> | |
23 | + <Card | |
24 | + size="small" | |
25 | + class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-1" | |
26 | + style="color: #666; width: 50%" | |
27 | + > | |
28 | + <div class="flex container"> | |
29 | + <div class="mr-4 flex chart-top"> | |
30 | + <PieChartDeviceSub | |
31 | + :legendData="legendStatusData" | |
32 | + :seriesData="seriesStatusData" | |
33 | + :radisData="radisStatusData" | |
34 | + /> | |
35 | + </div> | |
36 | + <div class="ml-20 flex justify-around right-text"> | |
37 | + <div class="text"> 待激活设备:2个 </div> | |
38 | + <div class="text"> 在线设备:1个 </div> | |
39 | + <div class="text"> 离线设备:4个 </div> | |
40 | + </div> | |
41 | + </div> | |
42 | + </Card> | |
43 | + </div> | |
44 | +</template> | |
45 | +<script lang="ts" setup> | |
46 | + import { ref, onMounted, defineComponent, Ref } from 'vue'; | |
47 | + import { Card } from 'ant-design-vue'; | |
48 | + import { getHomeData } from '/@/api/dashboard'; | |
49 | + import { isAdmin } from '/@/enums/roleEnum'; | |
50 | + import { toThousands } from '/@/utils/fnUtils'; | |
51 | + import PieChartDeviceSub from './PieChartDeviceSub.vue'; | |
52 | + | |
53 | + defineProps<{ | |
54 | + role: string; | |
55 | + }>(); | |
56 | + defineExpose({ | |
57 | + isAdmin, | |
58 | + toThousands, | |
59 | + }); | |
60 | + defineComponent({ | |
61 | + Card, | |
62 | + }); | |
63 | + interface CardList { | |
64 | + deviceInfo: { | |
65 | + sumCount: number; | |
66 | + onLine: number; | |
67 | + offLine: number; | |
68 | + inActive: number; | |
69 | + todayAdd: number; | |
70 | + }; | |
71 | + tenantInfo?: { sumCount: number; todayAdd: number }; | |
72 | + customerInfo?: { sumCount: number; todayAdd: number }; | |
73 | + alarmInfo?: { | |
74 | + sumCount: number; | |
75 | + todayAdd: number; | |
76 | + }; | |
77 | + messageInfo?: { | |
78 | + dataPointsCount: number; | |
79 | + messageCount: number; | |
80 | + todayDataPointsAdd: number; | |
81 | + todayMessageAdd: number; | |
82 | + }; | |
83 | + } | |
84 | + type seriesDataT = { | |
85 | + value: number; | |
86 | + name: string; | |
87 | + itemStyle: object; | |
88 | + }; | |
89 | + const radisData: any = ref('70%'); | |
90 | + const radisStatusData = ref<string[]>(['40%', '70%']); | |
91 | + const growCardList = ref<CardList>(); | |
92 | + const legendData = ref(['gateway', 'directly', 'sub-device']); | |
93 | + const seriesData: Ref<seriesDataT[]> = ref([ | |
94 | + { value: 1048, name: 'gateway', itemStyle: { color: '#3aa0ff' } }, | |
95 | + { value: 735, name: 'directly', itemStyle: { color: '#36cbcb' } }, | |
96 | + { value: 580, name: 'sub-device', itemStyle: { color: '#4ecb73' } }, | |
97 | + ]); | |
98 | + const legendStatusData = ref(['inactive', 'online', 'offline']); | |
99 | + const seriesStatusData: Ref<seriesDataT[]> = ref([ | |
100 | + { value: 1048, name: 'inactive', itemStyle: { color: '#3aa0ff' } }, | |
101 | + { value: 735, name: 'online', itemStyle: { color: '#36cbcb' } }, | |
102 | + { value: 580, name: 'offline', itemStyle: { color: '#4ecb73' } }, | |
103 | + ]); | |
104 | + onMounted(async () => { | |
105 | + const res = await getHomeData(); | |
106 | + growCardList.value = res; | |
107 | + }); | |
108 | +</script> | |
109 | + | |
110 | +<style lang="css"> | |
111 | + .right-text { | |
112 | + width: 40%; | |
113 | + flex-direction: column; | |
114 | + height: 240px; | |
115 | + margin: 10px 0 10px 50px; | |
116 | + } | |
117 | + | |
118 | + .text { | |
119 | + color: #333; | |
120 | + font-weight: bold; | |
121 | + display: flex; | |
122 | + flex-wrap: nowrap; | |
123 | + } | |
124 | + | |
125 | + .chart-top { | |
126 | + width: 60%; | |
127 | + height: 300px; | |
128 | + align-items: center; | |
129 | + margin-top: -30px; | |
130 | + } | |
131 | + | |
132 | + .container { | |
133 | + width: 100%; | |
134 | + } | |
135 | +</style> | ... | ... |
1 | +<template> | |
2 | + <div v-show="dataSeries.length" ref="chartRef" :style="{ height, width }"></div> | |
3 | + <div class="empty-box" v-show="!dataSeries.length" | |
4 | + ><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" | |
5 | + /></div> | |
6 | +</template> | |
7 | +<script lang="ts"> | |
8 | + import { defineComponent, PropType, ref, Ref, onMounted, toRefs } from 'vue'; | |
9 | + import { useECharts } from '/@/hooks/web/useECharts'; | |
10 | + import { Empty } from 'ant-design-vue'; | |
11 | + | |
12 | + export default defineComponent({ | |
13 | + components: { Empty }, | |
14 | + props: { | |
15 | + width: { | |
16 | + type: String as PropType<string>, | |
17 | + default: '100%', | |
18 | + }, | |
19 | + height: { | |
20 | + type: String as PropType<string>, | |
21 | + default: '200px', | |
22 | + }, | |
23 | + legendData: { | |
24 | + type: Array, | |
25 | + default: () => [], | |
26 | + }, | |
27 | + seriesData: { | |
28 | + type: Array, | |
29 | + default: () => [], | |
30 | + }, | |
31 | + radisData: { | |
32 | + type: Array || String, | |
33 | + default: () => ['40%', '70%'] || '70%', | |
34 | + }, | |
35 | + }, | |
36 | + setup(props) { | |
37 | + const { legendData, seriesData, radisData } = toRefs(props); | |
38 | + const dataSeries = ref<any>([]); | |
39 | + const legendDatas = ref<any>([]); | |
40 | + const radisDatas = ref<any>([]); | |
41 | + dataSeries.value = seriesData.value; | |
42 | + legendDatas.value = legendData.value; | |
43 | + radisDatas.value = radisData.value; | |
44 | + | |
45 | + const chartRef = ref<HTMLDivElement | null>(null); | |
46 | + const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>); | |
47 | + onMounted(() => { | |
48 | + setOptions({ | |
49 | + backgroundColor: '#ffffff', | |
50 | + tooltip: { | |
51 | + trigger: 'item', | |
52 | + formatter: '{b} {d}%', | |
53 | + }, | |
54 | + legend: { | |
55 | + bottom: 0, | |
56 | + width: 500, | |
57 | + height: 200, | |
58 | + left: 'center', | |
59 | + data: legendDatas.value, | |
60 | + }, | |
61 | + series: [ | |
62 | + { | |
63 | + type: 'pie', | |
64 | + radius: radisDatas.value, | |
65 | + data: dataSeries.value, | |
66 | + emphasis: { | |
67 | + itemStyle: { | |
68 | + shadowBlur: 10, | |
69 | + shadowOffsetX: 0, | |
70 | + shadowColor: 'rgba(0, 0, 0, 0.5)', | |
71 | + }, | |
72 | + }, | |
73 | + }, | |
74 | + ], | |
75 | + }); | |
76 | + //自适应 | |
77 | + window.addEventListener('resize', () => resize()); | |
78 | + }); | |
79 | + return { chartRef, Empty, dataSeries }; | |
80 | + }, | |
81 | + }); | |
82 | +</script> | |
83 | + | |
84 | +<style> | |
85 | + .empty-box { | |
86 | + display: flex; | |
87 | + align-items: center; | |
88 | + margin: 0 120px; | |
89 | + } | |
90 | +</style> | ... | ... |
... | ... | @@ -47,7 +47,7 @@ |
47 | 47 | </div> |
48 | 48 | </Card> |
49 | 49 | <div v-if="isAdmin(role)"> |
50 | - <Card | |
50 | + <!-- <Card | |
51 | 51 | v-bind="$attrs" |
52 | 52 | title="租户趋势" |
53 | 53 | style="height: 26.75rem" |
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 | </div> |
71 | 71 | </template> |
72 | 72 | <TenantTrend :tenantTrendList="tenantTrendList" /> |
73 | - </Card> | |
73 | + </Card> --> | |
74 | 74 | <Card |
75 | 75 | v-bind="$attrs" |
76 | 76 | title="客户趋势" |
... | ... | @@ -109,7 +109,7 @@ |
109 | 109 | import { formatToDateTime } from '/@/utils/dateUtil'; |
110 | 110 | import { getEntitiesId } from '/@/api/dashboard/index'; |
111 | 111 | import CustomerTrend from './CustomerTrend.vue'; |
112 | - import TenantTrend from './TenantTrend.vue'; | |
112 | + // import TenantTrend from './TenantTrend.vue'; | |
113 | 113 | import CustomerAlarmMessage from './CustomerAlarmMessage.vue'; |
114 | 114 | import { useDate } from '../hooks/useDate'; |
115 | 115 | import { getTrendData } from '/@/api/dashboard'; |
... | ... | @@ -129,10 +129,10 @@ |
129 | 129 | key: '1', |
130 | 130 | tab: '告警数统计', |
131 | 131 | }, |
132 | - { | |
133 | - key: '2', | |
134 | - tab: '消息量统计', | |
135 | - }, | |
132 | + // { | |
133 | + // key: '2', | |
134 | + // tab: '消息量统计', | |
135 | + // }, | |
136 | 136 | ]; |
137 | 137 | // 快速选择日期 |
138 | 138 | const activeIndex = ref(3); |
... | ... | @@ -453,15 +453,15 @@ |
453 | 453 | } |
454 | 454 | } |
455 | 455 | const { |
456 | - tenantDateValue, | |
456 | + // tenantDateValue, | |
457 | 457 | customerDateValue, |
458 | - tenantTrendList, | |
458 | + // tenantTrendList, | |
459 | 459 | customerTrendList, |
460 | - activeTenantIndex, | |
460 | + // activeTenantIndex, | |
461 | 461 | activeCustomerIndex, |
462 | 462 | TenantOrCustomerDateList, |
463 | 463 | quickQueryTenantOrCustomerTime, |
464 | - onDateTenantChange, | |
464 | + // onDateTenantChange, | |
465 | 465 | onDateCustomerChange, |
466 | 466 | } = useDate(); |
467 | 467 | </script> | ... | ... |
1 | +<template> | |
2 | + <Card | |
3 | + :tab-list="tabListTitle" | |
4 | + v-bind="$attrs" | |
5 | + :active-tab-key="activeKey" | |
6 | + @tabChange="onTabChange" | |
7 | + v-if="!isAdmin(role)" | |
8 | + > | |
9 | + <template #tabBarExtraContent> | |
10 | + <div class="extra-date"> | |
11 | + <template v-for="(item, index) in dateList" :key="item.value"> | |
12 | + <span | |
13 | + @click="quickQueryDate(index, item.value, role === 'CUSTOMER_USER' && 'customer')" | |
14 | + :class="{ active: index === activeIndex }" | |
15 | + >{{ item.label }}</span | |
16 | + > | |
17 | + </template> | |
18 | + <DatePicker | |
19 | + @change=" | |
20 | + (_, DateString) => onDateChange(_, DateString, role === 'CUSTOMER_USER' && 'customer') | |
21 | + " | |
22 | + v-model:value="dateValue" | |
23 | + /> | |
24 | + </div> | |
25 | + </template> | |
26 | + <!-- <div v-if="activeKey === '1'"> | |
27 | + <CustomerAlarmMessage | |
28 | + v-if="role === 'CUSTOMER_USER'" | |
29 | + type="CUSTOMER_ALARM_STATISTICAL" | |
30 | + :customerList="customerAlarmList" | |
31 | + /> | |
32 | + <VisitAnalysis v-else :alarmList="state.alarmList" /> | |
33 | + </div> --> | |
34 | + <div v-if="activeKey === '2'"> | |
35 | + <!-- 柱形图 --> | |
36 | + <CustomerAlarmMessage | |
37 | + v-if="role === 'CUSTOMER_USER'" | |
38 | + type="CUSTOMER_MESSAGE_STATISTICAL" | |
39 | + :customerList="customerMessageList" | |
40 | + /> | |
41 | + <VisitAnalysisBar | |
42 | + v-else | |
43 | + :dataPointList="state.dataPointList" | |
44 | + :messageList="state.messageList" | |
45 | + /> | |
46 | + </div> | |
47 | + </Card> | |
48 | + <div v-if="isAdmin(role)"> | |
49 | + <Card | |
50 | + v-bind="$attrs" | |
51 | + title="租户趋势" | |
52 | + style="height: 26.75rem" | |
53 | + :bodyStyle="{ padding: '0 24px' }" | |
54 | + > | |
55 | + <template #extra> | |
56 | + <div class="extra-date"> | |
57 | + <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> | |
58 | + <span | |
59 | + @click="quickQueryTenantOrCustomerTime(index, item.value, 'tenant')" | |
60 | + :class="{ active: index === activeTenantIndex }" | |
61 | + >{{ item.label }}</span | |
62 | + > | |
63 | + </template> | |
64 | + <DatePicker.RangePicker | |
65 | + @change="onDateTenantChange" | |
66 | + size="small" | |
67 | + v-model:value="tenantDateValue" | |
68 | + /> | |
69 | + </div> | |
70 | + </template> | |
71 | + <TenantTrend :tenantTrendList="tenantTrendList" /> | |
72 | + </Card> | |
73 | + <!-- <Card | |
74 | + v-bind="$attrs" | |
75 | + title="客户趋势" | |
76 | + style="height: 26.75rem" | |
77 | + :bodyStyle="{ padding: '0 24px' }" | |
78 | + > | |
79 | + <template #extra> | |
80 | + <div class="extra-date"> | |
81 | + <template v-for="(item, index) in TenantOrCustomerDateList" :key="item.value"> | |
82 | + <span | |
83 | + @click="quickQueryTenantOrCustomerTime(index, item.value)" | |
84 | + :class="{ active: index === activeCustomerIndex }" | |
85 | + >{{ item.label }}</span | |
86 | + > | |
87 | + </template> | |
88 | + <DatePicker.RangePicker | |
89 | + @change="onDateCustomerChange" | |
90 | + size="small" | |
91 | + v-model:value="customerDateValue" | |
92 | + /> | |
93 | + </div> | |
94 | + </template> | |
95 | + <CustomerTrend :customerTrendList="customerTrendList" /> | |
96 | + </Card> --> | |
97 | + </div> | |
98 | +</template> | |
99 | +<script lang="ts" setup> | |
100 | + import { ref, reactive } from 'vue'; | |
101 | + import { Card, DatePicker } from 'ant-design-vue'; | |
102 | + // import VisitAnalysis from './VisitAnalysis.vue'; | |
103 | + import VisitAnalysisBar from './VisitAnalysisBar.vue'; | |
104 | + import { isAdmin } from '/@/enums/roleEnum'; | |
105 | + import { useWebSocket } from '@vueuse/core'; | |
106 | + import { getAuthCache } from '/@/utils/auth'; | |
107 | + import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum'; | |
108 | + import { formatToDateTime } from '/@/utils/dateUtil'; | |
109 | + import { getEntitiesId } from '/@/api/dashboard/index'; | |
110 | + // import CustomerTrend from './CustomerTrend.vue'; | |
111 | + import TenantTrend from './TenantTrend.vue'; | |
112 | + import CustomerAlarmMessage from './CustomerAlarmMessage.vue'; | |
113 | + import { useDate } from '../hooks/useDate'; | |
114 | + import { getTrendData } from '/@/api/dashboard'; | |
115 | + import { useGlobSetting } from '/@/hooks/setting'; | |
116 | + | |
117 | + defineExpose({ | |
118 | + isAdmin, | |
119 | + }); | |
120 | + const props = defineProps<{ | |
121 | + role: string; | |
122 | + }>(); | |
123 | + const activeKey = ref('2'); | |
124 | + let entityId = null; | |
125 | + // 图表tab切换选项卡 1965 1280 1861 836 | |
126 | + const tabListTitle = [ | |
127 | + // { | |
128 | + // key: '1', | |
129 | + // tab: '告警数统计', | |
130 | + // }, | |
131 | + { | |
132 | + key: '2', | |
133 | + tab: '消息量统计', | |
134 | + }, | |
135 | + ]; | |
136 | + // 快速选择日期 | |
137 | + const activeIndex = ref(3); | |
138 | + const dateValue = ref(); | |
139 | + const dateList = ref([ | |
140 | + { label: '1小时', value: 3600000 }, | |
141 | + { label: '1天', value: 86400000 }, | |
142 | + { label: '7天', value: 604800000 }, | |
143 | + { label: '30天', value: 2592000000 }, | |
144 | + ]); | |
145 | + | |
146 | + // web Socket | |
147 | + const token: string = getAuthCache(JWT_TOKEN_KEY); | |
148 | + const { socketUrl } = useGlobSetting(); | |
149 | + const state = reactive({ | |
150 | + server: `${socketUrl}${token}`, | |
151 | + alarmList: new Array<[number, string]>(), | |
152 | + alarmItem: new Array<[number, string]>(), | |
153 | + dataPointList: new Array<[number, string]>(), | |
154 | + messageList: new Array<[number, string]>(), | |
155 | + dataPoint: new Array<[number, string]>(), | |
156 | + MsgCount: new Array<[number, string]>(), | |
157 | + }); | |
158 | + const { send, close } = useWebSocket(state.server, { | |
159 | + async onConnected() { | |
160 | + if (isAdmin(props.role)) return; | |
161 | + const res = await getEntitiesId(); | |
162 | + entityId = res.data[0]?.entityId; | |
163 | + const sendValue = JSON.stringify({ | |
164 | + entityDataCmds: [ | |
165 | + { | |
166 | + query: { | |
167 | + entityFilter: { | |
168 | + type: 'singleEntity', | |
169 | + singleEntity: entityId, | |
170 | + }, | |
171 | + pageLink: { | |
172 | + pageSize: 1024, | |
173 | + page: 0, | |
174 | + sortOrder: { | |
175 | + key: { | |
176 | + type: 'ENTITY_FIELD', | |
177 | + key: 'createdTime', | |
178 | + }, | |
179 | + direction: 'DESC', | |
180 | + }, | |
181 | + }, | |
182 | + entityFields: [ | |
183 | + { | |
184 | + type: 'ENTITY_FIELD', | |
185 | + key: 'name', | |
186 | + }, | |
187 | + { | |
188 | + type: 'ENTITY_FIELD', | |
189 | + key: 'label', | |
190 | + }, | |
191 | + { | |
192 | + type: 'ENTITY_FIELD', | |
193 | + key: 'additionalInfo', | |
194 | + }, | |
195 | + ], | |
196 | + latestValues: [ | |
197 | + { | |
198 | + type: 'TIME_SERIES', | |
199 | + key: 'transportMsgCountHourly', | |
200 | + }, | |
201 | + { | |
202 | + type: 'TIME_SERIES', | |
203 | + key: 'transportDataPointsCountHourly', | |
204 | + }, | |
205 | + ], | |
206 | + }, | |
207 | + cmdId: activeKey.value, | |
208 | + }, | |
209 | + ], | |
210 | + }); | |
211 | + const sendValue1 = JSON.stringify({ | |
212 | + entityDataCmds: [ | |
213 | + { | |
214 | + cmdId: activeKey.value, | |
215 | + historyCmd: { | |
216 | + keys: ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | |
217 | + startTs: Date.now() - 2592000000, | |
218 | + endTs: Date.now(), | |
219 | + interval: 86400000, | |
220 | + agg: 'SUM', | |
221 | + }, | |
222 | + }, | |
223 | + ], | |
224 | + }); | |
225 | + send(sendValue); | |
226 | + send(sendValue1); | |
227 | + }, | |
228 | + onMessage(_, e) { | |
229 | + const { data, update } = JSON.parse(e.data); | |
230 | + if (activeKey.value === '1') { | |
231 | + if (data) { | |
232 | + const { createdAlarmsCountHourly } = data.data[0].latest.TIME_SERIES; | |
233 | + state.alarmItem = [createdAlarmsCountHourly.ts, createdAlarmsCountHourly.value]; | |
234 | + state.alarmList.push([createdAlarmsCountHourly.ts, createdAlarmsCountHourly.value]); | |
235 | + } | |
236 | + if (update) { | |
237 | + const { createdAlarmsCountHourly } = update[0].timeseries; | |
238 | + const newArray: any = []; | |
239 | + for (const item of createdAlarmsCountHourly) { | |
240 | + newArray.push([item.ts, item.value]); | |
241 | + } | |
242 | + state.alarmList = newArray; | |
243 | + } | |
244 | + } else { | |
245 | + if (data) { | |
246 | + console.log('消息数', data); | |
247 | + if (data) { | |
248 | + const { transportDataPointsCountHourly, transportMsgCountHourly } = | |
249 | + data.data[0].latest.TIME_SERIES; | |
250 | + state.dataPoint = [ | |
251 | + transportDataPointsCountHourly?.ts, | |
252 | + transportDataPointsCountHourly?.value, | |
253 | + ]; | |
254 | + state.MsgCount = [ | |
255 | + transportDataPointsCountHourly?.ts, | |
256 | + transportDataPointsCountHourly?.value, | |
257 | + ]; | |
258 | + state.dataPointList.push([ | |
259 | + transportDataPointsCountHourly?.ts, | |
260 | + transportDataPointsCountHourly?.value, | |
261 | + ]); | |
262 | + console.log('state.dataPointList', state.dataPointList); | |
263 | + state.messageList.push([transportMsgCountHourly?.ts, transportMsgCountHourly?.value]); | |
264 | + console.log('state.messageList', state.messageList); | |
265 | + } | |
266 | + } | |
267 | + if (update) { | |
268 | + const { transportDataPointsCountHourly, transportMsgCountHourly } = update[0].timeseries; | |
269 | + const newArray: any[] = []; | |
270 | + const newArray1: any[] = []; | |
271 | + for (const item of transportDataPointsCountHourly) { | |
272 | + newArray.push([item.ts, item.value]); | |
273 | + } | |
274 | + for (const item of transportMsgCountHourly) { | |
275 | + newArray1.push([item.ts, item.value]); | |
276 | + } | |
277 | + state.dataPointList = newArray; | |
278 | + state.messageList = newArray1; | |
279 | + console.log('newArray', state.dataPointList); | |
280 | + console.log('newArray1', state.messageList); | |
281 | + } | |
282 | + } | |
283 | + }, | |
284 | + onDisconnected() { | |
285 | + console.log('断开连接了'); | |
286 | + close(); | |
287 | + }, | |
288 | + }); | |
289 | + | |
290 | + // 切换tab页 | |
291 | + function onTabChange(key: string) { | |
292 | + activeKey.value = key; | |
293 | + activeIndex.value = 3; | |
294 | + dateValue.value = ''; | |
295 | + const sendValue = JSON.stringify({ | |
296 | + entityDataCmds: [ | |
297 | + { | |
298 | + cmdId: activeKey.value, | |
299 | + historyCmd: { | |
300 | + keys: | |
301 | + activeKey.value === '1' | |
302 | + ? ['createdAlarmsCountHourly'] | |
303 | + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | |
304 | + startTs: Date.now() - 2592000000, | |
305 | + endTs: Date.now(), | |
306 | + interval: 86400000, | |
307 | + agg: 'SUM', | |
308 | + }, | |
309 | + }, | |
310 | + ], | |
311 | + }); | |
312 | + if (key === '2') { | |
313 | + const sendMessageValue = JSON.stringify({ | |
314 | + entityDataCmds: [ | |
315 | + { | |
316 | + query: { | |
317 | + entityFilter: { | |
318 | + type: 'singleEntity', | |
319 | + singleEntity: entityId, | |
320 | + }, | |
321 | + pageLink: { | |
322 | + pageSize: 1024, | |
323 | + page: 0, | |
324 | + sortOrder: { | |
325 | + key: { | |
326 | + type: 'ENTITY_FIELD', | |
327 | + key: 'createdTime', | |
328 | + }, | |
329 | + direction: 'DESC', | |
330 | + }, | |
331 | + }, | |
332 | + entityFields: [ | |
333 | + { | |
334 | + type: 'ENTITY_FIELD', | |
335 | + key: 'name', | |
336 | + }, | |
337 | + { | |
338 | + type: 'ENTITY_FIELD', | |
339 | + key: 'label', | |
340 | + }, | |
341 | + { | |
342 | + type: 'ENTITY_FIELD', | |
343 | + key: 'additionalInfo', | |
344 | + }, | |
345 | + ], | |
346 | + latestValues: [ | |
347 | + { | |
348 | + type: 'TIME_SERIES', | |
349 | + key: 'transportMsgCountHourly', | |
350 | + }, | |
351 | + { | |
352 | + type: 'TIME_SERIES', | |
353 | + key: 'transportDataPointsCountHourly', | |
354 | + }, | |
355 | + ], | |
356 | + }, | |
357 | + cmdId: activeKey.value, | |
358 | + }, | |
359 | + ], | |
360 | + }); | |
361 | + send(sendMessageValue); | |
362 | + } | |
363 | + send(sendValue); | |
364 | + } | |
365 | + // 选择日期 | |
366 | + async function onDateChange(_, dateString, roleType = '') { | |
367 | + console.log(_, dateString, roleType); | |
368 | + activeIndex.value = -1; | |
369 | + const dateTime = Number(formatToDateTime(dateString, 'x')); | |
370 | + if (!dateString) return; | |
371 | + if (roleType === 'customer') { | |
372 | + if (activeKey.value === '1') { | |
373 | + const data = await getTrendData({ | |
374 | + startTs: dateTime, | |
375 | + endTs: dateTime + 86400000, | |
376 | + interval: 7200000, | |
377 | + trend: 'CUSTOMER_ALARM_STATISTICAL', | |
378 | + }); | |
379 | + customerAlarmList.value = data; | |
380 | + } else if (activeKey.value === '2') { | |
381 | + const data = await getTrendData({ | |
382 | + startTs: dateTime, | |
383 | + endTs: dateTime + 86400000, | |
384 | + interval: 7200000, | |
385 | + trend: 'CUSTOMER_MESSAGE_STATISTICAL', | |
386 | + }); | |
387 | + customerMessageList.value = data; | |
388 | + } | |
389 | + } else { | |
390 | + // 动态发送ws数据 | |
391 | + const sendValue = JSON.stringify({ | |
392 | + entityDataCmds: [ | |
393 | + { | |
394 | + cmdId: activeKey.value, | |
395 | + historyCmd: { | |
396 | + keys: | |
397 | + activeKey.value === '1' | |
398 | + ? ['createdAlarmsCountHourly'] | |
399 | + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | |
400 | + startTs: dateTime, | |
401 | + endTs: dateTime + 86400000, | |
402 | + interval: 7200000, | |
403 | + limit: 12, | |
404 | + agg: 'SUM', | |
405 | + }, | |
406 | + }, | |
407 | + ], | |
408 | + }); | |
409 | + send(sendValue); | |
410 | + } | |
411 | + } | |
412 | + const customerAlarmList = ref([]); | |
413 | + const customerMessageList = ref([]); | |
414 | + // 快速选择时间 | |
415 | + async function quickQueryDate(index: number, value: number, roleType = '') { | |
416 | + if (activeIndex.value === index) return; | |
417 | + activeIndex.value = index; | |
418 | + dateValue.value = ''; | |
419 | + let interval = 300000; | |
420 | + if (value === 86400000) { | |
421 | + interval = 7200000; | |
422 | + } else if (value === 604800000 || value === 2592000000) { | |
423 | + interval = 86400000; | |
424 | + } | |
425 | + if (roleType === 'customer') { | |
426 | + if (activeKey.value === '1') { | |
427 | + const data = await getTrendData({ | |
428 | + startTs: Date.now() - value, | |
429 | + endTs: Date.now(), | |
430 | + interval, | |
431 | + trend: 'CUSTOMER_ALARM_STATISTICAL', | |
432 | + }); | |
433 | + customerAlarmList.value = data; | |
434 | + } else if (activeKey.value === '2') { | |
435 | + const data = await getTrendData({ | |
436 | + startTs: Date.now() - value, | |
437 | + endTs: Date.now(), | |
438 | + interval, | |
439 | + trend: 'CUSTOMER_MESSAGE_STATISTICAL', | |
440 | + }); | |
441 | + customerMessageList.value = data; | |
442 | + } | |
443 | + } else { | |
444 | + // 动态发送ws数据 | |
445 | + const sendValue = JSON.stringify({ | |
446 | + entityDataCmds: [ | |
447 | + { | |
448 | + cmdId: activeKey.value, | |
449 | + historyCmd: { | |
450 | + keys: | |
451 | + activeKey.value === '1' | |
452 | + ? ['createdAlarmsCountHourly'] | |
453 | + : ['transportMsgCountHourly', 'transportDataPointsCountHourly'], | |
454 | + startTs: Date.now() - value, | |
455 | + endTs: Date.now(), | |
456 | + interval, | |
457 | + agg: 'SUM', | |
458 | + }, | |
459 | + }, | |
460 | + ], | |
461 | + }); | |
462 | + send(sendValue); | |
463 | + } | |
464 | + } | |
465 | + const { | |
466 | + tenantDateValue, | |
467 | + // customerDateValue, | |
468 | + tenantTrendList, | |
469 | + // customerTrendList, | |
470 | + activeTenantIndex, | |
471 | + // activeCustomerIndex, | |
472 | + TenantOrCustomerDateList, | |
473 | + quickQueryTenantOrCustomerTime, | |
474 | + onDateTenantChange, | |
475 | + // onDateCustomerChange, | |
476 | + } = useDate(); | |
477 | +</script> | |
478 | + | |
479 | +<style lang="less"> | |
480 | + .center { | |
481 | + display: flex; | |
482 | + justify-content: center; | |
483 | + font-size: 16px; | |
484 | + } | |
485 | + | |
486 | + .active { | |
487 | + color: #0960bd; | |
488 | + font-weight: 500; | |
489 | + } | |
490 | + | |
491 | + .extra-date { | |
492 | + display: flex; | |
493 | + align-items: center; | |
494 | + justify-content: space-between; | |
495 | + | |
496 | + span { | |
497 | + margin-right: 20px; | |
498 | + cursor: pointer; | |
499 | + } | |
500 | + } | |
501 | +</style> | ... | ... |
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | </div> |
6 | 6 | </template> |
7 | 7 | <script lang="ts" setup> |
8 | - import { ref, Ref, watch, withDefaults } from 'vue'; | |
8 | + import { ref, Ref, watch, withDefaults, onMounted } from 'vue'; | |
9 | 9 | import { useECharts } from '/@/hooks/web/useECharts'; |
10 | 10 | import { Empty } from 'ant-design-vue'; |
11 | 11 | type DataItem = [number, string]; |
... | ... | @@ -23,6 +23,62 @@ |
23 | 23 | }); |
24 | 24 | const chartRef = ref<HTMLDivElement | null>(null); |
25 | 25 | const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); |
26 | + onMounted(() => { | |
27 | + // 计算总量 | |
28 | + let dataPointTotal = 0; | |
29 | + let messageTotal = 0; | |
30 | + for (const item of props.dataPointList) { | |
31 | + dataPointTotal += Number(item[1]); | |
32 | + } | |
33 | + for (const item of props.messageList) { | |
34 | + messageTotal += Number(item[1]); | |
35 | + } | |
36 | + setOptions({ | |
37 | + tooltip: { | |
38 | + trigger: 'axis', | |
39 | + axisPointer: { | |
40 | + type: 'shadow', | |
41 | + }, | |
42 | + }, | |
43 | + xAxis: { | |
44 | + type: 'time', | |
45 | + }, | |
46 | + legend: { | |
47 | + data: ['传输数据点', '传输消息量'], | |
48 | + left: 'center', | |
49 | + formatter: (name) => { | |
50 | + return name === '传输数据点' ? `${name} ${dataPointTotal}` : `${name} ${messageTotal}`; | |
51 | + }, | |
52 | + }, | |
53 | + | |
54 | + yAxis: {}, | |
55 | + grid: { | |
56 | + left: '3%', | |
57 | + right: '4%', | |
58 | + bottom: '3%', | |
59 | + containLabel: true, | |
60 | + }, | |
61 | + series: [ | |
62 | + { | |
63 | + name: '传输数据点', | |
64 | + type: 'line', | |
65 | + stack: 'total', | |
66 | + data: props.dataPointList, | |
67 | + // barWidth: '10%', | |
68 | + color: '#5AEEED', | |
69 | + }, | |
70 | + { | |
71 | + name: '传输消息量', | |
72 | + type: 'line', | |
73 | + stack: 'total', | |
74 | + // barWidth: '10%', | |
75 | + data: props.messageList, | |
76 | + color: '#3C78FF', | |
77 | + }, | |
78 | + ], | |
79 | + }); | |
80 | + }); | |
81 | + | |
26 | 82 | watch( |
27 | 83 | () => [props.dataPointList, props.messageList], |
28 | 84 | ([newValue, newValue1]) => { | ... | ... |
... | ... | @@ -2,6 +2,8 @@ |
2 | 2 | <div class="p-4 md:flex"> |
3 | 3 | <div class="md:w-7/10 w-full !mr-4 enter-y"> |
4 | 4 | <GrowCard :loading="loading" class="enter-y" :role="role" /> |
5 | + <PieChartDevice :loading="loading" class="enter-y" :role="role" /> | |
6 | + <SiteAnalysisMessage class="!my-4 enter-y" :loading="loading" :role="role" /> | |
5 | 7 | <SiteAnalysis class="!my-4 enter-y" :loading="loading" :role="role" /> |
6 | 8 | <div class="md:flex enter-y" v-if="!isAdmin(role)"> |
7 | 9 | <Card title="核心流程指南" style="width: 100%"> |
... | ... | @@ -23,11 +25,14 @@ |
23 | 25 | import { ref } from 'vue'; |
24 | 26 | import GrowCard from './components/GrowCard.vue'; |
25 | 27 | import SiteAnalysis from './components/SiteAnalysis.vue'; |
28 | + import SiteAnalysisMessage from './components/SiteAnalysisMessage.vue'; | |
26 | 29 | import { Card } from 'ant-design-vue'; |
27 | 30 | import HelpDoc from './components/HelpDoc.vue'; |
28 | 31 | import { USER_INFO_KEY } from '/@/enums/cacheEnum'; |
29 | 32 | import { getAuthCache } from '/@/utils/auth'; |
30 | 33 | import { isAdmin } from '/@/enums/roleEnum'; |
34 | + import PieChartDevice from './components/PieChartDevice.vue'; | |
35 | + | |
31 | 36 | defineExpose({ |
32 | 37 | isAdmin, |
33 | 38 | }); | ... | ... |