Commit 9dbe312e79de5d361c0ff67445176acfe12454de

Authored by xp.Huang
2 parents 6c0755f1 3702b0a8

Merge branch 'local_dev_ft' into 'main'

feat:首页新增饼状图和调整部分样式,待联调服务端。

See merge request huang/yun-teng-iot-front!444
@@ -131,6 +131,62 @@ @@ -131,6 +131,62 @@
131 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div 131 今日新增 {{ toThousands(growCardList?.customerInfo?.todayAdd) }}</div
132 > 132 >
133 </Card> 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 </div> 190 </div>
135 </template> 191 </template>
136 <script lang="ts" setup> 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,7 +47,7 @@
47 </div> 47 </div>
48 </Card> 48 </Card>
49 <div v-if="isAdmin(role)"> 49 <div v-if="isAdmin(role)">
50 - <Card 50 + <!-- <Card
51 v-bind="$attrs" 51 v-bind="$attrs"
52 title="租户趋势" 52 title="租户趋势"
53 style="height: 26.75rem" 53 style="height: 26.75rem"
@@ -70,7 +70,7 @@ @@ -70,7 +70,7 @@
70 </div> 70 </div>
71 </template> 71 </template>
72 <TenantTrend :tenantTrendList="tenantTrendList" /> 72 <TenantTrend :tenantTrendList="tenantTrendList" />
73 - </Card> 73 + </Card> -->
74 <Card 74 <Card
75 v-bind="$attrs" 75 v-bind="$attrs"
76 title="客户趋势" 76 title="客户趋势"
@@ -109,7 +109,7 @@ @@ -109,7 +109,7 @@
109 import { formatToDateTime } from '/@/utils/dateUtil'; 109 import { formatToDateTime } from '/@/utils/dateUtil';
110 import { getEntitiesId } from '/@/api/dashboard/index'; 110 import { getEntitiesId } from '/@/api/dashboard/index';
111 import CustomerTrend from './CustomerTrend.vue'; 111 import CustomerTrend from './CustomerTrend.vue';
112 - import TenantTrend from './TenantTrend.vue'; 112 + // import TenantTrend from './TenantTrend.vue';
113 import CustomerAlarmMessage from './CustomerAlarmMessage.vue'; 113 import CustomerAlarmMessage from './CustomerAlarmMessage.vue';
114 import { useDate } from '../hooks/useDate'; 114 import { useDate } from '../hooks/useDate';
115 import { getTrendData } from '/@/api/dashboard'; 115 import { getTrendData } from '/@/api/dashboard';
@@ -129,10 +129,10 @@ @@ -129,10 +129,10 @@
129 key: '1', 129 key: '1',
130 tab: '告警数统计', 130 tab: '告警数统计',
131 }, 131 },
132 - {  
133 - key: '2',  
134 - tab: '消息量统计',  
135 - }, 132 + // {
  133 + // key: '2',
  134 + // tab: '消息量统计',
  135 + // },
136 ]; 136 ];
137 // 快速选择日期 137 // 快速选择日期
138 const activeIndex = ref(3); 138 const activeIndex = ref(3);
@@ -453,15 +453,15 @@ @@ -453,15 +453,15 @@
453 } 453 }
454 } 454 }
455 const { 455 const {
456 - tenantDateValue, 456 + // tenantDateValue,
457 customerDateValue, 457 customerDateValue,
458 - tenantTrendList, 458 + // tenantTrendList,
459 customerTrendList, 459 customerTrendList,
460 - activeTenantIndex, 460 + // activeTenantIndex,
461 activeCustomerIndex, 461 activeCustomerIndex,
462 TenantOrCustomerDateList, 462 TenantOrCustomerDateList,
463 quickQueryTenantOrCustomerTime, 463 quickQueryTenantOrCustomerTime,
464 - onDateTenantChange, 464 + // onDateTenantChange,
465 onDateCustomerChange, 465 onDateCustomerChange,
466 } = useDate(); 466 } = useDate();
467 </script> 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,7 +5,7 @@
5 </div> 5 </div>
6 </template> 6 </template>
7 <script lang="ts" setup> 7 <script lang="ts" setup>
8 - import { ref, Ref, watch, withDefaults } from 'vue'; 8 + import { ref, Ref, watch, withDefaults, onMounted } from 'vue';
9 import { useECharts } from '/@/hooks/web/useECharts'; 9 import { useECharts } from '/@/hooks/web/useECharts';
10 import { Empty } from 'ant-design-vue'; 10 import { Empty } from 'ant-design-vue';
11 type DataItem = [number, string]; 11 type DataItem = [number, string];
@@ -23,6 +23,62 @@ @@ -23,6 +23,62 @@
23 }); 23 });
24 const chartRef = ref<HTMLDivElement | null>(null); 24 const chartRef = ref<HTMLDivElement | null>(null);
25 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 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 watch( 82 watch(
27 () => [props.dataPointList, props.messageList], 83 () => [props.dataPointList, props.messageList],
28 ([newValue, newValue1]) => { 84 ([newValue, newValue1]) => {
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 <div class="p-4 md:flex"> 2 <div class="p-4 md:flex">
3 <div class="md:w-7/10 w-full !mr-4 enter-y"> 3 <div class="md:w-7/10 w-full !mr-4 enter-y">
4 <GrowCard :loading="loading" class="enter-y" :role="role" /> 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 <SiteAnalysis class="!my-4 enter-y" :loading="loading" :role="role" /> 7 <SiteAnalysis class="!my-4 enter-y" :loading="loading" :role="role" />
6 <div class="md:flex enter-y" v-if="!isAdmin(role)"> 8 <div class="md:flex enter-y" v-if="!isAdmin(role)">
7 <Card title="核心流程指南" style="width: 100%"> 9 <Card title="核心流程指南" style="width: 100%">
@@ -23,11 +25,14 @@ @@ -23,11 +25,14 @@
23 import { ref } from 'vue'; 25 import { ref } from 'vue';
24 import GrowCard from './components/GrowCard.vue'; 26 import GrowCard from './components/GrowCard.vue';
25 import SiteAnalysis from './components/SiteAnalysis.vue'; 27 import SiteAnalysis from './components/SiteAnalysis.vue';
  28 + import SiteAnalysisMessage from './components/SiteAnalysisMessage.vue';
26 import { Card } from 'ant-design-vue'; 29 import { Card } from 'ant-design-vue';
27 import HelpDoc from './components/HelpDoc.vue'; 30 import HelpDoc from './components/HelpDoc.vue';
28 import { USER_INFO_KEY } from '/@/enums/cacheEnum'; 31 import { USER_INFO_KEY } from '/@/enums/cacheEnum';
29 import { getAuthCache } from '/@/utils/auth'; 32 import { getAuthCache } from '/@/utils/auth';
30 import { isAdmin } from '/@/enums/roleEnum'; 33 import { isAdmin } from '/@/enums/roleEnum';
  34 + import PieChartDevice from './components/PieChartDevice.vue';
  35 +
31 defineExpose({ 36 defineExpose({
32 isAdmin, 37 isAdmin,
33 }); 38 });