Commit edef147b3f2154a933ee65078f680ab988d455a1

Authored by xp.Huang
2 parents 9e0402d8 4601105b

Merge branch 'sqy_dev' into 'main'

feat:地理位置完成,首页条件判断完成,接口待调试,设备修改字段。

See merge request huang/yun-teng-iot-front!64
@@ -30,3 +30,14 @@ export const getDeviceDataKeys = (id: string) => { @@ -30,3 +30,14 @@ export const getDeviceDataKeys = (id: string) => {
30 } 30 }
31 ); 31 );
32 }; 32 };
  33 +// 获取设备状态,在线 or 离线时间
  34 +export const getDeviceActiveTime = (entityId: string) => {
  35 + return defHttp.get(
  36 + {
  37 + url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`,
  38 + },
  39 + {
  40 + joinPrefix: false,
  41 + }
  42 + );
  43 +};
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +enum HomeEnum {
  3 + home = '/homepage/left/top',
  4 +}
  5 +
  6 +export const getHomeData = () => {
  7 + return defHttp.get({
  8 + url: HomeEnum.home,
  9 + });
  10 +};
1 export enum RoleEnum { 1 export enum RoleEnum {
2 ROLE_SYS_ADMIN = 'SYS_ADMIN', 2 ROLE_SYS_ADMIN = 'SYS_ADMIN',
3 ROLE_TENANT_ADMIN = 'TENANT_ADMIN', 3 ROLE_TENANT_ADMIN = 'TENANT_ADMIN',
4 - ROLE_NORMAL_USER = 'CUSTOMER_USER',  
5 ROLE_PLATFORM_ADMIN = 'PLATFORM_ADMIN', 4 ROLE_PLATFORM_ADMIN = 'PLATFORM_ADMIN',
  5 + ROLE_NORMAL_USER = 'CUSTOMER_USER',
6 } 6 }
@@ -51,7 +51,6 @@ @@ -51,7 +51,6 @@
51 severity: alarmLevel(data.severity), 51 severity: alarmLevel(data.severity),
52 status: statusType(data.status), 52 status: statusType(data.status),
53 }); 53 });
54 - console.log(data.status);  
55 alarmStatus.value = data.status; 54 alarmStatus.value = data.status;
56 alarmId.value = data.id; 55 alarmId.value = data.id;
57 }); 56 });
@@ -34,8 +34,16 @@ @@ -34,8 +34,16 @@
34 :canFullscreen="false" 34 :canFullscreen="false"
35 > 35 >
36 <BasicForm @register="registerForm" /> 36 <BasicForm @register="registerForm" />
37 - <div ref="chartRef" :style="{ height: '600px', width }"></div> 37 + <Alert
  38 + v-if="!isNull"
  39 + message="当前时间节点暂无历史数据"
  40 + description="请尝试选择其他时间段查询历史数据"
  41 + type="warning"
  42 + show-icon
  43 + />
  44 + <div v-show="isNull" ref="chartRef" :style="{ height: '600px', width }"></div>
38 </BasicModal> 45 </BasicModal>
  46 + <DeviceDetailDrawer @register="registerDetailDrawer" />
39 </div> 47 </div>
40 </template> 48 </template>
41 <script lang="ts"> 49 <script lang="ts">
@@ -44,22 +52,30 @@ @@ -44,22 +52,30 @@
44 import { formSchema, columns } from './config.data'; 52 import { formSchema, columns } from './config.data';
45 import { BasicTable, useTable } from '/@/components/Table'; 53 import { BasicTable, useTable } from '/@/components/Table';
46 import { devicePage } from '/@/api/alarm/contact/alarmContact'; 54 import { devicePage } from '/@/api/alarm/contact/alarmContact';
47 - import { Tag } from 'ant-design-vue'; 55 + import { Tag, Alert } from 'ant-design-vue';
48 import { DeviceState } from '/@/api/device/model/deviceModel'; 56 import { DeviceState } from '/@/api/device/model/deviceModel';
49 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils'; 57 import { BAI_DU_MAP_URL } from '/@/utils/fnUtils';
50 import { useModal, BasicModal } from '/@/components/Modal'; 58 import { useModal, BasicModal } from '/@/components/Modal';
51 import { BasicForm, useForm } from '/@/components/Form'; 59 import { BasicForm, useForm } from '/@/components/Form';
52 import { schemas } from './config.data'; 60 import { schemas } from './config.data';
53 import { useECharts } from '/@/hooks/web/useECharts'; 61 import { useECharts } from '/@/hooks/web/useECharts';
54 - import { getDeviceHistoryInfo, getDeviceDataKeys } from '/@/api/alarm/position'; 62 + import {
  63 + getDeviceHistoryInfo,
  64 + getDeviceDataKeys,
  65 + getDeviceActiveTime,
  66 + } from '/@/api/alarm/position';
  67 + import { useDrawer } from '/@/components/Drawer';
  68 + import DeviceDetailDrawer from '/@/views/device/manage/cpns/modal/DeviceDetailDrawer.vue';
55 import moment from 'moment'; 69 import moment from 'moment';
56 export default defineComponent({ 70 export default defineComponent({
57 name: 'BaiduMap', 71 name: 'BaiduMap',
58 components: { 72 components: {
59 BasicTable, 73 BasicTable,
60 Tag, 74 Tag,
  75 + Alert,
61 BasicModal, 76 BasicModal,
62 BasicForm, 77 BasicForm,
  78 + DeviceDetailDrawer,
63 }, 79 },
64 props: { 80 props: {
65 width: { 81 width: {
@@ -74,7 +90,10 @@ @@ -74,7 +90,10 @@
74 setup() { 90 setup() {
75 const wrapRef = ref<HTMLDivElement | null>(null); 91 const wrapRef = ref<HTMLDivElement | null>(null);
76 const { toPromise } = useScript({ src: BAI_DU_MAP_URL }); 92 const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
77 - const entityId = ref(''); 93 + const [registerDetailDrawer, { openDrawer }] = useDrawer();
  94 +
  95 + let entityId = '';
  96 + let globalRecord: any = {};
78 async function initMap() { 97 async function initMap() {
79 await toPromise(); 98 await toPromise();
80 await nextTick(); 99 await nextTick();
@@ -101,13 +120,14 @@ @@ -101,13 +120,14 @@
101 }, 120 },
102 }); 121 });
103 // 点击表格某一行触发 122 // 点击表格某一行触发
104 - const deviceRowClick = (record) => {  
105 - entityId.value = record.tbDeviceId; 123 + const deviceRowClick = async (record) => {
  124 + entityId = record.tbDeviceId;
  125 + globalRecord = record;
106 const BMap = (window as any).BMap; 126 const BMap = (window as any).BMap;
107 const wrapEl = unref(wrapRef); 127 const wrapEl = unref(wrapRef);
108 const map = new BMap.Map(wrapEl); 128 const map = new BMap.Map(wrapEl);
109 if (record.deviceInfo.address) { 129 if (record.deviceInfo.address) {
110 - const { name, organizationDTO, updateTime, deviceState, deviceProfile } = record; 130 + const { name, organizationDTO, deviceState, deviceProfile } = record;
111 const { longitude, latitude, address } = record.deviceInfo; 131 const { longitude, latitude, address } = record.deviceInfo;
112 const point = new BMap.Point(longitude, latitude); 132 const point = new BMap.Point(longitude, latitude);
113 let options = { 133 let options = {
@@ -117,6 +137,10 @@ @@ -117,6 +137,10 @@
117 map.centerAndZoom(point, 15); 137 map.centerAndZoom(point, 15);
118 map.enableScrollWheelZoom(true); 138 map.enableScrollWheelZoom(true);
119 // 创建信息窗口对象 139 // 创建信息窗口对象
  140 + const res = await getDeviceActiveTime(entityId);
  141 +
  142 + let { value: activeStatus, lastUpdateTs } = res[0];
  143 + lastUpdateTs = moment(lastUpdateTs).format('YYYY-MM-DD HH:mm:ss');
120 let infoWindow = new BMap.InfoWindow( 144 let infoWindow = new BMap.InfoWindow(
121 ` 145 `
122 <div style="display:flex;justify-content:space-between; margin:20px 0px;"> 146 <div style="display:flex;justify-content:space-between; margin:20px 0px;">
@@ -132,10 +156,9 @@ @@ -132,10 +156,9 @@
132 <div>所属组织:${organizationDTO.name}</div> 156 <div>所属组织:${organizationDTO.name}</div>
133 <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div> 157 <div style="margin-top:6px;">接入协议:${deviceProfile.transportType}</div>
134 <div style="margin-top:6px;">设备位置:${address}</div> 158 <div style="margin-top:6px;">设备位置:${address}</div>
135 - <div style="margin-top:6px;">下线时间:${updateTime}</div>  
136 - <div style="display:flex;justify-content:space-between; margin-top:10px">  
137 - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>  
138 - <button style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">报警记录</button> 159 + <div style="margin-top:6px;">${activeStatus ? '在' : '离'}线时间:${lastUpdateTs}</div>
  160 + <div style="display:flex;justify-content:end; margin-top:10px">
  161 + <button onclick="openDeviceInfoDrawer()" style="margin-right:10px;color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">设备信息</button>
139 <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button> 162 <button onclick="openHistoryModal()" style="color:#fff;background-color:#409eff;padding:4px; border-radius:4px;">历史数据</button>
140 </div> 163 </div>
141 `, 164 `,
@@ -185,13 +208,20 @@ @@ -185,13 +208,20 @@
185 endTs = Date.now(); 208 endTs = Date.now();
186 // 发送请求 209 // 发送请求
187 const res = await getDeviceHistoryInfo({ 210 const res = await getDeviceHistoryInfo({
188 - entityId: entityId.value, 211 + entityId,
189 keys: keys.join(), 212 keys: keys.join(),
190 startTs, 213 startTs,
191 endTs, 214 endTs,
192 interval, 215 interval,
193 agg, 216 agg,
194 }); 217 });
  218 + // 判断对象是否为空
  219 + if (Object.keys(res).length === 0) {
  220 + isNull.value = false;
  221 + return;
  222 + } else {
  223 + isNull.value = true;
  224 + }
195 // 处理数据 225 // 处理数据
196 for (const key in res) { 226 for (const key in res) {
197 for (const item1 of res[key]) { 227 for (const item1 of res[key]) {
@@ -250,24 +280,38 @@ @@ -250,24 +280,38 @@
250 280
251 const chartRef = ref<HTMLDivElement | null>(null); 281 const chartRef = ref<HTMLDivElement | null>(null);
252 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>); 282 const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
253 - 283 + const isNull = ref(true);
  284 + // 设备信息
  285 + const openDeviceInfoDrawer = async () => {
  286 + const { id, tbDeviceId } = globalRecord;
  287 + openDrawer(true, {
  288 + id,
  289 + tbDeviceId,
  290 + });
  291 + };
254 const openHistoryModal = async () => { 292 const openHistoryModal = async () => {
255 openModal(true); 293 openModal(true);
256 -  
257 // 收集参数 294 // 收集参数
258 const dataArray: any[] = []; 295 const dataArray: any[] = [];
259 const startTs = Date.now() - 86400000; //最近一天 296 const startTs = Date.now() - 86400000; //最近一天
260 const endTs = Date.now(); 297 const endTs = Date.now();
261 // 发送请求 298 // 发送请求
262 - keys = await getDeviceDataKeys(entityId.value); 299 + keys = await getDeviceDataKeys(entityId);
263 const res = await getDeviceHistoryInfo({ 300 const res = await getDeviceHistoryInfo({
264 - entityId: entityId.value, 301 + entityId,
265 keys: keys.join(), 302 keys: keys.join(),
266 startTs, 303 startTs,
267 endTs, 304 endTs,
268 interval: 7200000, //间隔两小时 305 interval: 7200000, //间隔两小时
269 agg: 'AVG', 306 agg: 'AVG',
270 }); 307 });
  308 + // 判断对象是否为空
  309 + if (Object.keys(res).length === 0) {
  310 + isNull.value = false;
  311 + return;
  312 + } else {
  313 + isNull.value = true;
  314 + }
271 // 处理数据 315 // 处理数据
272 for (const key in res) { 316 for (const key in res) {
273 for (const item1 of res[key]) { 317 for (const item1 of res[key]) {
@@ -327,12 +371,14 @@ @@ -327,12 +371,14 @@
327 agg: 'AVG', 371 agg: 'AVG',
328 }); 372 });
329 }; 373 };
  374 +
330 const cancelHistoryModal = () => { 375 const cancelHistoryModal = () => {
331 resetFields(); 376 resetFields();
332 setOptions({}); 377 setOptions({});
333 }; 378 };
334 onMounted(() => { 379 onMounted(() => {
335 initMap(); 380 initMap();
  381 + (window as any).openDeviceInfoDrawer = openDeviceInfoDrawer;
336 (window as any).openHistoryModal = openHistoryModal; 382 (window as any).openHistoryModal = openHistoryModal;
337 }); 383 });
338 return { 384 return {
@@ -343,7 +389,9 @@ @@ -343,7 +389,9 @@
343 registerModal, 389 registerModal,
344 registerForm, 390 registerForm,
345 chartRef, 391 chartRef,
  392 + isNull,
346 cancelHistoryModal, 393 cancelHistoryModal,
  394 + registerDetailDrawer,
347 }; 395 };
348 }, 396 },
349 }); 397 });
1 <template> 1 <template>
2 - <div class="md:flex justify-between">  
3 - <template v-for="(item, index) in growCardList" :key="item.title">  
4 - <div  
5 - class="growCardItem md:w-1/3 w-full !md:mt-0 !mt-4 bg-white"  
6 - :class="index === 0 ? '!md:ml-0' : '!md:ml-4'"  
7 - >  
8 - <div  
9 - class="  
10 - growCardItem-top  
11 - border border-solid border-t-0 border-r-0 border-l-0 border-b-1  
12 - dark:border-#ccc  
13 - light:border-#F2F2F5  
14 - "  
15 - >  
16 - <img :src="item.imgUrl" style="width: 5rem; height: 5rem" />  
17 - <div class="growCardItem-right">  
18 - <div class="flex justify-between ml-3">  
19 - <div style="font-size: 1.625rem; color: #333">{{ item.value }}</div>  
20 - <img src="../../../../assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" /> 2 + <div class="md:flex">
  3 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4">
  4 + <div class="flex" style="height: 100px">
  5 + <div class="mr-4"
  6 + ><img src="/src/assets/images/device-count.png" style="width: 5rem; height: 5rem"
  7 + /></div>
  8 + <div class="flex-auto">
  9 + <div class="flex justify-between" style="align-items: center">
  10 + <div style="font-size: 1.625rem; color: #333">{{
  11 + growCardList?.deviceInfo?.sumCount
  12 + }}</div>
  13 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  14 + </div>
  15 + <div> 设备数(个) </div>
  16 + <div class="flex mt-2">
  17 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem"
  18 + ><img src="/src/assets/images/online.png" class="mr-1" />
  19 + <span>在线{{ growCardList?.deviceInfo?.onLine }}</span>
21 </div> 20 </div>
22 - <div class="ml-3">{{ item.title }}</div>  
23 - <div class="ml-1.5 mt-3 flex flex-nowrap" style="width: 15rem" v-if="item.offLine">  
24 - <div class="count">  
25 - <img  
26 - src="../../../../assets/images/online.png"  
27 - style="width: 0.6rem; height: 0.6rem"  
28 - class="mr-1"  
29 - />  
30 - 在线 {{ item.onLine }}  
31 - </div>  
32 - <div class="count">  
33 - <img  
34 - src="../../../../assets/images/offline.png"  
35 - style="width: 0.6rem; height: 0.6rem"  
36 - class="mr-1"  
37 - />  
38 - 离线 {{ item.offLine }}  
39 - </div>  
40 - <div class="count">  
41 - <img  
42 - src="../../../../assets/images/inactive.png"  
43 - style="width: 0.6rem; height: 0.6rem"  
44 - class="mr-1"  
45 - />  
46 - 未激活 {{ item.inactive }}  
47 - </div> 21 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem">
  22 + <img src="/src/assets/images/offline.png" class="mr-1" />
  23 + <span> 离线{{ growCardList?.deviceInfo?.offLine }} </span>
48 </div> 24 </div>
  25 + <div class="flex mr-1" style="align-items: center; font-size: 0.75rem">
  26 + <img src="/src/assets/images/inactive.png" class="mr-1" />
  27 + <span> 未激活{{ growCardList?.deviceInfo?.inActive }} </span>
  28 + </div>
  29 + </div>
  30 + </div>
  31 + </div>
  32 + <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  33 + 今日新增 {{ growCardList?.deviceInfo?.todayAdd }}</div
  34 + >
  35 + </Card>
  36 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4 !md:mr-4">
  37 + <div class="flex" style="height: 100px">
  38 + <div class="mr-4"
  39 + ><img
  40 + :src="
  41 + role === 'TENANT_ADMIN'
  42 + ? '/src/assets/images/alarm-count.png'
  43 + : '/src/assets/images/zh.png'
  44 + "
  45 + style="width: 5rem; height: 5rem"
  46 + /></div>
  47 + <div class="flex-auto">
  48 + <div class="flex justify-between" style="align-items: center">
  49 + <div style="font-size: 1.625rem; color: #333">{{
  50 + growCardList?.tenantInfo?.sumCount
  51 + }}</div>
  52 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
49 </div> 53 </div>
  54 + <div> {{ role === 'TENANT_ADMIN' ? '11月告警数(条)' : '租户总量(个)' }}</div>
50 </div> 55 </div>
51 - <div class="growCardItem-bottom"> 今日新增 {{ item.newDay }}</div>  
52 </div> 56 </div>
53 - </template> 57 + <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  58 + 今日新增 {{ growCardList?.tenantInfo?.todayAdd }}</div
  59 + >
  60 + </Card>
  61 + <Card size="small" class="md:w-1/3 w-full !md:mt-0 !mt-4">
  62 + <div class="flex" style="height: 100px">
  63 + <div class="mr-4"
  64 + ><img
  65 + :src="
  66 + role === 'TENANT_ADMIN'
  67 + ? '/src/assets/images/msg-count.png'
  68 + : '/src/assets/images/kf.png'
  69 + "
  70 + style="width: 5rem; height: 5rem"
  71 + /></div>
  72 + <div class="flex-auto">
  73 + <div class="flex justify-between" style="align-items: center">
  74 + <div style="font-size: 1.625rem; color: #333">{{
  75 + growCardList?.customerInfo?.sumCount
  76 + }}</div>
  77 + <img src="/src/assets/images/tip.png" style="width: 1.4rem; height: 1.4rem" />
  78 + </div>
  79 + <div> {{ role === 'TENANT_ADMIN' ? '11月消息量(条)' : '客户总量(个)' }} </div>
  80 + </div>
  81 + </div>
  82 + <div class="ml-2 pt-4" style="border-top: 2px solid #f0f2f5">
  83 + 今日新增 {{ growCardList?.customerInfo?.todayAdd }}</div
  84 + >
  85 + </Card>
54 </div> 86 </div>
55 </template> 87 </template>
56 <script lang="ts" setup> 88 <script lang="ts" setup>
57 - import { growCardList } from '../data';  
58 -</script>  
59 -  
60 -<style scoped lang="less">  
61 - .growCardItem {  
62 - height: 11.187rem;  
63 - color: #666;  
64 - .growCardItem-top {  
65 - display: flex;  
66 - margin: 1.25rem;  
67 - padding-bottom: 0.625rem;  
68 - .growCardItem-right {  
69 - width: 18.75rem;  
70 - .count {  
71 - display: flex;  
72 - font-size: 0.75rem;  
73 - align-items: center;  
74 - margin-left: 0.5rem;  
75 - }  
76 - }  
77 - }  
78 - .growCardItem-bottom {  
79 - margin-left: 1.25rem;  
80 - } 89 + import { defineProps, ref, onMounted } from 'vue';
  90 + import { Card } from 'ant-design-vue';
  91 + import { getHomeData } from '/@/api/dashboard';
  92 + interface CardList {
  93 + deviceInfo: {
  94 + sumCount: number;
  95 + onLine: number;
  96 + offLine: number;
  97 + inActive: number;
  98 + todayAdd: number;
  99 + };
  100 + tenantInfo: { sumCount: number; todayAdd: number };
  101 + customerInfo: { sumCount: number; todayAdd: number };
81 } 102 }
82 -</style> 103 + const growCardList = ref<CardList>();
  104 + onMounted(async () => {
  105 + const res = await getHomeData();
  106 + growCardList.value = res;
  107 + console.log(growCardList.value);
  108 + });
  109 + defineProps<{
  110 + role: string;
  111 + }>();
  112 +</script>
1 <template> 1 <template>
2 - <Card title="帮助文档">  
3 - <div>  
4 - <template v-for="item in helpDoc" :key="item.title">  
5 - <AnchorLink v-bind="item" />  
6 - </template>  
7 - </div>  
8 - <Card  
9 - :tab-list="tabListTitle"  
10 - v-bind="$attrs"  
11 - :active-tab-key="activeKey"  
12 - :bordered="false"  
13 - @tabChange="onTabChange"  
14 - :bodyStyle="{ padding: 0 }"  
15 - >  
16 - <div v-if="activeKey === 'tab1'">  
17 - <List item-layout="horizontal" :dataSource="dataSource">  
18 - <template #renderItem="{ item }">  
19 - <ListItem>  
20 - <ListItemMeta>  
21 - <template #avatar>  
22 - <Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />  
23 - </template>  
24 - <template #description>  
25 - <span  
26 - @click="go('/stationnotification/mynotification')"  
27 - class="cursor-pointer noticeTitle"  
28 - >{{ item.sysNotice.title }}  
29 - </span>  
30 - </template>  
31 - <template #title>  
32 - <span>{{ item.user.realName }}</span> 2 + <div>
  3 + <Card title="帮助文档" v-if="role === 'TENANT_ADMIN'">
  4 + <div>
  5 + <template v-for="item in helpDoc" :key="item.title">
  6 + <AnchorLink v-bind="item" />
  7 + </template>
  8 + </div>
  9 + <Card
  10 + v-if="role === 'TENANT_ADMIN'"
  11 + :tab-list="tabListTitle"
  12 + v-bind="$attrs"
  13 + :active-tab-key="activeKey"
  14 + :bordered="false"
  15 + @tabChange="onTabChange"
  16 + :bodyStyle="{ padding: 0 }"
  17 + >
  18 + <div v-if="activeKey === 'tab1'">
  19 + <List item-layout="horizontal" :dataSource="dataSource">
  20 + <template #renderItem="{ item }">
  21 + <ListItem>
  22 + <ListItemMeta>
  23 + <template #avatar>
  24 + <Avatar
  25 + src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
  26 + />
  27 + </template>
  28 + <template #description>
  29 + <span
  30 + class="cursor-pointer noticeTitle"
  31 + @click="go('/stationnotification/mynotification')"
  32 + >{{ item.sysNotice.title }}
  33 + </span>
  34 + </template>
  35 + <template #title>
  36 + <span>{{ item.user.realName }}</span>
  37 + </template>
  38 + </ListItemMeta>
  39 + <template #extra>
  40 + <Time :value="item.sysNotice.senderDate" />
33 </template> 41 </template>
34 - </ListItemMeta>  
35 - <template #extra>  
36 - <Time :value="item.sysNotice.senderDate" />  
37 - </template>  
38 - </ListItem>  
39 - </template>  
40 - </List>  
41 - <Card hoverable title="联系我们" :bordered="false">  
42 - <template #cover>  
43 - <img :src="getQrCode" alt="" style="width: 150px; height: 150px; margin: 50px auto" />  
44 - </template>  
45 - <CardMeta>  
46 - <template #description>  
47 - <p>联系人: {{ getContacts }}</p>  
48 - <p>联系电话: {{ getTel }}</p>  
49 - <p>联系地址: {{ getAddress }} </p> 42 + </ListItem>
50 </template> 43 </template>
51 - </CardMeta>  
52 - </Card>  
53 - </div> 44 + </List>
  45 + <Card hoverable title="联系我们" :bordered="false">
  46 + <template #cover>
  47 + <img :src="getQrCode" alt="" style="width: 150px; height: 150px; margin: 50px auto" />
  48 + </template>
  49 + <CardMeta>
  50 + <template #description>
  51 + <p>联系人: {{ getContacts }}</p>
  52 + <p>联系电话: {{ getTel }}</p>
  53 + <p>联系地址: {{ getAddress }} </p>
  54 + </template>
  55 + </CardMeta>
  56 + </Card>
  57 + </div>
  58 + </Card>
54 </Card> 59 </Card>
55 - </Card> 60 +
  61 + <Card v-if="role !== 'TENANT_ADMIN'">
  62 + <Descriptions title="租户消息量TOP10" :column="1">
  63 + <template v-for="(item, index) in 10" :key="index">
  64 + <DescriptionsItem>
  65 + <span
  66 + class="mr-2"
  67 + style="
  68 + width: 1.25rem;
  69 + height: 1.25rem;
  70 + border: 1px solid;
  71 + color: #0b55f1;
  72 + border-radius: 50%;
  73 + display: flex;
  74 + align-items: center;
  75 + justify-content: center;
  76 + "
  77 + :style="{
  78 + color:
  79 + index === 0
  80 + ? '#f0a16e'
  81 + : index === 1
  82 + ? '#868585'
  83 + : index === 2
  84 + ? '#e78739'
  85 + : '#4e84f5',
  86 + backgroundColor:
  87 + index === 0 ? '#fed36a' : index === 1 ? '#CBCAC9' : index === 2 ? '#F1B889' : '',
  88 + borderColor:
  89 + index === 0
  90 + ? '#fdee7d'
  91 + : index === 1
  92 + ? '#e6e6e5'
  93 + : index === 2
  94 + ? '#f8c296'
  95 + : '#0b55f1;',
  96 + }"
  97 + >{{ index + 1 }}</span
  98 + >兰州天兆猪业</DescriptionsItem
  99 + >
  100 + </template>
  101 + </Descriptions>
  102 + </Card>
  103 + <BasicTable @register="registerTable" v-if="role !== 'TENANT_ADMIN'" />
  104 + </div>
56 </template> 105 </template>
57 106
58 <script lang="ts"> 107 <script lang="ts">
59 import { defineComponent, ref, computed, onMounted } from 'vue'; 108 import { defineComponent, ref, computed, onMounted } from 'vue';
60 - import { Card, AnchorLink, List, ListItem, ListItemMeta, Avatar, CardMeta } from 'ant-design-vue'; 109 + import {
  110 + Card,
  111 + AnchorLink,
  112 + List,
  113 + ListItem,
  114 + ListItemMeta,
  115 + Avatar,
  116 + CardMeta,
  117 + Descriptions,
  118 + DescriptionsItem,
  119 + } from 'ant-design-vue';
61 import { useUserStore } from '/@/store/modules/user'; 120 import { useUserStore } from '/@/store/modules/user';
62 import { getEnterPriseDetail } from '/@/api/oem'; 121 import { getEnterPriseDetail } from '/@/api/oem';
63 import { notifyMyGetrPageApi } from '/@/api/stationnotification/stationnotifyApi'; 122 import { notifyMyGetrPageApi } from '/@/api/stationnotification/stationnotifyApi';
64 import { Time } from '/@/components/Time'; 123 import { Time } from '/@/components/Time';
65 import { useGo } from '/@/hooks/web/usePage'; 124 import { useGo } from '/@/hooks/web/usePage';
  125 + import { BasicTable, useTable } from '/@/components/Table';
  126 + import { columns } from './props';
66 export default defineComponent({ 127 export default defineComponent({
67 components: { 128 components: {
68 Card, 129 Card,
@@ -73,12 +134,18 @@ @@ -73,12 +134,18 @@
73 Avatar, 134 Avatar,
74 Time, 135 Time,
75 CardMeta, 136 CardMeta,
  137 + BasicTable,
  138 + Descriptions,
  139 + DescriptionsItem,
76 }, 140 },
77 - setup() {  
78 - onMounted(async () => {  
79 - const res = await getEnterPriseDetail();  
80 - userStore.setEnterPriseInfo(res);  
81 - }); 141 + props: {
  142 + role: String,
  143 + },
  144 + setup(props) {
  145 + // 通知数据
  146 + const dataSource = ref([]);
  147 + const go = useGo();
  148 +
82 const helpDoc = ref([ 149 const helpDoc = ref([
83 { 150 {
84 title: '如何接入设备?', 151 title: '如何接入设备?',
@@ -108,6 +175,12 @@ @@ -108,6 +175,12 @@
108 const onTabChange = (key) => { 175 const onTabChange = (key) => {
109 activeKey.value = key; 176 activeKey.value = key;
110 }; 177 };
  178 + const [registerTable] = useTable({
  179 + title: '本月即将过期租户',
  180 + showIndexColumn: false,
  181 + useSearchForm: false,
  182 + columns,
  183 + });
111 184
112 const userStore = useUserStore(); 185 const userStore = useUserStore();
113 const getContacts = computed(() => { 186 const getContacts = computed(() => {
@@ -122,13 +195,12 @@ @@ -122,13 +195,12 @@
122 const getQrCode = computed(() => { 195 const getQrCode = computed(() => {
123 return userStore.enterPriseInfo?.qrCode; 196 return userStore.enterPriseInfo?.qrCode;
124 }); 197 });
125 -  
126 - // 通知数据  
127 - const dataSource = ref([]);  
128 - const go = useGo();  
129 onMounted(async () => { 198 onMounted(async () => {
130 - const res = await notifyMyGetrPageApi({ page: 1, pageSize: 5 });  
131 - dataSource.value = res.items; 199 + if (props.role !== 'TENANT_ADMIN') return;
  200 + const res = await getEnterPriseDetail();
  201 + const notice = await notifyMyGetrPageApi({ page: 1, pageSize: 5 });
  202 + userStore.setEnterPriseInfo(res);
  203 + dataSource.value = notice.items;
132 }); 204 });
133 205
134 return { 206 return {
@@ -142,6 +214,7 @@ @@ -142,6 +214,7 @@
142 getTel, 214 getTel,
143 dataSource, 215 dataSource,
144 go, 216 go,
  217 + registerTable,
145 }; 218 };
146 }, 219 },
147 }); 220 });
@@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
5 :active-tab-key="activeKey" 5 :active-tab-key="activeKey"
6 @tabChange="onTabChange" 6 @tabChange="onTabChange"
7 > 7 >
8 - <template #tabBarExtraContent> 8 + <template #tabBarExtraContent v-if="role === 'TENANT_ADMIN'">
9 <div class="extra-date"> 9 <div class="extra-date">
10 <template v-for="(item, index) in dateList" :key="item"> 10 <template v-for="(item, index) in dateList" :key="item">
11 <span @click="changeDate(index)" :class="{ active: index === activeIndex }">{{ 11 <span @click="changeDate(index)" :class="{ active: index === activeIndex }">{{
@@ -16,8 +16,9 @@ @@ -16,8 +16,9 @@
16 </div> 16 </div>
17 </template> 17 </template>
18 <div v-if="activeKey === 'tab1'"> 18 <div v-if="activeKey === 'tab1'">
19 - <p class="center">告警数</p>  
20 - <VisitAnalysis /> 19 + <p class="center">{{ role === 'TENANT_ADMIN' ? '告警数' : '租户趋势' }}</p>
  20 + <VisitAnalysis v-if="role === 'TENANT_ADMIN'" />
  21 + <VisitAnalysisBar v-else />
21 </div> 22 </div>
22 <div v-else> 23 <div v-else>
23 <p class="center">消息数</p> 24 <p class="center">消息数</p>
@@ -30,19 +31,33 @@ @@ -30,19 +31,33 @@
30 import { Card, DatePicker } from 'ant-design-vue'; 31 import { Card, DatePicker } from 'ant-design-vue';
31 import VisitAnalysis from './VisitAnalysis.vue'; 32 import VisitAnalysis from './VisitAnalysis.vue';
32 import VisitAnalysisBar from './VisitAnalysisBar.vue'; 33 import VisitAnalysisBar from './VisitAnalysisBar.vue';
  34 + import { defineProps } from 'vue';
33 35
  36 + const props = defineProps<{
  37 + role: string;
  38 + }>();
34 const activeKey = ref('tab1'); 39 const activeKey = ref('tab1');
35 40
36 - const tabListTitle = [  
37 - {  
38 - key: 'tab1',  
39 - tab: '告警数统计',  
40 - },  
41 - {  
42 - key: 'tab2',  
43 - tab: '消息量统计',  
44 - },  
45 - ]; 41 + // 动态根据登录角色来判断
  42 + const tabListTitle =
  43 + props.role === 'TENANT_ADMIN'
  44 + ? [
  45 + {
  46 + key: 'tab1',
  47 + tab: '告警数统计',
  48 + },
  49 + {
  50 + key: 'tab2',
  51 + tab: '消息量统计',
  52 + },
  53 + ]
  54 + : [
  55 + {
  56 + key: 'tab1',
  57 + tab: '租户',
  58 + },
  59 + ];
  60 +
46 const dateList = ref(['1小时', '1天', '7天', '30天']); 61 const dateList = ref(['1小时', '1天', '7天', '30天']);
47 const activeIndex = ref(0); 62 const activeIndex = ref(0);
48 function onTabChange(key) { 63 function onTabChange(key) {
1 import { PropType } from 'vue'; 1 import { PropType } from 'vue';
2 - 2 +import type { BasicColumn } from '/@/components/Table';
3 export interface BasicProps { 3 export interface BasicProps {
4 width: string; 4 width: string;
5 height: string; 5 height: string;
@@ -14,3 +14,16 @@ export const basicProps = { @@ -14,3 +14,16 @@ export const basicProps = {
14 default: '280px', 14 default: '280px',
15 }, 15 },
16 }; 16 };
  17 +
  18 +export const columns: BasicColumn[] = [
  19 + {
  20 + title: '租户名称',
  21 + dataIndex: 'tenantName',
  22 + width: 120,
  23 + },
  24 + {
  25 + title: '过期时间',
  26 + dataIndex: 'createdTime',
  27 + width: 120,
  28 + },
  29 +];
1 -export interface GrowCardItem {  
2 - imgUrl: string;  
3 - title: string;  
4 - value: string;  
5 - onLine?: number;  
6 - offLine?: number;  
7 - inactive?: number;  
8 - newDay: string;  
9 -}  
10 -  
11 -export const growCardList: GrowCardItem[] = [  
12 - {  
13 - imgUrl: '/src/assets/images/device-count.png',  
14 - title: '设备数(个)',  
15 - value: '10,000',  
16 - onLine: 2000,  
17 - offLine: 3000,  
18 - inactive: 4000,  
19 - newDay: '123,45',  
20 - },  
21 - {  
22 - imgUrl: '/src/assets/images/alarm-count.png',  
23 - title: '11月告警数(条)',  
24 - value: '11,000',  
25 - newDay: '167,45',  
26 - },  
27 - {  
28 - imgUrl: '/src/assets/images/msg-count.png',  
29 - title: '11月消息量(条)',  
30 - value: '12,000',  
31 - newDay: '198,45',  
32 - },  
33 -];  
1 <template> 1 <template>
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" />  
5 - <SiteAnalysis class="!my-4 enter-y" :loading="loading" />  
6 - <div class="md:flex enter-y"> 4 + <GrowCard :loading="loading" class="enter-y" :role="role" />
  5 + <SiteAnalysis class="!my-4 enter-y" :loading="loading" :role="role" />
  6 + <div class="md:flex enter-y" v-if="role === 'TENANT_ADMIN'">
7 <Card title="核心流程指南" style="width: 100%"> 7 <Card title="核心流程指南" style="width: 100%">
8 <img alt="核心流程指南" src="../../../assets/images/flow.png" /> 8 <img alt="核心流程指南" src="../../../assets/images/flow.png" />
9 </Card> 9 </Card>
10 </div> 10 </div>
11 </div> 11 </div>
12 <div class="md:w-3/10 w-full enter-y"> 12 <div class="md:w-3/10 w-full enter-y">
13 - <HelpDoc /> 13 + <HelpDoc :role="role" />
14 </div> 14 </div>
15 </div> 15 </div>
16 </template> 16 </template>
@@ -20,7 +20,11 @@ @@ -20,7 +20,11 @@
20 import SiteAnalysis from './components/SiteAnalysis.vue'; 20 import SiteAnalysis from './components/SiteAnalysis.vue';
21 import { Card } from 'ant-design-vue'; 21 import { Card } from 'ant-design-vue';
22 import HelpDoc from './components/HelpDoc.vue'; 22 import HelpDoc from './components/HelpDoc.vue';
23 - 23 + import { USER_INFO_KEY } from '/@/enums/cacheEnum';
  24 + import { getAuthCache } from '/@/utils/auth';
  25 + const userInfo: any = getAuthCache(USER_INFO_KEY);
  26 + const role = userInfo.roles[0];
  27 + console.log(role);
24 const loading = ref(true); 28 const loading = ref(true);
25 setTimeout(() => { 29 setTimeout(() => {
26 loading.value = false; 30 loading.value = false;
1 import { formatToDateTime } from '/@/utils/dateUtil'; 1 import { formatToDateTime } from '/@/utils/dateUtil';
2 import { FormSchema } from '/@/components/Form'; 2 import { FormSchema } from '/@/components/Form';
3 import { BasicColumn } from '/@/components/Table'; 3 import { BasicColumn } from '/@/components/Table';
  4 +
4 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; 5 import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
5 6
6 export const columns: BasicColumn[] = [ 7 export const columns: BasicColumn[] = [
@@ -8,25 +9,20 @@ export const columns: BasicColumn[] = [ @@ -8,25 +9,20 @@ export const columns: BasicColumn[] = [
8 title: '设备名称', 9 title: '设备名称',
9 dataIndex: 'name', 10 dataIndex: 'name',
10 width: 120, 11 width: 120,
11 - key: 'name',  
12 }, 12 },
13 { 13 {
14 title: '设备标签', 14 title: '设备标签',
15 dataIndex: 'label', 15 dataIndex: 'label',
16 width: 100, 16 width: 100,
17 - key: 'label',  
18 }, 17 },
19 { 18 {
20 title: '设备配置', 19 title: '设备配置',
21 dataIndex: 'deviceProfile.name', 20 dataIndex: 'deviceProfile.name',
22 width: 160, 21 width: 160,
23 - key: 'deviceProfile.name',  
24 }, 22 },
25 -  
26 { 23 {
27 title: '设备类型', 24 title: '设备类型',
28 dataIndex: 'deviceType', 25 dataIndex: 'deviceType',
29 - key: 'deviceType',  
30 customRender({ text }) { 26 customRender({ text }) {
31 return text === DeviceTypeEnum.GATEWAY 27 return text === DeviceTypeEnum.GATEWAY
32 ? '网关设备' 28 ? '网关设备'
@@ -39,7 +35,6 @@ export const columns: BasicColumn[] = [ @@ -39,7 +35,6 @@ export const columns: BasicColumn[] = [
39 title: '描述', 35 title: '描述',
40 dataIndex: 'description', 36 dataIndex: 'description',
41 width: 180, 37 width: 180,
42 - key: 'description',  
43 }, 38 },
44 ]; 39 ];
45 // 实时数据表格 40 // 实时数据表格
  1 +import { formatToDate } from '/@/utils/dateUtil';
1 import { BasicColumn } from '/@/components/Table'; 2 import { BasicColumn } from '/@/components/Table';
2 import { FormSchema } from '/@/components/Table'; 3 import { FormSchema } from '/@/components/Table';
3 import { DeviceTypeEnum, DeviceState } from '/@/api/device/model/deviceModel'; 4 import { DeviceTypeEnum, DeviceState } from '/@/api/device/model/deviceModel';
@@ -19,6 +20,7 @@ export const columns: BasicColumn[] = [ @@ -19,6 +20,7 @@ export const columns: BasicColumn[] = [
19 dataIndex: 'deviceProfile.name', 20 dataIndex: 'deviceProfile.name',
20 width: 160, 21 width: 160,
21 slots: { customRender: 'deviceProfile' }, 22 slots: { customRender: 'deviceProfile' },
  23 + ellipsis: true,
22 }, 24 },
23 25
24 { 26 {
@@ -36,10 +38,16 @@ export const columns: BasicColumn[] = [ @@ -36,10 +38,16 @@ export const columns: BasicColumn[] = [
36 width: 120, 38 width: 120,
37 slots: { customRender: 'deviceState' }, 39 slots: { customRender: 'deviceState' },
38 }, 40 },
39 -  
40 { 41 {
41 title: '最后连接时间', 42 title: '最后连接时间',
42 - dataIndex: 'lastConnectTime', 43 + dataIndex: 'lastOnlineTime',
  44 + format: (text) => formatToDate(text, 'YYYY-MM-DD HH:mm:ss'),
  45 + width: 180,
  46 + },
  47 + {
  48 + title: '最后断开时间',
  49 + dataIndex: 'lastOfflineTime',
  50 + format: (text) => formatToDate(text, 'YYYY-MM-DD HH:mm:ss'),
43 width: 180, 51 width: 180,
44 }, 52 },
45 ]; 53 ];
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 :destroyOnClose="true" 6 :destroyOnClose="true"
7 @close="closeDrawer" 7 @close="closeDrawer"
8 :title="deviceDetail.name" 8 :title="deviceDetail.name"
9 - width="78%" 9 + width="70%"
10 > 10 >
11 <Tabs v-model:activeKey="activeKey" :size="size" type="card"> 11 <Tabs v-model:activeKey="activeKey" :size="size" type="card">
12 <TabPane key="1" tab="详情" 12 <TabPane key="1" tab="详情"
@@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
10 bordered 10 bordered
11 :columns="columns" 11 :columns="columns"
12 :data-source="[deviceDetail]" 12 :data-source="[deviceDetail]"
  13 + :rowKey="(_, index) => index"
13 :pagination="false" 14 :pagination="false"
14 - rowKey="tbDeviceId"  
15 style="width: 800px" 15 style="width: 800px"
16 /> 16 />
17 </div> 17 </div>
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 </div> 23 </div>
24 <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4"> 24 <div v-if="deviceDetail?.deviceInfo?.address" class="mt-4">
25 <p>设备位置</p> 25 <p>设备位置</p>
26 - <div ref="wrapRef" style="height: 400px; width: 90%"></div> 26 + <div ref="wrapRef" style="height: 400px; width: 100%"></div>
27 </div> 27 </div>
28 </div> 28 </div>
29 </template> 29 </template>