Commit 10c47882cbd98ebf7315a67a61032a7435d731cf

Authored by fengwotao
1 parent 02df0669

pref:修改首页饼图为圆环图

1 -<template>  
2 - <div>  
3 - <!-- 首页基础信息 -->  
4 - <div class="md:flex">  
5 - <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4" style="color: #666">  
6 - <div class="flex" style="height: 100px">  
7 - <div class="mr-4"  
8 - ><img  
9 - src="/src/assets/images/device-count.png"  
10 - style="width: 5.625rem; height: 5.625rem"  
11 - /></div>  
12 - <div class="flex-auto">  
13 - <div class="flex justify-between" style="align-items: center">  
14 - <div style="font-size: 1.625rem; color: #333; font-weight: bold">  
15 - <CountTo  
16 - v-if="growCardList?.deviceInfo?.sumCount"  
17 - :endVal="growCardList.deviceInfo.sumCount"  
18 - />  
19 - <CountTo v-else :endVal="0" />  
20 - </div>  
21 - <Tooltip>  
22 - <template #title>  
23 - 设备数 : {{ growCardList?.deviceInfo.sumCount }} 今日新增  
24 - {{ toThousands(growCardList?.deviceInfo?.todayAdd) }}  
25 - </template>  
26 - <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />  
27 - </Tooltip>  
28 - </div>  
29 - <div> 设备数 </div>  
30 - </div>  
31 - </div>  
32 - <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
33 - 今日新增 {{ toThousands(growCardList?.deviceInfo?.todayAdd) }}  
34 - </div>  
35 - </Card>  
36 - <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4" style="color: #666">  
37 - <div class="flex" style="height: 100px">  
38 - <div class="mr-4">  
39 - <img  
40 - v-if="!isAdmin(role)"  
41 - src="/src/assets/images/alarm-count.png"  
42 - style="width: 5.625rem; height: 5.625rem"  
43 - />  
44 - <img v-else src="/src/assets/images/zh.png" style="width: 5.625rem; height: 5.625rem" />  
45 - </div>  
46 - <div class="flex-auto">  
47 - <div class="flex justify-between" style="align-items: center">  
48 - <div  
49 - v-if="!isAdmin(role)"  
50 - style="font-size: 1.625rem; color: #333; font-weight: bold"  
51 - >  
52 - <CountTo  
53 - v-if="growCardList?.alarmInfo?.sumCount"  
54 - :end-val="growCardList.alarmInfo.sumCount"  
55 - />  
56 - <CountTo v-else :end-val="0" />  
57 - </div>  
58 - <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>  
59 - <CountTo  
60 - v-if="growCardList?.tenantInfo?.sumCount"  
61 - :end-val="growCardList.tenantInfo.sumCount"  
62 - />  
63 - <CountTo v-else :end-val="0" />  
64 - </div>  
65 - <Tooltip>  
66 - <template #title>  
67 - {{  
68 - !isAdmin(role)  
69 - ? `告警数:${growCardList?.alarmInfo?.sumCount} 今日新增 ${toThousands(  
70 - growCardList?.alarmInfo?.todayAdd  
71 - )}`  
72 - : `租户总量:${growCardList?.tenantInfo?.sumCount} 今日新增 ${toThousands(  
73 - growCardList?.alarmInfo?.todayAdd  
74 - )}`  
75 - }}  
76 - </template>  
77 - <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />  
78 - </Tooltip>  
79 - </div>  
80 - <div> {{ !isAdmin(role) ? `告警数` : '租户总量' }}</div>  
81 - </div>  
82 - </div>  
83 - <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
84 - 今日新增 {{ toThousands(growCardList?.alarmInfo?.todayAdd) }}</div  
85 - >  
86 - <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
87 - 今日新增 {{ toThousands(growCardList?.tenantInfo?.todayAdd) }}</div  
88 - >  
89 - </Card>  
90 - <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4" style="color: #666">  
91 - <div class="flex" style="height: 100px">  
92 - <div class="mr-4">  
93 - <img  
94 - v-if="!isAdmin(role)"  
95 - src="/src/assets/images/msg-count.png"  
96 - style="width: 5.625rem; height: 5.625rem"  
97 - />  
98 - <img v-else src="/src/assets/images/kf.png" style="width: 5.625rem; height: 5.625rem" />  
99 - </div>  
100 - <div class="flex-auto">  
101 - <div class="flex justify-between" style="align-items: center">  
102 - <div  
103 - v-if="!isAdmin(role)"  
104 - style="font-size: 1.625rem; color: #333; font-weight: bold"  
105 - >  
106 - <CountTo  
107 - v-if="growCardList?.messageInfo?.messageCount"  
108 - :end-val="growCardList.messageInfo.messageCount"  
109 - />  
110 - <CountTo v-else :end-val="0" />  
111 - </div>  
112 - <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>  
113 - <CountTo  
114 - v-if="growCardList?.customerInfo?.sumCount"  
115 - :end-val="growCardList.customerInfo.sumCount"  
116 - />  
117 - <CountTo v-else :end-val="0" />  
118 - </div>  
119 - <Tooltip>  
120 - <template #title>  
121 - {{  
122 - !isAdmin(role)  
123 - ? `消息数:${growCardList?.messageInfo?.messageCount} 今日新增 ${toThousands(  
124 - growCardList?.messageInfo?.todayMessageAdd  
125 - )}`  
126 - : `客户总量:${growCardList?.customerInfo?.sumCount} 今日新增 ${toThousands(  
127 - growCardList?.messageInfo?.todayMessageAdd  
128 - )}`  
129 - }}  
130 - </template>  
131 - <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />  
132 - </Tooltip>  
133 - </div>  
134 - <div> {{ !isAdmin(role) ? `消息数` : '客户总量' }}</div>  
135 - </div>  
136 - </div>  
137 - <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
138 - 今日新增 {{ toThousands(growCardList?.messageInfo?.todayMessageAdd) }}</div  
139 - >  
140 - <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
141 - 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div  
142 - >  
143 - </Card>  
144 - <Card  
145 - v-if="!isAdmin(role)"  
146 - size="small"  
147 - class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-4"  
148 - style="color: #666"  
149 - >  
150 - <div class="flex" style="height: 100px">  
151 - <div class="mr-4">  
152 - <img  
153 - v-if="!isAdmin(role)"  
154 - src="/src/assets/images/product.png"  
155 - style="width: 5.625rem; height: 5.625rem"  
156 - />  
157 - <img  
158 - v-else  
159 - src="/src/assets/images/product.png"  
160 - style="width: 5.625rem; height: 5.625rem"  
161 - />  
162 - </div>  
163 - <div class="flex-auto">  
164 - <div class="flex justify-between" style="align-items: center">  
165 - <div  
166 - v-if="!isAdmin(role)"  
167 - style="font-size: 1.625rem; color: #333; font-weight: bold"  
168 - >  
169 - <CountTo  
170 - v-if="growCardList?.productInfo?.sumCount"  
171 - :end-val="growCardList.productInfo.sumCount"  
172 - />  
173 - <CountTo v-else :end-val="0" />  
174 - </div>  
175 - <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>  
176 - <CountTo  
177 - v-if="growCardList?.productInfo?.sumCount"  
178 - :end-val="growCardList.productInfo?.sumCount"  
179 - />  
180 - <CountTo v-else :end-val="0" />  
181 - </div>  
182 - <Tooltip>  
183 - <template #title>  
184 - {{  
185 - !isAdmin(role)  
186 - ? `产品数:${growCardList?.productInfo?.sumCount} 今日新增 ${toThousands(  
187 - growCardList?.productInfo?.todayAdd  
188 - )}`  
189 - : `产品数:${growCardList?.customerInfo?.sumCount} 今日新增 ${toThousands(  
190 - growCardList?.productInfo?.todayAdd  
191 - )}`  
192 - }}  
193 - </template>  
194 - <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />  
195 - </Tooltip>  
196 - </div>  
197 - <div> {{ !isAdmin(role) ? `产品数` : '产品数' }}</div>  
198 - </div>  
199 - </div>  
200 - <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
201 - 今日新增 {{ toThousands(growCardList?.productInfo?.todayAdd) }}</div  
202 - >  
203 - <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">  
204 - 今日新增 {{ toThousands(growCardList?.productInfo?.todayAdd) }}</div  
205 - >  
206 - </Card>  
207 - </div>  
208 - <!-- 首页饼图 -->  
209 - <div class="md:flex mt-4" v-if="!isAdmin(role)">  
210 - <Card  
211 - size="small"  
212 - class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4"  
213 - style="color: #666; width: 50%"  
214 - >  
215 - <div class="flex container ml-4">  
216 - <div class="mr-4 flex chart-top">  
217 - <PieChartDeviceSub  
218 - v-if="seriesData.length > 0"  
219 - :legendData="legendData"  
220 - :seriesData="seriesData"  
221 - :isCircle="true"  
222 - />  
223 - </div>  
224 - <div class="ml-20 flex justify-around right-text">  
225 - <div class="text">  
226 - 直连设备:{{ growCardList?.deviceInfo?.directConnection ?? 0 }}个  
227 - </div>  
228 - <div class="text"> 网关设备:{{ growCardList?.deviceInfo?.gateWay ?? 0 }}个 </div>  
229 - <div class="text"> 网关子设备:{{ growCardList?.deviceInfo?.sensor ?? 0 }}个 </div>  
230 - </div>  
231 - </div>  
232 - </Card>  
233 - <Card  
234 - size="small"  
235 - class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-1"  
236 - style="color: #666; width: 50%"  
237 - >  
238 - <div class="flex container ml-4">  
239 - <div class="mr-4 flex chart-top">  
240 - <PieChartDeviceSub  
241 - v-if="seriesStatusData.length > 0"  
242 - :legendData="legendStatusData"  
243 - :seriesData="seriesStatusData"  
244 - :isCircle="false"  
245 - />  
246 - </div>  
247 - <div class="ml-20 flex justify-around right-text">  
248 - <div class="text"> 待激活设备:{{ growCardList?.deviceInfo?.inActive ?? 0 }}个 </div>  
249 - <div class="text"> 在线设备:{{ growCardList?.deviceInfo?.onLine ?? 0 }}个 </div>  
250 - <div class="text"> 离线设备:{{ growCardList?.deviceInfo?.offLine ?? 0 }}个 </div>  
251 - </div>  
252 - </div>  
253 - </Card>  
254 - </div>  
255 - </div>  
256 -</template>  
257 -<script lang="ts" setup>  
258 - import { ref, onMounted, defineComponent, Ref } from 'vue';  
259 - import { Card } from 'ant-design-vue';  
260 - import { getHomeData } from '/@/api/dashboard';  
261 - import { isAdmin } from '/@/enums/roleEnum';  
262 - import { toThousands } from '/@/utils/fnUtils';  
263 - import { CountTo } from '/@/components/CountTo/index';  
264 - import { Tooltip } from 'ant-design-vue';  
265 - import { CardList, seriesDataT } from './props';  
266 - import PieChartDeviceSub from './PieChartDeviceSub.vue';  
267 -  
268 - defineProps<{  
269 - role: string;  
270 - }>();  
271 -  
272 - defineExpose({  
273 - isAdmin,  
274 - toThousands,  
275 - });  
276 -  
277 - defineComponent({  
278 - Card,  
279 - });  
280 -  
281 - const legendData = ref(['网关设备', '直连设备', '网关子设备']);  
282 -  
283 - const seriesData: Ref<seriesDataT[]> = ref([]);  
284 -  
285 - const legendStatusData = ref(['待激活', '在线', '离线']);  
286 -  
287 - const seriesStatusData: Ref<seriesDataT[]> = ref([]);  
288 -  
289 - const growCardList = ref<CardList>();  
290 -  
291 - const devicePieColor = [  
292 - { key: 'directConnection', itemStyle: { color: '#5C7BD9' }, value: '直连设备' },  
293 - { key: 'gateWay', itemStyle: { color: '#91CC75' }, value: '网关设备' },  
294 - { key: 'sensor', itemStyle: { color: '#FAC859' }, value: '网关子设备' },  
295 - { key: 'inActive', itemStyle: { color: '#5C7BD9' }, value: '待激活' },  
296 - { key: 'onLine', itemStyle: { color: '#91CC75 ' }, value: '在线' },  
297 - { key: 'offLine', itemStyle: { color: '#EC4040' }, value: '离线' },  
298 - ];  
299 -  
300 - onMounted(async () => {  
301 - const res = await getHomeData();  
302 - growCardList.value = res;  
303 - const { deviceInfo } = growCardList.value!;  
304 - let data = Object.entries(deviceInfo).map(([key, value]) => {  
305 - const name = devicePieColor?.find((f) => f.key === key)?.value;  
306 - const itemStyle = devicePieColor?.find((f) => f.key === key)?.itemStyle;  
307 - return { key, value, itemStyle, name };  
308 - });  
309 - seriesData.value = data.filter(  
310 - (f) => f.key === 'directConnection' || f.key === 'gateWay' || f.key === 'sensor'  
311 - );  
312 - seriesStatusData.value = data.filter(  
313 - (f) => f.key === 'inActive' || f.key === 'onLine' || f.key === 'offLine'  
314 - );  
315 - });  
316 -</script>  
317 -<style lang="css">  
318 - .right-text {  
319 - width: 40%;  
320 - flex-direction: column;  
321 - height: 240px;  
322 - margin: 10px 0 10px 50px;  
323 - }  
324 -  
325 - .text {  
326 - color: #333;  
327 - font-weight: bold;  
328 - display: flex;  
329 - flex-wrap: nowrap;  
330 - }  
331 -  
332 - .chart-top {  
333 - width: 60%;  
334 - height: 300px;  
335 - align-items: center;  
336 - margin-top: -30px;  
337 - }  
338 -  
339 - .container {  
340 - width: 100%;  
341 - }  
342 -</style> 1 +<template>
  2 + <div>
  3 + <!-- 首页基础信息 -->
  4 + <div class="md:flex">
  5 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4" style="color: #666">
  6 + <div class="flex" style="height: 100px">
  7 + <div class="mr-4"
  8 + ><img
  9 + src="/src/assets/images/device-count.png"
  10 + style="width: 5.625rem; height: 5.625rem"
  11 + /></div>
  12 + <div class="flex-auto">
  13 + <div class="flex justify-between" style="align-items: center">
  14 + <div style="font-size: 1.625rem; color: #333; font-weight: bold">
  15 + <CountTo
  16 + v-if="growCardList?.deviceInfo?.sumCount"
  17 + :endVal="growCardList.deviceInfo.sumCount"
  18 + />
  19 + <CountTo v-else :endVal="0" />
  20 + </div>
  21 + <Tooltip>
  22 + <template #title>
  23 + 设备数 : {{ growCardList?.deviceInfo.sumCount }} 今日新增
  24 + {{ toThousands(growCardList?.deviceInfo?.todayAdd) }}
  25 + </template>
  26 + <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />
  27 + </Tooltip>
  28 + </div>
  29 + <div> 设备数 </div>
  30 + </div>
  31 + </div>
  32 + <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  33 + 今日新增 {{ toThousands(growCardList?.deviceInfo?.todayAdd) }}
  34 + </div>
  35 + </Card>
  36 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4" style="color: #666">
  37 + <div class="flex" style="height: 100px">
  38 + <div class="mr-4">
  39 + <img
  40 + v-if="!isAdmin(role)"
  41 + src="/src/assets/images/alarm-count.png"
  42 + style="width: 5.625rem; height: 5.625rem"
  43 + />
  44 + <img v-else src="/src/assets/images/zh.png" style="width: 5.625rem; height: 5.625rem" />
  45 + </div>
  46 + <div class="flex-auto">
  47 + <div class="flex justify-between" style="align-items: center">
  48 + <div
  49 + v-if="!isAdmin(role)"
  50 + style="font-size: 1.625rem; color: #333; font-weight: bold"
  51 + >
  52 + <CountTo
  53 + v-if="growCardList?.alarmInfo?.sumCount"
  54 + :end-val="growCardList.alarmInfo.sumCount"
  55 + />
  56 + <CountTo v-else :end-val="0" />
  57 + </div>
  58 + <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>
  59 + <CountTo
  60 + v-if="growCardList?.tenantInfo?.sumCount"
  61 + :end-val="growCardList.tenantInfo.sumCount"
  62 + />
  63 + <CountTo v-else :end-val="0" />
  64 + </div>
  65 + <Tooltip>
  66 + <template #title>
  67 + {{
  68 + !isAdmin(role)
  69 + ? `告警数:${growCardList?.alarmInfo?.sumCount} 今日新增 ${toThousands(
  70 + growCardList?.alarmInfo?.todayAdd
  71 + )}`
  72 + : `租户总量:${growCardList?.tenantInfo?.sumCount} 今日新增 ${toThousands(
  73 + growCardList?.alarmInfo?.todayAdd
  74 + )}`
  75 + }}
  76 + </template>
  77 + <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />
  78 + </Tooltip>
  79 + </div>
  80 + <div> {{ !isAdmin(role) ? `告警数` : '租户总量' }}</div>
  81 + </div>
  82 + </div>
  83 + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  84 + 今日新增 {{ toThousands(growCardList?.alarmInfo?.todayAdd) }}</div
  85 + >
  86 + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  87 + 今日新增 {{ toThousands(growCardList?.tenantInfo?.todayAdd) }}</div
  88 + >
  89 + </Card>
  90 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4" style="color: #666">
  91 + <div class="flex" style="height: 100px">
  92 + <div class="mr-4">
  93 + <img
  94 + v-if="!isAdmin(role)"
  95 + src="/src/assets/images/msg-count.png"
  96 + style="width: 5.625rem; height: 5.625rem"
  97 + />
  98 + <img v-else src="/src/assets/images/kf.png" style="width: 5.625rem; height: 5.625rem" />
  99 + </div>
  100 + <div class="flex-auto">
  101 + <div class="flex justify-between" style="align-items: center">
  102 + <div
  103 + v-if="!isAdmin(role)"
  104 + style="font-size: 1.625rem; color: #333; font-weight: bold"
  105 + >
  106 + <CountTo
  107 + v-if="growCardList?.messageInfo?.messageCount"
  108 + :end-val="growCardList.messageInfo.messageCount"
  109 + />
  110 + <CountTo v-else :end-val="0" />
  111 + </div>
  112 + <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>
  113 + <CountTo
  114 + v-if="growCardList?.customerInfo?.sumCount"
  115 + :end-val="growCardList.customerInfo.sumCount"
  116 + />
  117 + <CountTo v-else :end-val="0" />
  118 + </div>
  119 + <Tooltip>
  120 + <template #title>
  121 + {{
  122 + !isAdmin(role)
  123 + ? `消息数:${growCardList?.messageInfo?.messageCount} 今日新增 ${toThousands(
  124 + growCardList?.messageInfo?.todayMessageAdd
  125 + )}`
  126 + : `客户总量:${growCardList?.customerInfo?.sumCount} 今日新增 ${toThousands(
  127 + growCardList?.messageInfo?.todayMessageAdd
  128 + )}`
  129 + }}
  130 + </template>
  131 + <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />
  132 + </Tooltip>
  133 + </div>
  134 + <div> {{ !isAdmin(role) ? `消息数` : '客户总量' }}</div>
  135 + </div>
  136 + </div>
  137 + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  138 + 今日新增 {{ toThousands(growCardList?.messageInfo?.todayMessageAdd) }}</div
  139 + >
  140 + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  141 + 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div
  142 + >
  143 + </Card>
  144 + <Card
  145 + v-if="!isAdmin(role)"
  146 + size="small"
  147 + class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-4"
  148 + style="color: #666"
  149 + >
  150 + <div class="flex" style="height: 100px">
  151 + <div class="mr-4">
  152 + <img
  153 + v-if="!isAdmin(role)"
  154 + src="/src/assets/images/product.png"
  155 + style="width: 5.625rem; height: 5.625rem"
  156 + />
  157 + <img
  158 + v-else
  159 + src="/src/assets/images/product.png"
  160 + style="width: 5.625rem; height: 5.625rem"
  161 + />
  162 + </div>
  163 + <div class="flex-auto">
  164 + <div class="flex justify-between" style="align-items: center">
  165 + <div
  166 + v-if="!isAdmin(role)"
  167 + style="font-size: 1.625rem; color: #333; font-weight: bold"
  168 + >
  169 + <CountTo
  170 + v-if="growCardList?.productInfo?.sumCount"
  171 + :end-val="growCardList.productInfo.sumCount"
  172 + />
  173 + <CountTo v-else :end-val="0" />
  174 + </div>
  175 + <div style="font-size: 1.625rem; color: #333; font-weight: bold" v-else>
  176 + <CountTo
  177 + v-if="growCardList?.productInfo?.sumCount"
  178 + :end-val="growCardList.productInfo?.sumCount"
  179 + />
  180 + <CountTo v-else :end-val="0" />
  181 + </div>
  182 + <Tooltip>
  183 + <template #title>
  184 + {{
  185 + !isAdmin(role)
  186 + ? `产品数:${growCardList?.productInfo?.sumCount} 今日新增 ${toThousands(
  187 + growCardList?.productInfo?.todayAdd
  188 + )}`
  189 + : `产品数:${growCardList?.customerInfo?.sumCount} 今日新增 ${toThousands(
  190 + growCardList?.productInfo?.todayAdd
  191 + )}`
  192 + }}
  193 + </template>
  194 + <img src="/src/assets/images/tip.png" style="width: 1.125rem; height: 1.125rem" />
  195 + </Tooltip>
  196 + </div>
  197 + <div> {{ !isAdmin(role) ? `产品数` : '产品数' }}</div>
  198 + </div>
  199 + </div>
  200 + <div v-if="!isAdmin(role)" class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  201 + 今日新增 {{ toThousands(growCardList?.productInfo?.todayAdd) }}</div
  202 + >
  203 + <div v-else class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  204 + 今日新增 {{ toThousands(growCardList?.productInfo?.todayAdd) }}</div
  205 + >
  206 + </Card>
  207 + </div>
  208 + <!-- 首页饼图 -->
  209 + <div class="md:flex mt-4" v-if="!isAdmin(role)">
  210 + <Card
  211 + size="small"
  212 + class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4"
  213 + style="color: #666; width: 50%"
  214 + title="设备数量统计"
  215 + >
  216 + <div class="flex container ml-4">
  217 + <div class="mr-4 flex chart-top">
  218 + <PieChartDeviceSub
  219 + v-if="seriesData.length > 0"
  220 + :legendData="legendData"
  221 + :seriesData="seriesData"
  222 + :isCircle="false"
  223 + />
  224 + </div>
  225 + <div class="ml-20 flex justify-around right-text">
  226 + <div class="text flex items-center">
  227 + <span class="left-icon-d-color"></span>
  228 + 直连设备:{{ growCardList?.deviceInfo?.directConnection ?? 0 }}个
  229 + </div>
  230 + <div class="text flex items-center">
  231 + <span class="left-icon-g-color"></span>
  232 + 网关设备:{{ growCardList?.deviceInfo?.gateWay ?? 0 }}个
  233 + </div>
  234 + <div class="text flex items-center">
  235 + <span class="left-icon-s-color"></span>
  236 + 网关子设备:{{ growCardList?.deviceInfo?.sensor ?? 0 }}个
  237 + </div>
  238 + </div>
  239 + </div>
  240 + </Card>
  241 + <Card
  242 + size="small"
  243 + class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:ml-1"
  244 + style="color: #666; width: 50%"
  245 + title="设备状态统计"
  246 + >
  247 + <div class="flex container ml-4">
  248 + <div class="mr-4 flex chart-top-status">
  249 + <PieChartDeviceStatus
  250 + v-if="seriesStatusData.length > 0"
  251 + :seriesStatusData="seriesStatusData"
  252 + />
  253 + <div class="empty-box" v-else><Empty :image="Empty.PRESENTED_IMAGE_SIMPLE" /></div>
  254 + </div>
  255 + <div class="ml-20 flex justify-around right-text">
  256 + <div class="text flex items-center">
  257 + <span class="right-icon-d-color"></span>
  258 + 待激活设备:{{ growCardList?.deviceInfo?.inActive ?? 0 }}个
  259 + </div>
  260 + <div class="text flex items-center">
  261 + <span class="right-icon-g-color"></span>
  262 + 在线设备:{{ growCardList?.deviceInfo?.onLine ?? 0 }}个
  263 + </div>
  264 + <div class="text flex items-center">
  265 + <span class="right-icon-s-color"></span>
  266 + 离线设备:{{ growCardList?.deviceInfo?.offLine ?? 0 }}个
  267 + </div>
  268 + </div>
  269 + </div>
  270 + </Card>
  271 + </div>
  272 + </div>
  273 +</template>
  274 +<script lang="ts" setup>
  275 + import { ref, onMounted, defineComponent, Ref } from 'vue';
  276 + import { Card } from 'ant-design-vue';
  277 + import { getHomeData } from '/@/api/dashboard';
  278 + import { isAdmin } from '/@/enums/roleEnum';
  279 + import { toThousands } from '/@/utils/fnUtils';
  280 + import { CountTo } from '/@/components/CountTo/index';
  281 + import { Tooltip } from 'ant-design-vue';
  282 + import { CardList, seriesDataT } from './props';
  283 + import PieChartDeviceSub from './PieChartDeviceSub.vue';
  284 + import PieChartDeviceStatus from './PieChartDeviceStatus.vue';
  285 + import { Empty } from 'ant-design-vue';
  286 +
  287 + defineProps<{
  288 + role: string;
  289 + }>();
  290 +
  291 + defineExpose({
  292 + isAdmin,
  293 + toThousands,
  294 + });
  295 +
  296 + defineComponent({
  297 + Card,
  298 + });
  299 +
  300 + const legendData = ref(['网关设备', '直连设备', '网关子设备']);
  301 +
  302 + const seriesData: Ref<seriesDataT[]> = ref([]);
  303 +
  304 + const seriesStatusData: Ref<seriesDataT[]> = ref([]);
  305 +
  306 + const growCardList = ref<CardList>();
  307 +
  308 + const devicePieColor = [
  309 + { key: 'directConnection', itemStyle: { color: '#5C7BD9' }, value: '直连设备' },
  310 + { key: 'gateWay', itemStyle: { color: '#91CC75' }, value: '网关设备' },
  311 + { key: 'sensor', itemStyle: { color: '#FAC859' }, value: '网关子设备' },
  312 + { key: 'inActive', itemStyle: { color: '#5C7BD9' }, value: '待激活' },
  313 + { key: 'onLine', itemStyle: { color: '#91CC75 ' }, value: '在线' },
  314 + { key: 'offLine', itemStyle: { color: '#EC4040' }, value: '离线' },
  315 + ];
  316 +
  317 + onMounted(async () => {
  318 + const res = await getHomeData();
  319 + growCardList.value = res;
  320 + const { deviceInfo } = growCardList.value!;
  321 + let data = Object.entries(deviceInfo).map(([key, value]) => {
  322 + const name = devicePieColor?.find((f) => f.key === key)?.value;
  323 + const itemStyle = devicePieColor?.find((f) => f.key === key)?.itemStyle;
  324 + return { key, value, itemStyle, name };
  325 + });
  326 + seriesData.value = data.filter(
  327 + (f) => f.key === 'directConnection' || f.key === 'gateWay' || f.key === 'sensor'
  328 + );
  329 + seriesStatusData.value = data.filter(
  330 + (f) => f.key === 'inActive' || f.key === 'onLine' || f.key === 'offLine'
  331 + );
  332 + });
  333 +</script>
  334 +<style lang="less">
  335 + .right-text {
  336 + width: 40%;
  337 + flex-direction: column;
  338 + height: 240px;
  339 + margin: 10px 0 10px 50px;
  340 + }
  341 +
  342 + .text {
  343 + color: #333;
  344 + font-weight: bold;
  345 + display: flex;
  346 + flex-wrap: nowrap;
  347 + }
  348 +
  349 + .chart-top {
  350 + width: 60%;
  351 + height: 300px;
  352 + align-items: center;
  353 + margin-top: -30px;
  354 + }
  355 +
  356 + .container {
  357 + width: 100%;
  358 + }
  359 +
  360 + .chart-top-status {
  361 + height: 300px;
  362 + align-items: center;
  363 + margin-top: -40px;
  364 + }
  365 +
  366 + .base-left-icon-color {
  367 + border-radius: 50%;
  368 + width: 15px;
  369 + height: 15px;
  370 + position: relative;
  371 + right: 10px;
  372 + }
  373 + .left-icon-d-color :extend(.base-left-icon-color) {
  374 + background-color: #5c7bd9;
  375 + }
  376 + .left-icon-g-color :extend(.base-left-icon-color) {
  377 + background-color: #91cc75;
  378 + }
  379 + .left-icon-s-color :extend(.base-left-icon-color) {
  380 + background-color: #fac859;
  381 + }
  382 + .right-icon-d-color :extend(.base-left-icon-color) {
  383 + background-color: #5c7bd9;
  384 + }
  385 + .right-icon-g-color :extend(.base-left-icon-color) {
  386 + background-color: #91cc75;
  387 + }
  388 + .right-icon-s-color :extend(.base-left-icon-color) {
  389 + background-color: #ec4040;
  390 + }
  391 +</style>
  1 +<template>
  2 + <div v-for="(item, index) in seriesStatusData" :key="index">
  3 + <div :id="`chartPie${item.key}`" style="width: 150px; height: 200px"></div>
  4 + </div>
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent, PropType, onMounted, toRefs } from 'vue';
  8 + import * as echarts from 'echarts';
  9 + import { seriesDataT } from './props';
  10 +
  11 + export default defineComponent({
  12 + props: {
  13 + seriesStatusData: {
  14 + type: Array as PropType<seriesDataT[]>,
  15 + default: () => [],
  16 + },
  17 + },
  18 +
  19 + setup(props) {
  20 + const { seriesStatusData } = toRefs(props);
  21 + const total = seriesStatusData.value
  22 + .map((m) => m.value)
  23 + .reduce((prev, cur) => prev! + cur!, 0);
  24 + onMounted(() => {
  25 + seriesStatusData.value.forEach((item) => {
  26 + initEchart(item.key, item, item.value);
  27 + });
  28 + });
  29 +
  30 + const initEchart = (id, item, value) => {
  31 + //牵引线条颜色
  32 + const labelLine = {
  33 + normal: {
  34 + show: true,
  35 + length: 20,
  36 + length2: 20,
  37 + lineStyle: {
  38 + color: '#808080',
  39 + },
  40 + },
  41 + } as any;
  42 + const legendKey = item.key == 'onLine' ? '在线' : item.key == 'offLine' ? '离线' : '待激活';
  43 + const echartInstance = echarts.init(
  44 + document.getElementById(`chartPie${id}`) as HTMLElement
  45 + );
  46 + echartInstance.setOption({
  47 + backgroundColor: '#ffffff',
  48 + tooltip: {
  49 + trigger: 'item',
  50 + formatter: `${legendKey}设备${((value / total!) * 100).toFixed(2)}%`,
  51 + },
  52 + legend: {
  53 + bottom: 0,
  54 + left: 'center',
  55 + data: [legendKey],
  56 + },
  57 + graphic: [
  58 + {
  59 + type: 'text',
  60 + left: '42%',
  61 + top: '48%',
  62 + z: 10,
  63 + style: {
  64 + fill: '#1a1a1a',
  65 + text: value + '个',
  66 + font: '14px Microsoft YaHei',
  67 + },
  68 + },
  69 + ],
  70 + series: [
  71 + {
  72 + data: [item],
  73 + avoidLabelOverlap: false,
  74 + type: 'pie',
  75 + radius: ['40%', '60%'],
  76 + label: {
  77 + normal: {
  78 + show: true,
  79 + position: 'outer',
  80 + formatter: `${legendKey}${((value / total!) * 100).toFixed(2)}%`,
  81 + borderWidth: 5,
  82 + borderRadius: 0,
  83 + padding: [90, -60],
  84 + },
  85 + },
  86 + labelLine,
  87 + },
  88 + ],
  89 + });
  90 + window.addEventListener('resize', () => echartInstance.resize());
  91 + };
  92 + },
  93 + });
  94 +</script>
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 - import { seriesDataT } from './props';  
12 -  
13 - export default defineComponent({  
14 - components: { Empty },  
15 - props: {  
16 - width: {  
17 - type: String as PropType<string>,  
18 - default: '100%',  
19 - },  
20 - height: {  
21 - type: String as PropType<string>,  
22 - default: '200px',  
23 - },  
24 - legendData: {  
25 - type: Array,  
26 - default: () => [],  
27 - },  
28 - seriesData: {  
29 - type: Array,  
30 - default: () => [],  
31 - },  
32 - isCircle: {  
33 - type: Boolean,  
34 - default: true,  
35 - },  
36 - },  
37 - setup(props) {  
38 - const { legendData, seriesData } = toRefs(props);  
39 - const dataSeries: Ref<seriesDataT[]> = ref([]);  
40 - const legendDatas: Ref<seriesDataT[]> = ref([]);  
41 - dataSeries.value = seriesData.value as unknown as seriesDataT[];  
42 - legendDatas.value = legendData.value as unknown as seriesDataT[];  
43 -  
44 - const chartRef = ref<HTMLDivElement | null>(null);  
45 - const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>);  
46 - const labelLine = {  
47 - normal: {  
48 - show: true,  
49 - length2: 1,  
50 - },  
51 - } as any;  
52 - onMounted(() => {  
53 - setOptions({  
54 - backgroundColor: '#ffffff',  
55 - tooltip: {  
56 - trigger: 'item',  
57 - formatter: '{b} {d}%',  
58 - },  
59 - legend: {  
60 - bottom: 0,  
61 - width: 500,  
62 - height: 200,  
63 - left: 'center',  
64 - data: legendDatas.value,  
65 - },  
66 - series: [  
67 - {  
68 - avoidLabelOverlap: false,  
69 - type: 'pie',  
70 - radius: props.isCircle ? '60%' : ['40%', '60%'],  
71 - data: dataSeries.value,  
72 - emphasis: {  
73 - itemStyle: {  
74 - shadowBlur: 10,  
75 - shadowOffsetX: 0,  
76 - shadowColor: 'rgba(0, 0, 0, 0.5)',  
77 - },  
78 - },  
79 - labelLine,  
80 - },  
81 - ],  
82 - });  
83 - //自适应  
84 - window.addEventListener('resize', () => resize());  
85 - });  
86 - return { chartRef, Empty, dataSeries };  
87 - },  
88 - });  
89 -</script>  
90 -  
91 -<style>  
92 - .empty-box {  
93 - display: flex;  
94 - align-items: center;  
95 - margin: 0 120px;  
96 - }  
97 -</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 + import { seriesDataT } from './props';
  12 +
  13 + export default defineComponent({
  14 + components: { Empty },
  15 + props: {
  16 + width: {
  17 + type: String as PropType<string>,
  18 + default: '100%',
  19 + },
  20 + height: {
  21 + type: String as PropType<string>,
  22 + default: '200px',
  23 + },
  24 + legendData: {
  25 + type: Array,
  26 + default: () => [],
  27 + },
  28 + seriesData: {
  29 + type: Array,
  30 + default: () => [],
  31 + },
  32 + isCircle: {
  33 + type: Boolean,
  34 + default: true,
  35 + },
  36 + },
  37 + setup(props) {
  38 + const { legendData, seriesData } = toRefs(props);
  39 + const dataSeries: Ref<seriesDataT[]> = ref([]);
  40 + const legendDatas: Ref<seriesDataT[]> = ref([]);
  41 + dataSeries.value = seriesData.value as unknown as seriesDataT[];
  42 + legendDatas.value = legendData.value as unknown as seriesDataT[];
  43 +
  44 + const chartRef = ref<HTMLDivElement | null>(null);
  45 + const { setOptions, resize } = useECharts(chartRef as Ref<HTMLDivElement>);
  46 + const labelLine = {
  47 + normal: {
  48 + show: true,
  49 + length2: 1,
  50 + },
  51 + } as any;
  52 + onMounted(() => {
  53 + setOptions({
  54 + backgroundColor: '#ffffff',
  55 + tooltip: {
  56 + trigger: 'item',
  57 + formatter: '{b} {d}%',
  58 + },
  59 + legend: {
  60 + bottom: 0,
  61 + left: 'center',
  62 + data: legendDatas.value,
  63 + },
  64 + series: [
  65 + {
  66 + avoidLabelOverlap: false,
  67 + type: 'pie',
  68 + radius: props.isCircle ? '60%' : ['40%', '60%'],
  69 + data: dataSeries.value,
  70 + emphasis: {
  71 + itemStyle: {
  72 + shadowBlur: 10,
  73 + shadowOffsetX: 0,
  74 + shadowColor: 'rgba(0, 0, 0, 0.5)',
  75 + },
  76 + },
  77 + labelLine,
  78 + },
  79 + ],
  80 + });
  81 + //自适应
  82 + window.addEventListener('resize', () => resize());
  83 + });
  84 + return { chartRef, Empty, dataSeries };
  85 + },
  86 + });
  87 +</script>
  88 +
  89 +<style>
  90 + .empty-box {
  91 + display: flex;
  92 + align-items: center;
  93 + margin: 0 120px;
  94 + }
  95 +</style>
1 -import { PropType } from 'vue';  
2 -export interface BasicProps {  
3 - width: string;  
4 - height: string;  
5 -}  
6 -export const basicProps = {  
7 - width: {  
8 - type: String as PropType<string>,  
9 - default: '100%',  
10 - },  
11 - height: {  
12 - type: String as PropType<string>,  
13 - default: '280px',  
14 - },  
15 -};  
16 -  
17 -export interface CardList {  
18 - deviceInfo: {  
19 - sumCount: number;  
20 - onLine: number;  
21 - offLine: number;  
22 - inActive: number;  
23 - todayAdd: number;  
24 - directConnection: number;  
25 - gateWay: number;  
26 - sensor: number;  
27 - };  
28 - tenantInfo?: { sumCount: number; todayAdd: number };  
29 - customerInfo?: { sumCount: number; todayAdd: number };  
30 - alarmInfo?: {  
31 - sumCount: number;  
32 - todayAdd: number;  
33 - };  
34 - messageInfo?: {  
35 - dataPointsCount: number;  
36 - messageCount: number;  
37 - todayDataPointsAdd: number;  
38 - todayMessageAdd: number;  
39 - };  
40 - productInfo?: {  
41 - sumCount: number;  
42 - todayAdd: number;  
43 - };  
44 -}  
45 -export type seriesDataT = {  
46 - value: number | undefined;  
47 - name: string | undefined;  
48 - itemStyle: object | undefined;  
49 - key?: string | undefined;  
50 -}; 1 +import { PropType } from 'vue';
  2 +export interface BasicProps {
  3 + width: string;
  4 + height: string;
  5 +}
  6 +export const basicProps = {
  7 + width: {
  8 + type: String as PropType<string>,
  9 + default: '100%',
  10 + },
  11 + height: {
  12 + type: String as PropType<string>,
  13 + default: '280px',
  14 + },
  15 +};
  16 +
  17 +export interface CardList {
  18 + deviceInfo: {
  19 + sumCount: number;
  20 + onLine: number;
  21 + offLine: number;
  22 + inActive: number;
  23 + todayAdd: number;
  24 + directConnection: number;
  25 + gateWay: number;
  26 + sensor: number;
  27 + };
  28 + tenantInfo?: { sumCount: number; todayAdd: number };
  29 + customerInfo?: { sumCount: number; todayAdd: number };
  30 + alarmInfo?: {
  31 + sumCount: number;
  32 + todayAdd: number;
  33 + };
  34 + messageInfo?: {
  35 + dataPointsCount: number;
  36 + messageCount: number;
  37 + todayDataPointsAdd: number;
  38 + todayMessageAdd: number;
  39 + };
  40 + productInfo?: {
  41 + sumCount: number;
  42 + todayAdd: number;
  43 + };
  44 +}
  45 +export type seriesDataT = {
  46 + value: number | undefined;
  47 + name: string | undefined;
  48 + itemStyle: object | undefined;
  49 + key?: string | undefined;
  50 +};