refactor.dashboard: 重构工作台页面
- 移除欢迎信息和分割线 - 添加产品数、设备数等统计卡片 - 新增链式图表组件 - 更新数据面板布局和样式 - 添加设备上报记录功能 -优化地图组件 - 调整产品属性表格
This commit is contained in:
parent
ea1df9d55f
commit
64d2079779
22
src/api/dashboard.ts
Normal file
22
src/api/dashboard.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import axios from 'axios';
|
||||
|
||||
|
||||
// 获取设备信息
|
||||
export function getDeviceInfo() {
|
||||
return axios.get('/api/rest/device/status');
|
||||
}
|
||||
|
||||
// 获取消息信息
|
||||
export function getMessageInfo() {
|
||||
return axios.get('/api/rest/device/record/status');
|
||||
}
|
||||
|
||||
// 获取告警信息
|
||||
export function getAlarmInfo() {
|
||||
return axios.get('/api/rest/device/data/status');
|
||||
}
|
||||
|
||||
// 获取产品信息
|
||||
export function getProductInfo() {
|
||||
return axios.get('/api/rest/product/status');
|
||||
}
|
@ -32,11 +32,12 @@ export interface DeviceUpdateRecord extends DeviceCreateRecord {
|
||||
|
||||
export interface DeviceEventRecord {
|
||||
id: number;
|
||||
clientId: string;
|
||||
clientId: number;
|
||||
serveName: string;
|
||||
params: string;
|
||||
}
|
||||
|
||||
|
||||
// 分页查询
|
||||
export function queryDeviceList(data: DeviceRecord) {
|
||||
return axios({
|
||||
@ -74,11 +75,11 @@ export function deleteDevice(id: number) {
|
||||
}
|
||||
|
||||
// 查询上报
|
||||
export function queryDeviceReport(clientId: number) {
|
||||
export function queryDeviceRecord(data: DeviceRecord) {
|
||||
return axios({
|
||||
url: `/api/rest/device/record/photo`,
|
||||
url: `/api/rest/device/record/data`,
|
||||
method: 'get',
|
||||
params: clientId,
|
||||
params: data,
|
||||
});
|
||||
}
|
||||
|
||||
@ -95,3 +96,5 @@ export function triggerEvent(data: DeviceEventRecord) {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<a-col class="banner">
|
||||
<a-col :span="8">
|
||||
<a-typography-title :heading="5" style="margin-top: 0">
|
||||
{{ $t('workplace.welcome') }} {{ userInfo.name }}
|
||||
</a-typography-title>
|
||||
</a-col>
|
||||
<a-divider class="panel-border" />
|
||||
<!-- <a-col :span="8">-->
|
||||
<!-- <a-typography-title :heading="5" style="margin-top: 0">-->
|
||||
<!-- {{ $t('workplace.welcome') }} {{ userInfo.name }}-->
|
||||
<!-- </a-typography-title>-->
|
||||
<!-- </a-col>-->
|
||||
<!-- <a-divider class="panel-border" />-->
|
||||
</a-col>
|
||||
</template>
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
.banner {
|
||||
width: 100%;
|
||||
padding: 20px 20px 0 20px;
|
||||
background-color: var(--color-bg-2);
|
||||
//background-color: var(--color-bg-2);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
|
264
src/views/dashboard/workplace/components/chain-item.vue
Normal file
264
src/views/dashboard/workplace/components/chain-item.vue
Normal file
@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<a-spin :loading="loading" style="width: 100%">
|
||||
<a-card :bordered="false" :style="cardStyle">
|
||||
<div class="content-wrap">
|
||||
<div class="content">
|
||||
<a-statistic
|
||||
:title="title"
|
||||
:value="renderData.count"
|
||||
:value-from="0"
|
||||
animation
|
||||
show-group-separator
|
||||
/>
|
||||
<div class="desc">
|
||||
<a-typography-text type="secondary" class="label">
|
||||
较昨日
|
||||
</a-typography-text>
|
||||
<a-typography-text type="danger">
|
||||
{{ renderData.growth }}
|
||||
<icon-arrow-rise />
|
||||
</a-typography-text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<Chart v-if="!loading" :option="chartOption" />
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, PropType, CSSProperties } from 'vue';
|
||||
import useLoading from '@/hooks/loading';
|
||||
import useChartOption from '@/hooks/chart-option';
|
||||
|
||||
const barChartOptionsFactory = () => {
|
||||
const data = ref<any>([]);
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 10,
|
||||
bottom: 0,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
},
|
||||
series: {
|
||||
name: 'total',
|
||||
data,
|
||||
type: 'bar',
|
||||
barWidth: 7,
|
||||
itemStyle: {
|
||||
borderRadius: 2,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
return {
|
||||
data,
|
||||
chartOption,
|
||||
};
|
||||
};
|
||||
|
||||
const lineChartOptionsFactory = () => {
|
||||
const data = ref<number[][]>([[], []]);
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 10,
|
||||
bottom: 0,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '2001',
|
||||
data: data.value[0],
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#165DFF',
|
||||
width: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '2002',
|
||||
data: data.value[1],
|
||||
type: 'line',
|
||||
showSymbol: false,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
color: '#6AA1FF',
|
||||
width: 3,
|
||||
type: 'dashed',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
return {
|
||||
data,
|
||||
chartOption,
|
||||
};
|
||||
};
|
||||
|
||||
const pieChartOptionsFactory = () => {
|
||||
const data = ref<any>([]);
|
||||
const { chartOption } = useChartOption(() => {
|
||||
return {
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
top: 'center',
|
||||
right: '0',
|
||||
orient: 'vertical',
|
||||
icon: 'circle',
|
||||
itemWidth: 6,
|
||||
itemHeight: 6,
|
||||
textStyle: {
|
||||
color: '#4E5969',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
show: true,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '总计',
|
||||
type: 'pie',
|
||||
radius: ['50%', '70%'],
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data,
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
return {
|
||||
data,
|
||||
chartOption,
|
||||
};
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
quota: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
chartType: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
cardStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
default: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { loading, setLoading } = useLoading(true);
|
||||
const { chartOption: lineChartOption, data: lineData } =
|
||||
lineChartOptionsFactory();
|
||||
const { chartOption: barChartOption, data: barData } =
|
||||
barChartOptionsFactory();
|
||||
const { chartOption: pieChartOption, data: pieData } =
|
||||
pieChartOptionsFactory();
|
||||
const renderData = ref<any>({
|
||||
count: 0,
|
||||
growth: 0,
|
||||
chartData: [],
|
||||
});
|
||||
const chartOption = ref({});
|
||||
try {
|
||||
// const { data } = renderData.value
|
||||
// renderData.value = data;
|
||||
if (props.chartType === 'bar') {
|
||||
chartOption.value = barChartOption.value;
|
||||
} else if (props.chartType === 'line') {
|
||||
chartOption.value = lineChartOption.value;
|
||||
} else {
|
||||
chartOption.value = pieChartOption.value;
|
||||
}
|
||||
} catch (err) {
|
||||
// you can report use errorHandler or other
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-card) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
:deep(.arco-card-body) {
|
||||
width: 100%;
|
||||
height: 134px;
|
||||
padding: 0;
|
||||
}
|
||||
.content-wrap {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
:deep(.content) {
|
||||
float: left;
|
||||
width: 108px;
|
||||
height: 102px;
|
||||
}
|
||||
:deep(.arco-statistic) {
|
||||
.arco-statistic-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.arco-statistic-content {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
float: right;
|
||||
width: calc(100% - 108px);
|
||||
height: 90px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-right: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
@ -1,158 +1,203 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<a-grid :cols="24" :row-gap="16" class="panel">
|
||||
<a-grid :cols="24" :row-gap="10" :col-gap="10" class="panel">
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 8, xl: 6, xxl: 6 }"
|
||||
>
|
||||
<a-space @click="to('PASS')">
|
||||
<a-avatar :size="54" class="col-avatar">
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/288b89194e657603ff40db39e8072640.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-statistic
|
||||
title="通过"
|
||||
:value="formData.pass || 0"
|
||||
:value-from="0"
|
||||
show-group-separator
|
||||
>
|
||||
<template #suffix>
|
||||
<span class="unit">{{ $t('workplace.pecs') }}</span>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
<a-card :bordered="false" class="square-card">
|
||||
<a-row align="center">
|
||||
<a-col :span="12">
|
||||
<a-statistic
|
||||
title="产品数"
|
||||
value-style="font-size:36px"
|
||||
:value="365"
|
||||
:value-from="0"
|
||||
animation
|
||||
>
|
||||
</a-statistic>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-avatar
|
||||
:size="80"
|
||||
image-url="http://iot.le5le.cn/iot/img/%E4%BA%A7%E5%93%81.png"
|
||||
shape="square"
|
||||
:style="{ backgroundColor: '#ffffff' }"
|
||||
/>
|
||||
<!-- <a-avatar-->
|
||||
<!-- :size="80"-->
|
||||
<!-- image-url="http://iot.le5le.cn/iot/img/%E6%95%B0%E6%8D%AE.png"-->
|
||||
<!-- shape="square"-->
|
||||
<!-- :style="{ backgroundColor: '#ffffff' }"-->
|
||||
<!-- />-->
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-descriptions :data="data" layout="inline-vertical" class="responsive-margin" />
|
||||
|
||||
|
||||
</a-card>
|
||||
</a-grid-item>
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 8, xl: 6, xxl: 6 }"
|
||||
>
|
||||
<a-space @click="to('FAILED')">
|
||||
<a-avatar :size="54" class="col-avatar">
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/fdc66b07224cdf18843c6076c2587eb5.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-statistic
|
||||
title="未通过"
|
||||
:value="formData.notPass || 0"
|
||||
:value-from="0"
|
||||
show-group-separator
|
||||
>
|
||||
<template #suffix>
|
||||
<span class="unit">{{ $t('workplace.pecs') }}</span>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
<a-card :bordered="false" class="square-card">
|
||||
<a-row align="center">
|
||||
<a-col :span="12">
|
||||
<a-statistic
|
||||
title="设备数"
|
||||
value-style="font-size:36px"
|
||||
:value="deviceInfo.deviceCount"
|
||||
:value-from="0"
|
||||
animation
|
||||
>
|
||||
</a-statistic>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-avatar
|
||||
:size="80"
|
||||
image-url="http://iot.le5le.cn/iot/img/%E8%AE%BE%E5%A4%87.png"
|
||||
shape="square"
|
||||
:style="{ backgroundColor: '#ffffff' }"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-descriptions :data="data" layout="inline-vertical" class="responsive-margin" />
|
||||
|
||||
|
||||
</a-card>
|
||||
</a-grid-item>
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 8, xl: 6, xxl: 6 }"
|
||||
>
|
||||
<a-space @click="to('EXAMINE')">
|
||||
<a-avatar :size="54" class="col-avatar">
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/77d74c9a245adeae1ec7fb5d4539738d.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-statistic
|
||||
title="未审计"
|
||||
:value="formData.notAudit || 0"
|
||||
:value-from="0"
|
||||
show-group-separator
|
||||
>
|
||||
<template #suffix>
|
||||
<span class="unit">{{ $t('workplace.pecs') }}</span>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
<a-card :bordered="false" class="square-card">
|
||||
<a-row align="center">
|
||||
<a-col :span="12">
|
||||
<a-statistic
|
||||
title="产品数"
|
||||
value-style="font-size:36px"
|
||||
:value="365"
|
||||
:value-from="0"
|
||||
animation
|
||||
>
|
||||
</a-statistic>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-avatar
|
||||
:size="80"
|
||||
image-url="http://iot.le5le.cn/iot/img/%E5%91%8A%E8%AD%A6.png"
|
||||
shape="square"
|
||||
:style="{ backgroundColor: '#ffffff' }"
|
||||
/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-descriptions :data="data" layout="inline-vertical" class="responsive-margin" />
|
||||
|
||||
|
||||
</a-card>
|
||||
</a-grid-item>
|
||||
<a-grid-item
|
||||
v-if="userStore.permissions !== 'auditor'"
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
|
||||
style="border-right: none"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 8, xl: 6, xxl: 6 }"
|
||||
>
|
||||
<a-space @click="to('SUBMIT')">
|
||||
<a-avatar :size="54" class="col-avatar">
|
||||
<img
|
||||
alt="avatar"
|
||||
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/c8b36e26d2b9bb5dbf9b74dd6d7345af.svg~tplv-49unhts6dw-image.image"
|
||||
/>
|
||||
</a-avatar>
|
||||
<a-statistic
|
||||
v-model="formData.notFiled"
|
||||
title="未归档"
|
||||
:value="formData.notFiled || 0"
|
||||
:value-from="0"
|
||||
show-group-separator
|
||||
>
|
||||
<template #suffix>
|
||||
<span class="unit">{{ $t('workplace.pecs') }}</span>
|
||||
</template>
|
||||
</a-statistic>
|
||||
</a-space>
|
||||
<Announcement />
|
||||
</a-grid-item>
|
||||
<a-grid-item :span="24">
|
||||
<a-divider class="panel-border" />
|
||||
</a-grid-item>
|
||||
</a-grid>
|
||||
<a-grid :span="24" :row-gap="10" :col-gap="10" class="panel">
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 8, xxl: 8 }">
|
||||
<ChainItem
|
||||
title="分享总量"
|
||||
quota="share"
|
||||
chart-type="pie"
|
||||
:card-style="{
|
||||
background: isDark
|
||||
? 'linear-gradient(180deg, #312565 0%, #201936 100%)'
|
||||
: 'linear-gradient(180deg, #F7F7FF 0%, #ECECFF 100%)',
|
||||
}"
|
||||
/>
|
||||
</a-grid-item>
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 8, xxl: 8 }">
|
||||
<ChainItem
|
||||
title="分享总量"
|
||||
quota="share"
|
||||
chart-type="pie"
|
||||
:card-style="{
|
||||
background: isDark
|
||||
? 'linear-gradient(180deg, #312565 0%, #201936 100%)'
|
||||
: 'linear-gradient(180deg, #F7F7FF 0%, #ECECFF 100%)',
|
||||
}"
|
||||
/>
|
||||
</a-grid-item>
|
||||
<a-grid-item
|
||||
class="panel-col"
|
||||
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 8, xxl: 8 }">
|
||||
<ChainItem
|
||||
title="分享总量"
|
||||
quota="share"
|
||||
chart-type="pie"
|
||||
:card-style="{
|
||||
background: isDark
|
||||
? 'linear-gradient(180deg, #312565 0%, #201936 100%)'
|
||||
: 'linear-gradient(180deg, #F7F7FF 0%, #ECECFF 100%)',
|
||||
}"
|
||||
/>
|
||||
</a-grid-item>
|
||||
<a-grid-item :span="24">
|
||||
<a-divider class="panel-border" />
|
||||
</a-grid-item>
|
||||
</a-grid>
|
||||
<a-card :bordered="false">
|
||||
<a-space id="TicketEcharts" style="width: 98%; height: 400px"></a-space>
|
||||
</a-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useUserStore } from '@/store';
|
||||
import Announcement from '@/views/dashboard/workplace/components/announcement.vue';
|
||||
import ChainItem from '@/views/dashboard/workplace/components/chain-item.vue';
|
||||
import useThemes from '@/hooks/themes';
|
||||
import { getDeviceInfo } from '@/api/dashboard';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import router from '@/router';
|
||||
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const formData = ref({
|
||||
notAudit: undefined,
|
||||
notFiled: undefined,
|
||||
notPass: undefined,
|
||||
pass: undefined,
|
||||
});
|
||||
|
||||
const getHomeData = async (params: {
|
||||
auditorId: number | undefined;
|
||||
userId: number | string | undefined;
|
||||
}) => {
|
||||
if (userStore.permissions === 'admin') {
|
||||
params.userId = '';
|
||||
} else if (userStore.permissions === 'auditor') {
|
||||
params.auditorId = userStore.id;
|
||||
} else if (userStore.permissions === 'user') {
|
||||
params.userId = userStore.id;
|
||||
}
|
||||
};
|
||||
|
||||
const to = (key: string) => {
|
||||
router.push({
|
||||
name: 'TicketManage',
|
||||
query: {
|
||||
status: key,
|
||||
},
|
||||
});
|
||||
};
|
||||
getHomeData({});
|
||||
const data = [{
|
||||
label: 'Name',
|
||||
value: 'Socrates',
|
||||
}, {
|
||||
label: 'Mobile',
|
||||
value: '123',
|
||||
}, {
|
||||
label: 'Residence',
|
||||
value: 'Beijing'
|
||||
}];
|
||||
const deviceInfo = ref({
|
||||
deviceCount: 0,
|
||||
onlineCount: 0,
|
||||
offlineCount: 0,
|
||||
alarmCount: 0,
|
||||
productCount: 0,
|
||||
})
|
||||
const fetchData = async () => {
|
||||
const res = await getDeviceInfo();
|
||||
deviceInfo.value = res.data;
|
||||
}
|
||||
const { isDark } = useThemes();
|
||||
onMounted(() => {
|
||||
fetchData();
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.arco-grid.panel {
|
||||
margin-bottom: 0;
|
||||
padding: 16px 20px 0 20px;
|
||||
padding: 10px 20px 0 0;
|
||||
}
|
||||
.panel-col {
|
||||
padding-left: 43px;
|
||||
padding-left: 20px; // 修改: 调整内边距以使卡片看起来更大
|
||||
border-right: 1px solid rgb(var(--gray-2));
|
||||
}
|
||||
.col-avatar {
|
||||
@ -170,4 +215,14 @@
|
||||
:deep(.panel-border) {
|
||||
margin: 4px 0 0 0;
|
||||
}
|
||||
.square-card {
|
||||
width: 100%;
|
||||
//aspect-ratio: 4 / 3; // 修改: 将卡片的长宽比改为4:3
|
||||
padding: 15px 20px 13px 20px;
|
||||
}
|
||||
.responsive-margin {
|
||||
margin-top: 20px; // 默认值
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -16,21 +16,23 @@
|
||||
</a-grid-item>
|
||||
</a-grid>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
<a-grid :cols="24" :row-gap="16">
|
||||
<a-grid-item :span="24">
|
||||
<div class="panel moduler-wrap">
|
||||
<QuickOperation />
|
||||
<RecentlyVisited />
|
||||
</div>
|
||||
</a-grid-item>
|
||||
<a-grid-item class="panel" :span="24"> </a-grid-item>
|
||||
<a-grid-item class="panel" :span="24">
|
||||
<Announcement />
|
||||
</a-grid-item>
|
||||
<a-grid-item class="panel" :span="24"> </a-grid-item>
|
||||
</a-grid>
|
||||
</div>
|
||||
<!-- <div class="right-side">-->
|
||||
<!-- <a-grid :cols="24" :row-gap="16">-->
|
||||
<!-- <a-grid-item :span="24">-->
|
||||
<!-- <div class="panel moduler-wrap">-->
|
||||
<!--<!– <QuickOperation />–>-->
|
||||
<!--<!– <RecentlyVisited />–>-->
|
||||
<!-- </div>-->
|
||||
<!-- </a-grid-item>-->
|
||||
<!-- <a-grid-item class="panel" :span="24">-->
|
||||
<!-- <Announcement />-->
|
||||
<!-- </a-grid-item>-->
|
||||
<!-- <a-grid-item class="panel" :span="24">-->
|
||||
<!-- <Announcement />-->
|
||||
<!-- </a-grid-item>-->
|
||||
<!-- <a-grid-item class="panel" :span="24"> </a-grid-item>-->
|
||||
<!-- </a-grid>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -50,7 +52,7 @@
|
||||
|
||||
<style lang="less" scoped>
|
||||
.container {
|
||||
background-color: var(--color-fill-2);
|
||||
//background-color: var(--color-fill-2);
|
||||
padding: 16px 20px;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
@ -67,7 +69,7 @@
|
||||
}
|
||||
|
||||
.panel {
|
||||
background-color: var(--color-bg-2);
|
||||
//background-color: var(--color-bg-2);
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
@ -77,7 +79,7 @@
|
||||
}
|
||||
.moduler-wrap {
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-bg-2);
|
||||
//background-color: var(--color-bg-2);
|
||||
:deep(.text) {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
|
@ -63,6 +63,9 @@
|
||||
<a-tab-pane key="3" tab="执行服务" title="执行服务">
|
||||
执行服务内容
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="4" tab="上报记录" title="上报记录">
|
||||
<a-table :columns="deviceReportColumns" :data="deviceReportData" />
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
</div>
|
||||
@ -72,7 +75,7 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { queryDeviceDetail } from '@/api/device';
|
||||
import { queryDeviceDetail, queryDeviceRecord } from '@/api/device';
|
||||
|
||||
const route = useRoute();
|
||||
const id = Number(route.params.id);
|
||||
@ -98,11 +101,29 @@
|
||||
slotName: 'type',
|
||||
},
|
||||
];
|
||||
const deviceReportColumns = [
|
||||
{
|
||||
title: '上报时间',
|
||||
dataIndex: 'recordTime',
|
||||
slotName: 'recordTime',
|
||||
},
|
||||
{
|
||||
title: '内容',
|
||||
dataIndex: 'content',
|
||||
slotName: 'content',
|
||||
},
|
||||
];
|
||||
const activeKey = ref('1');
|
||||
const renderData = ref([]);
|
||||
const deviceReportData = ref([]);
|
||||
const fetchData = async (Id: number) => {
|
||||
const res = await queryDeviceDetail(Id);
|
||||
renderData.value = res.data;
|
||||
const res1 = await queryDeviceRecord({
|
||||
size: 10,
|
||||
current: 1
|
||||
});
|
||||
deviceReportData.value = res1.data;
|
||||
};
|
||||
const handleMenuClick = (e: any) => {
|
||||
activeKey.value = e;
|
||||
|
@ -6,13 +6,6 @@
|
||||
</a-layout-sider>
|
||||
<a-layout-content>
|
||||
<div id="container"></div>
|
||||
<!-- 标记点信息弹出框 -->
|
||||
<a-modal v-model:visible="infoVisible" title="设备信息" :footer="null">
|
||||
<p>ID: {{ selectedDevice.id }}</p>
|
||||
<p>名称: {{ selectedDevice.name }}</p>
|
||||
<p>状态: {{ selectedDevice.state }}</p>
|
||||
<p>在线状态: {{ selectedDevice.online ? '是' : '否' }}</p>
|
||||
</a-modal>
|
||||
</a-layout-content>
|
||||
</a-layout>
|
||||
</template>
|
||||
@ -24,43 +17,46 @@
|
||||
import "@amap/amap-jsapi-types";
|
||||
|
||||
let map: any = null;
|
||||
const selectedDevice = ref<DeviceRecord | null>(null);
|
||||
const infoVisible = ref(false);
|
||||
const deviceList = ref<DeviceRecord[]>([]);
|
||||
const markerContent = `<div class="custom-content-marker">
|
||||
<img src="//a.amap.com/jsapi_demos/static/demo-center/icons/dir-via-marker.png">
|
||||
<div class="close-btn">X</div>
|
||||
</div>`;
|
||||
|
||||
|
||||
|
||||
AMapLoader.load({
|
||||
key: 'a4e80eed798a56451b226dcfca81b846', // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
})
|
||||
.then((AMap) => {
|
||||
map = new AMap.Map('container', {
|
||||
// 设置地图容器id
|
||||
viewMode: '3D', // 是否为3D地图模式
|
||||
zoom: 11, // 初始化地图级别
|
||||
center: [116.397428, 39.90923], // 初始化地图中心点位置
|
||||
});
|
||||
map.on('click', (e: any) => {
|
||||
// 获取点击位置的经纬度
|
||||
const lngLat = e.lnglat;
|
||||
const content = document.createElement('div');
|
||||
content.className = 'custom-content-marker';
|
||||
content.innerHTML = `
|
||||
<img src="https://webapi.amap.com/theme/v1.3/markers/n/mark_b.png" alt="标记点" />
|
||||
<div class="close-btn">×</div>`;
|
||||
// 创建标记点
|
||||
const marker = new AMap.Marker({
|
||||
position: lngLat,
|
||||
content,
|
||||
});
|
||||
// 将标记点添加到地图上
|
||||
map.add(marker);
|
||||
// 监听删除按钮的点击事件
|
||||
const closeBtn = marker.getContent().querySelector('.close-btn');
|
||||
closeBtn.addEventListener('click', () => {
|
||||
map.remove(marker); // 移除标记点
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
AMapLoader.load({
|
||||
key: 'a4e80eed798a56451b226dcfca81b846', // 申请好的Web端开发者Key,首次调用 load 时必填
|
||||
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
|
||||
plugins: [], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
|
||||
})
|
||||
.then((AMap) => {
|
||||
map = new AMap.Map('container', {
|
||||
// 设置地图容器id
|
||||
viewMode: '3D', // 是否为3D地图模式
|
||||
zoom: 11, // 初始化地图级别
|
||||
center: [116.397428, 39.90923], // 初始化地图中心点位置
|
||||
});
|
||||
// 创建标记点
|
||||
const position = new AMap.LngLat(116.397428, 39.90923);
|
||||
const marker = new AMap.Marker({
|
||||
position,
|
||||
content: markerContent,
|
||||
offset: new AMap.Pixel(-13, -30),
|
||||
});
|
||||
map.add(marker);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// 获取设备列表
|
||||
queryDeviceList({
|
||||
current: 1,
|
||||
@ -74,9 +70,9 @@
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
map?.destroy();
|
||||
});
|
||||
// onUnmounted(() => {
|
||||
// map?.destroy();
|
||||
// });
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -23,7 +23,11 @@
|
||||
<template #icon><icon-plus /></template>
|
||||
新建
|
||||
</a-button>
|
||||
<a-table :columns="columns" :data="propertyData">
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data="propertyData"
|
||||
:pagination="true"
|
||||
>
|
||||
<template #operation="{ record }">
|
||||
<a-button
|
||||
v-permission="['iot:property:delete']"
|
||||
@ -426,6 +430,7 @@
|
||||
} from '@/api/tsl';
|
||||
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
|
||||
import { FormInstance } from '@arco-design/web-vue/es/form';
|
||||
import usePagination from '@/hooks/pagination';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
const { visible, setVisible } = useVisible();
|
||||
@ -579,7 +584,7 @@
|
||||
|
||||
const fetchData = async (Id: number) => {
|
||||
const params = {
|
||||
size: 10,
|
||||
size: 50,
|
||||
current: 1,
|
||||
productId: Id,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user