fix: 运行状态详情查看
This commit is contained in:
parent
15be308dc8
commit
25ee85681c
|
@ -37,6 +37,7 @@
|
||||||
"unplugin-vue-components": "^0.22.12",
|
"unplugin-vue-components": "^0.22.12",
|
||||||
"vite-plugin-monaco-editor": "^1.1.0",
|
"vite-plugin-monaco-editor": "^1.1.0",
|
||||||
"vue": "^3.2.45",
|
"vue": "^3.2.45",
|
||||||
|
"vue-json-viewer": "^3.0.4",
|
||||||
"vue-router": "^4.1.6",
|
"vue-router": "^4.1.6",
|
||||||
"vue3-markdown-it": "^1.0.10",
|
"vue3-markdown-it": "^1.0.10",
|
||||||
"vue3-ts-jsoneditor": "^2.7.1"
|
"vue3-ts-jsoneditor": "^2.7.1"
|
||||||
|
|
|
@ -467,3 +467,19 @@ export const saveEdgeMap = (deviceId: string, data?: any) => server.post(`/edge/
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getPropertyData = (deviceId: string, params: Record<string, unknown>) => server.get(`/device-instance/${deviceId}/properties/_query`, params)
|
export const getPropertyData = (deviceId: string, params: Record<string, unknown>) => server.get(`/device-instance/${deviceId}/properties/_query`, params)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聚合查询设备属性
|
||||||
|
* @param deviceId
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getPropertiesInfo = (deviceId: string, data: Record<string, unknown>) => server.post(`/device-instance/${deviceId}/agg/_query`, data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 聚合查询设备属性
|
||||||
|
* @param deviceId
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getPropertiesList = (deviceId: string, property: string, data: Record<string, unknown>) => server.post(`/device-instance/${deviceId}/property/${property}/_query`, data)
|
|
@ -4,6 +4,8 @@ import { filterAsnycRouter, MenuItem } from '@/utils/menu'
|
||||||
import { isArray } from 'lodash-es'
|
import { isArray } from 'lodash-es'
|
||||||
import { usePermissionStore } from './permission'
|
import { usePermissionStore } from './permission'
|
||||||
import router from '@/router'
|
import router from '@/router'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { onlyMessage } from '@/utils/comm'
|
||||||
|
|
||||||
const defaultOwnParams = [
|
const defaultOwnParams = [
|
||||||
{
|
{
|
||||||
|
@ -77,6 +79,7 @@ export const useMenuStore = defineStore({
|
||||||
name, params, query
|
name, params, query
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
onlyMessage('暂无权限,请联系管理员', 'error')
|
||||||
console.warn(`没有找到对应的页面: ${name}`)
|
console.warn(`没有找到对应的页面: ${name}`)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import AIcon from "@/components/AIcon";
|
import AIcon from "@/components/AIcon";
|
||||||
|
import { useInstanceStore } from "@/store/instance";
|
||||||
|
import { useMenuStore } from "@/store/menu";
|
||||||
import { Button, Descriptions, Modal } from "ant-design-vue"
|
import { Button, Descriptions, Modal } from "ant-design-vue"
|
||||||
import styles from './index.module.less'
|
import styles from './index.module.less'
|
||||||
|
|
||||||
|
@ -14,6 +16,10 @@ const ManualInspection = defineComponent({
|
||||||
|
|
||||||
const { data } = props
|
const { data } = props
|
||||||
|
|
||||||
|
const instanceStore = useInstanceStore();
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
const dataRender = () => {
|
const dataRender = () => {
|
||||||
if (data.type === 'device' || data.type === 'product') {
|
if (data.type === 'device' || data.type === 'product') {
|
||||||
return (
|
return (
|
||||||
|
@ -207,7 +213,13 @@ const ManualInspection = defineComponent({
|
||||||
emit('save', data)
|
emit('save', data)
|
||||||
}}
|
}}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
// TODO 跳转设备和产品
|
if (data.type === 'device') {
|
||||||
|
instanceStore.tabActiveKey = 'Info'
|
||||||
|
} else if (data.type === 'product') {
|
||||||
|
menuStory.jumpPage('device/Product/Detail', { id: data.productId, tab: 'access' });
|
||||||
|
} else {
|
||||||
|
menuStory.jumpPage('link/AccessConfig/Detail', { id: data.configuration?.id });
|
||||||
|
}
|
||||||
}}>
|
}}>
|
||||||
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
<div style={{ display: 'flex' }}>{dataRender()}</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import _ from "lodash"
|
||||||
import DiagnosticAdvice from './DiagnosticAdvice'
|
import DiagnosticAdvice from './DiagnosticAdvice'
|
||||||
import ManualInspection from './ManualInspection'
|
import ManualInspection from './ManualInspection'
|
||||||
import { deployDevice } from "@/api/initHome"
|
import { deployDevice } from "@/api/initHome"
|
||||||
|
import PermissionButton from '@/components/PermissionButton/index.vue'
|
||||||
|
import { useMenuStore } from "@/store/menu"
|
||||||
|
|
||||||
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel'
|
||||||
|
|
||||||
|
@ -41,6 +43,7 @@ const Status = defineComponent({
|
||||||
const diagnoseData = ref<Partial<Record<string, any>>>()
|
const diagnoseData = ref<Partial<Record<string, any>>>()
|
||||||
|
|
||||||
const bindParentVisible = ref<boolean>(false)
|
const bindParentVisible = ref<boolean>(false)
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
const configuration = reactive<{
|
const configuration = reactive<{
|
||||||
product: Record<string, any>,
|
product: Record<string, any>,
|
||||||
|
@ -57,19 +60,8 @@ const Status = defineComponent({
|
||||||
artificialData.value = params
|
artificialData.value = params
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
const jumpAccessConfig = () => {
|
const jumpAccessConfig = () => {
|
||||||
// const purl = getMenuPathByCode(MENUS_CODE['device/Product/Detail']);
|
menuStory.jumpPage('device/Product/Detail', { id: unref(device).productId, tab: 'access' });
|
||||||
// if (purl) {
|
|
||||||
// history.push(
|
|
||||||
// `${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], device.productId)}`,
|
|
||||||
// {
|
|
||||||
// tab: 'access',
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// } else {
|
|
||||||
// message.error('规则可能有加密处理,请联系管理员');
|
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const jumpDeviceConfig = () => {
|
const jumpDeviceConfig = () => {
|
||||||
|
@ -123,34 +115,40 @@ const Status = defineComponent({
|
||||||
<Badge
|
<Badge
|
||||||
status="default"
|
status="default"
|
||||||
text={
|
text={
|
||||||
<span>网络组件已禁用,请先<Popconfirm
|
<span>网络组件已禁用,请先
|
||||||
title="确认启用"
|
<PermissionButton
|
||||||
onConfirm={async () => {
|
type="link"
|
||||||
const res = await startNetwork(
|
hasPermission="link/Type:action"
|
||||||
unref(gateway)?.channelId,
|
popConfirm={{
|
||||||
);
|
title: '确认启用',
|
||||||
if (res.status === 200) {
|
onConfirm: async () => {
|
||||||
message.success('操作成功!');
|
const res = await startNetwork(
|
||||||
list.value = modifyArrayList(
|
unref(gateway)?.channelId,
|
||||||
list.value,
|
);
|
||||||
{
|
if (res.status === 200) {
|
||||||
key: 'network',
|
message.success('操作成功!');
|
||||||
name: '网络组件',
|
list.value = modifyArrayList(
|
||||||
desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
|
list.value,
|
||||||
status: 'success',
|
{
|
||||||
text: '正常',
|
key: 'network',
|
||||||
info: null,
|
name: '网络组件',
|
||||||
},
|
desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败',
|
||||||
);
|
status: 'success',
|
||||||
}
|
text: '正常',
|
||||||
}}
|
info: null,
|
||||||
>
|
},
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
);
|
||||||
</Popconfirm></span>
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
启用
|
||||||
|
</PermissionButton>
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div >
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<div class={styles.infoItem}>
|
<div class={styles.infoItem}>
|
||||||
|
@ -287,28 +285,31 @@ const Status = defineComponent({
|
||||||
<Badge
|
<Badge
|
||||||
status="default"
|
status="default"
|
||||||
text={<span>设备接入网关已禁用,请先
|
text={<span>设备接入网关已禁用,请先
|
||||||
<Popconfirm
|
<PermissionButton
|
||||||
title="确认启用"
|
hasPermission="link/Type:action"
|
||||||
onConfirm={async () => {
|
popConfirm={{
|
||||||
const resp = await startGateway(unref(device).accessId || '');
|
title: '确认启用',
|
||||||
if (resp.status === 200) {
|
onConfirm: async () => {
|
||||||
message.success('操作成功!');
|
const resp = await startGateway(unref(device).accessId || '');
|
||||||
list.value = modifyArrayList(
|
if (resp.status === 200) {
|
||||||
list.value,
|
message.success('操作成功!');
|
||||||
{
|
list.value = modifyArrayList(
|
||||||
key: 'gateway',
|
list.value,
|
||||||
name: '设备接入网关',
|
{
|
||||||
desc: desc,
|
key: 'gateway',
|
||||||
status: 'success',
|
name: '设备接入网关',
|
||||||
text: '正常',
|
desc: desc,
|
||||||
info: null,
|
status: 'success',
|
||||||
},
|
text: '正常',
|
||||||
);
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
启用
|
||||||
</Popconfirm>
|
</PermissionButton>
|
||||||
</span>}
|
</span>}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -411,28 +412,32 @@ const Status = defineComponent({
|
||||||
status="default"
|
status="default"
|
||||||
text={
|
text={
|
||||||
<span>
|
<span>
|
||||||
设备接入网关已禁用,请先<Popconfirm
|
设备接入网关已禁用,请先
|
||||||
title="确认启用"
|
<PermissionButton
|
||||||
onConfirm={async () => {
|
hasPermission="link/AccessConfig:action"
|
||||||
const resp = await startGateway(unref(device).accessId || '');
|
popConfirm={{
|
||||||
if (resp.status === 200) {
|
title: '确认启用',
|
||||||
message.success('操作成功!');
|
onConfirm: async () => {
|
||||||
list.value = modifyArrayList(
|
const resp = await startGateway(unref(device).accessId || '');
|
||||||
list.value,
|
if (resp.status === 200) {
|
||||||
{
|
message.success('操作成功!');
|
||||||
key: 'gateway',
|
list.value = modifyArrayList(
|
||||||
name: '设备接入网关',
|
list.value,
|
||||||
desc: desc,
|
{
|
||||||
status: 'success',
|
key: 'gateway',
|
||||||
text: '正常',
|
name: '设备接入网关',
|
||||||
info: null,
|
desc: desc,
|
||||||
},
|
status: 'success',
|
||||||
);
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
启用
|
||||||
</Popconfirm>
|
</PermissionButton>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -519,28 +524,32 @@ const Status = defineComponent({
|
||||||
status="default"
|
status="default"
|
||||||
text={
|
text={
|
||||||
<span>
|
<span>
|
||||||
网关父设备已禁用,请先<Popconfirm
|
网关父设备已禁用,请先
|
||||||
title="确认启用"
|
<PermissionButton
|
||||||
onConfirm={async () => {
|
hasPermission="device/Product:action"
|
||||||
const resp = await _deploy(response?.result?.id || '');
|
popConfirm={{
|
||||||
if (resp.status === 200) {
|
title: '确认启用',
|
||||||
message.success('操作成功!');
|
onConfirm: async () => {
|
||||||
list.value = modifyArrayList(
|
const resp = await _deploy(response?.result?.id || '');
|
||||||
list.value,
|
if (resp.status === 200) {
|
||||||
{
|
message.success('操作成功!');
|
||||||
key: 'parent-device',
|
list.value = modifyArrayList(
|
||||||
name: '网关父设备',
|
list.value,
|
||||||
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
{
|
||||||
status: 'success',
|
key: 'parent-device',
|
||||||
text: '正常',
|
name: '网关父设备',
|
||||||
info: null,
|
desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败',
|
||||||
},
|
status: 'success',
|
||||||
);
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
启用
|
||||||
</Popconfirm>
|
</PermissionButton>
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -623,28 +632,32 @@ const Status = defineComponent({
|
||||||
status="default"
|
status="default"
|
||||||
text={
|
text={
|
||||||
<span>
|
<span>
|
||||||
产品已禁用,请<Popconfirm
|
产品已禁用,请
|
||||||
title="确认启用"
|
<PermissionButton
|
||||||
onConfirm={async () => {
|
hasPermission="device/Product:action"
|
||||||
const resp = await _deployProduct(unref(device).productId || '');
|
popConfirm={{
|
||||||
if (resp.status === 200) {
|
title: '确认启用',
|
||||||
message.success('操作成功!');
|
onConfirm: async () => {
|
||||||
list.value = modifyArrayList(
|
const resp = await _deployProduct(unref(device).productId || '');
|
||||||
list.value,
|
if (resp.status === 200) {
|
||||||
{
|
message.success('操作成功!');
|
||||||
key: 'product',
|
list.value = modifyArrayList(
|
||||||
name: '产品状态',
|
list.value,
|
||||||
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
{
|
||||||
status: 'success',
|
key: 'product',
|
||||||
text: '正常',
|
name: '产品状态',
|
||||||
info: null,
|
desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败',
|
||||||
},
|
status: 'success',
|
||||||
);
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
启用
|
||||||
</Popconfirm>
|
</PermissionButton>
|
||||||
产品
|
产品
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
|
@ -695,29 +708,34 @@ const Status = defineComponent({
|
||||||
status="default"
|
status="default"
|
||||||
text={
|
text={
|
||||||
<span>
|
<span>
|
||||||
设备已禁用,请<Popconfirm
|
设备已禁用,请
|
||||||
title="确认启用"
|
<PermissionButton
|
||||||
onConfirm={async () => {
|
hasPermission="device/Instance:action"
|
||||||
const resp = await _deploy(unref(device)?.id || '');
|
popConfirm={{
|
||||||
if (resp.status === 200) {
|
title: '确认启用',
|
||||||
instanceStore.current.state = { value: 'offline', text: '离线' }
|
onConfirm: async () => {
|
||||||
message.success('操作成功!');
|
const resp = await _deploy(unref(device)?.id || '');
|
||||||
list.value = modifyArrayList(
|
if (resp.status === 200) {
|
||||||
list.value,
|
instanceStore.current.state = { value: 'offline', text: '离线' }
|
||||||
{
|
message.success('操作成功!');
|
||||||
key: 'device',
|
list.value = modifyArrayList(
|
||||||
name: '设备状态',
|
list.value,
|
||||||
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
{
|
||||||
status: 'success',
|
key: 'device',
|
||||||
text: '正常',
|
name: '设备状态',
|
||||||
info: null,
|
desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败',
|
||||||
},
|
status: 'success',
|
||||||
);
|
text: '正常',
|
||||||
|
info: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="link" style="padding: 0">启用</Button>
|
启用
|
||||||
</Popconfirm>设备
|
</PermissionButton>
|
||||||
|
设备
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="_getEventList"
|
:request="_getEventList"
|
||||||
model="TABLE"
|
model="TABLE"
|
||||||
:bodyStyle="{padding: '0 24px'}"
|
:bodyStyle="{ padding: '0 24px' }"
|
||||||
>
|
>
|
||||||
<template #timestamp="slotProps">
|
<template #timestamp="slotProps">
|
||||||
{{ moment(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
{{ moment(slotProps.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
|
@ -19,18 +19,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import moment from 'moment'
|
import moment from 'moment';
|
||||||
import { getEventList } from '@/api/device/instance'
|
import { getEventList } from '@/api/device/instance';
|
||||||
import { useInstanceStore } from '@/store/instance'
|
import { useInstanceStore } from '@/store/instance';
|
||||||
import { Modal } from 'ant-design-vue'
|
import { Modal } from 'ant-design-vue';
|
||||||
|
|
||||||
const events = defineProps({
|
const events = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {}
|
default: () => {},
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const instanceStore = useInstanceStore()
|
const instanceStore = useInstanceStore();
|
||||||
|
|
||||||
const columns = ref<Record<string, any>>([
|
const columns = ref<Record<string, any>>([
|
||||||
{
|
{
|
||||||
|
@ -38,43 +38,52 @@ const columns = ref<Record<string, any>>([
|
||||||
dataIndex: 'timestamp',
|
dataIndex: 'timestamp',
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'date',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
}
|
},
|
||||||
])
|
]);
|
||||||
const params = ref<Record<string, any>>({})
|
const params = ref<Record<string, any>>({});
|
||||||
|
|
||||||
const _getEventList = () => getEventList(instanceStore.current.id || '', events.data.id || '', params.value)
|
const _getEventList = () =>
|
||||||
|
getEventList(
|
||||||
|
instanceStore.current.id || '',
|
||||||
|
events.data.id || '',
|
||||||
|
params.value,
|
||||||
|
);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if(events.data?.valueType?.type === 'object'){
|
if (events.data?.valueType?.type === 'object') {
|
||||||
(events.data.valueType?.properties || []).map((i: any) => {
|
(events.data.valueType?.properties || []).map((i: any) => {
|
||||||
columns.value.splice(0, 0, {
|
columns.value.splice(0, 0, {
|
||||||
key: i.id,
|
key: i.id,
|
||||||
title: i.name,
|
title: i.name,
|
||||||
dataIndex: `${i.id}_format`
|
dataIndex: `${i.id}_format`,
|
||||||
})
|
search: {
|
||||||
})
|
type: 'string',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
columns.value.splice(0, 0, {
|
columns.value.splice(0, 0, {
|
||||||
title: '数据',
|
title: '数据',
|
||||||
dataIndex: 'value',
|
dataIndex: 'value',
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const detail = () => {
|
const detail = () => {
|
||||||
Modal.info({
|
Modal.info({
|
||||||
title: () => '详情',
|
title: () => '详情',
|
||||||
width: 850,
|
width: 850,
|
||||||
content: () => h('div', {}, [
|
content: () => h('div', {}, [h('p', '暂未开发')]),
|
||||||
h('p', '暂未开发'),
|
okText: '关闭',
|
||||||
]),
|
});
|
||||||
okText: '关闭'
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
|
@ -1,3 +1,54 @@
|
||||||
|
<!-- 坐标点拾取组件 -->
|
||||||
<template>
|
<template>
|
||||||
<div>AMap</div>
|
<div style="width: 100%; height: 400px">
|
||||||
|
<div style="position: relative">
|
||||||
|
<div style="position: absolute; right: 0; top: 5px; z-index: 999">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary" @click="start">开始动画</a-button>
|
||||||
|
<a-button type="primary" @click="stop">停止动画</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-amap :center="center" :zooms="[3, 20]" @init="initMap" ref="map"></el-amap>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { initAMapApiLoader } from '@vuemap/vue-amap';
|
||||||
|
import AMapUI from '@vuemap/vue-amap'
|
||||||
|
import '@vuemap/vue-amap/dist/style.css';
|
||||||
|
|
||||||
|
initAMapApiLoader({
|
||||||
|
key: 'a0415acfc35af15f10221bfa5a6850b4',
|
||||||
|
securityJsCode: 'cae6108ec3dd222f946d1a7237c78be0',
|
||||||
|
});
|
||||||
|
|
||||||
|
interface EmitProps {
|
||||||
|
(e: 'update:points', data: string): void;
|
||||||
|
}
|
||||||
|
const props = defineProps({
|
||||||
|
points: { type: Array, default: () => [] },
|
||||||
|
});
|
||||||
|
const emit = defineEmits<EmitProps>();
|
||||||
|
|
||||||
|
// 地图拾取的坐标点(经纬度字符串)
|
||||||
|
const mapPoint = ref('');
|
||||||
|
|
||||||
|
const map = ref(null);
|
||||||
|
|
||||||
|
const center = ref([106.55, 29.56]);
|
||||||
|
const marker = ref(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地图初始化
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
const initMap = (e: any) => {
|
||||||
|
console.log(e)
|
||||||
|
// map = e;
|
||||||
|
// const pointStr = mapPoint.value as string;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="chart" ref="chart"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const { proxy } = <any>getCurrentInstance();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
// 图表数据
|
||||||
|
options:{
|
||||||
|
type:Object,
|
||||||
|
default:()=>{}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制图表
|
||||||
|
*/
|
||||||
|
const createChart = () => {
|
||||||
|
nextTick(() => {
|
||||||
|
const myChart = echarts.init(proxy.$refs.chart);
|
||||||
|
myChart.setOption(props.options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.options,
|
||||||
|
() => createChart(),
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,3 +1,218 @@
|
||||||
<template>
|
<template>
|
||||||
<div>Charts</div>
|
<a-spin :spinning="loading">
|
||||||
|
<div>
|
||||||
|
<a-space>
|
||||||
|
<div>
|
||||||
|
统计周期:
|
||||||
|
<a-select v-model:value="cycle" style="width: 120px">
|
||||||
|
<a-select-option value="*" v-if="_type"
|
||||||
|
>实际值</a-select-option
|
||||||
|
>
|
||||||
|
<a-select-option value="1m">按分钟统计</a-select-option>
|
||||||
|
<a-select-option value="1h">按小时统计</a-select-option>
|
||||||
|
<a-select-option value="1d">按天统计</a-select-option>
|
||||||
|
<a-select-option value="1w">按周统计</a-select-option>
|
||||||
|
<a-select-option value="1M">按月统计</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
<div v-if="cycle !== '*' && _type">
|
||||||
|
统计规则:
|
||||||
|
<a-select v-model:value="agg" style="width: 120px">
|
||||||
|
<a-select-option value="AVG">平均值</a-select-option>
|
||||||
|
<a-select-option value="MAX">最大值</a-select-option>
|
||||||
|
<a-select-option value="MIN">最小值</a-select-option>
|
||||||
|
<a-select-option value="COUNT">总数</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
<div style="width: 100%; height: 500px">
|
||||||
|
<Chart :options="options" v-if="chartsList.length" />
|
||||||
|
<JEmpty v-else />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getPropertiesInfo, getPropertiesList } from '@/api/device/instance';
|
||||||
|
import { useInstanceStore } from '@/store/instance';
|
||||||
|
import Chart from './Chart.vue';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const list = ['int', 'float', 'double', 'long'];
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const cycle = ref<string>('*');
|
||||||
|
const agg = ref<string>('AVG');
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const chartsList = ref<any[]>([]);
|
||||||
|
const instanceStore = useInstanceStore();
|
||||||
|
const options = ref({});
|
||||||
|
|
||||||
|
const _type = computed(() => {
|
||||||
|
const flag = list.includes(prop.data?.valueType?.type || '')
|
||||||
|
cycle.value = flag ? '*' : '1m'
|
||||||
|
return flag
|
||||||
|
});
|
||||||
|
|
||||||
|
const queryChartsAggList = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await getPropertiesInfo(instanceStore.current.id, {
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
property: prop.data.id,
|
||||||
|
alias: prop.data.id,
|
||||||
|
agg: agg.value,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
query: {
|
||||||
|
interval: cycle.value,
|
||||||
|
format: 'yyyy-MM-dd HH:mm:ss',
|
||||||
|
from: prop.time[0],
|
||||||
|
to: prop.time[1],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
loading.value = false;
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const dataList: any[] = [
|
||||||
|
{
|
||||||
|
year: prop.time[1],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
(resp.result as any[]).forEach((i: any) => {
|
||||||
|
dataList.push({
|
||||||
|
...i,
|
||||||
|
year: i.time,
|
||||||
|
value: Number(i[prop.data.id || '']),
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dataList.push({
|
||||||
|
year: prop.time[0],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
chartsList.value = (dataList || []).reverse();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryChartsList = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await getPropertiesList(
|
||||||
|
instanceStore.current.id,
|
||||||
|
prop.data.id,
|
||||||
|
{
|
||||||
|
paging: false,
|
||||||
|
terms: [
|
||||||
|
{
|
||||||
|
column: 'timestamp$BTW',
|
||||||
|
value:
|
||||||
|
prop.time[0] && prop.time[1]
|
||||||
|
? [prop.time[0], prop.time[1]]
|
||||||
|
: [],
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sorts: [{ name: 'timestamp', order: 'asc' }],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
loading.value = false;
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const dataList: any[] = [
|
||||||
|
{
|
||||||
|
year: prop.time[0],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
(resp.result as any)?.data?.forEach((i: any) => {
|
||||||
|
dataList.push({
|
||||||
|
...i,
|
||||||
|
year: i.timestamp,
|
||||||
|
value: i.value,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
dataList.push({
|
||||||
|
year: prop.time[1],
|
||||||
|
value: undefined,
|
||||||
|
type: prop.data?.name || '',
|
||||||
|
});
|
||||||
|
chartsList.value = dataList || [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOptions = (arr: any[]) => {
|
||||||
|
options.value = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: arr.map((item) => {
|
||||||
|
return echarts.format.formatTime(
|
||||||
|
'yyyy-MM-dd\nhh:mm:ss',
|
||||||
|
item.year,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
name: '时间',
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
name: arr[0]?.type,
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
position: function (pt: any) {
|
||||||
|
return [pt[0], '10%'];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: arr.map((i: any) => i.value),
|
||||||
|
type: 'line',
|
||||||
|
areaStyle: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [cycle, agg],
|
||||||
|
([newCycle, newAgg]) => {
|
||||||
|
if (newCycle.value === '*' && _type.value) {
|
||||||
|
queryChartsList();
|
||||||
|
} else {
|
||||||
|
queryChartsAggList();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (chartsList.value.length) {
|
||||||
|
getOptions(chartsList.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div style="position: relative">
|
||||||
|
<div style="position: absolute; right: 0; top: 5px; z-index: 999">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="primary">开始动画</a-button>
|
||||||
|
<a-button type="primary">停止动画</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AMap :points="geoList" />
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { getPropertyData } from '@/api/device/instance';
|
||||||
|
import { useInstanceStore } from '@/store/instance';
|
||||||
|
import encodeQuery from '@/utils/encodeQuery';
|
||||||
|
import AMap from './AMap.vue';
|
||||||
|
|
||||||
|
const instanceStore = useInstanceStore();
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
time: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const geoList = ref<any[]>([]);
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
|
||||||
|
const query = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await getPropertyData(
|
||||||
|
instanceStore.current.id,
|
||||||
|
encodeQuery({
|
||||||
|
paging: false,
|
||||||
|
terms: {
|
||||||
|
property: prop.data.id,
|
||||||
|
timestamp$BTW: prop.time[0] && prop.time[1] ? prop.time : [],
|
||||||
|
},
|
||||||
|
sorts: { timestamp: 'asc' },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
loading.value = false;
|
||||||
|
if (resp.status === 200) {
|
||||||
|
const list: any[] = [];
|
||||||
|
((resp.result as any)?.data || []).forEach((item: any) => {
|
||||||
|
list.push([item.value.lon, item.value.lat]);
|
||||||
|
});
|
||||||
|
geoList.value = list
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [prop.data.id, prop.time],
|
||||||
|
([newVal]) => {
|
||||||
|
if (newVal) {
|
||||||
|
query();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true, immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -18,6 +18,13 @@
|
||||||
<template v-if="column.key === 'timestamp'">
|
<template v-if="column.key === 'timestamp'">
|
||||||
{{ moment(record.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
{{ moment(record.timestamp).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="column.key === 'value'">
|
||||||
|
<ValueRender
|
||||||
|
type="table"
|
||||||
|
:data="_props.data"
|
||||||
|
:value="{ formatValue: record.value }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template v-else-if="column.key === 'action'">
|
<template v-else-if="column.key === 'action'">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button
|
<a-button
|
||||||
|
@ -30,7 +37,7 @@
|
||||||
@click="_download(record)"
|
@click="_download(record)"
|
||||||
><AIcon type="DownloadOutlined"
|
><AIcon type="DownloadOutlined"
|
||||||
/></a-button>
|
/></a-button>
|
||||||
<a-button type="link"
|
<a-button type="link" @click="showDetail(record)"
|
||||||
><AIcon type="SearchOutlined"
|
><AIcon type="SearchOutlined"
|
||||||
/></a-button>
|
/></a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
|
@ -38,6 +45,28 @@
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
</div>
|
</div>
|
||||||
|
<a-modal
|
||||||
|
title="详情"
|
||||||
|
:visible="visible"
|
||||||
|
@ok="visible = false"
|
||||||
|
@cancel="visible = false"
|
||||||
|
>
|
||||||
|
<div>自定义属性</div>
|
||||||
|
<JsonViewer
|
||||||
|
v-if="
|
||||||
|
data?.valueType?.type === 'object' ||
|
||||||
|
data?.valueType?.type === 'array'
|
||||||
|
"
|
||||||
|
:expand-depth="5"
|
||||||
|
:value="current.formatValue"
|
||||||
|
/>
|
||||||
|
<a-textarea
|
||||||
|
v-else-if="data?.valueType?.type === 'file'"
|
||||||
|
:value="current.formatValue"
|
||||||
|
:row="3"
|
||||||
|
/>
|
||||||
|
<a-input v-else disabled :value="current.formatValue" />
|
||||||
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -46,6 +75,8 @@ import { useInstanceStore } from '@/store/instance';
|
||||||
import encodeQuery from '@/utils/encodeQuery';
|
import encodeQuery from '@/utils/encodeQuery';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { getType } from '../index';
|
import { getType } from '../index';
|
||||||
|
import ValueRender from '../ValueRender.vue';
|
||||||
|
import JsonViewer from 'vue-json-viewer';
|
||||||
|
|
||||||
const _props = defineProps({
|
const _props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -57,8 +88,11 @@ const _props = defineProps({
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const instanceStore = useInstanceStore();
|
const instanceStore = useInstanceStore();
|
||||||
const dataSource = ref({});
|
const dataSource = ref({});
|
||||||
|
const current = ref<any>({});
|
||||||
|
const visible = ref<boolean>(false);
|
||||||
|
|
||||||
const columns = computed(() => {
|
const columns = computed(() => {
|
||||||
const arr: any[] = [
|
const arr: any[] = [
|
||||||
|
@ -92,6 +126,11 @@ const showLoad = computed(() => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const showDetail = (item: any) => {
|
||||||
|
visible.value = true;
|
||||||
|
current.value = item;
|
||||||
|
};
|
||||||
|
|
||||||
const queryPropertyData = async (params: any) => {
|
const queryPropertyData = async (params: any) => {
|
||||||
const resp = await getPropertyData(
|
const resp = await getPropertyData(
|
||||||
instanceStore.current.id,
|
instanceStore.current.id,
|
||||||
|
@ -99,7 +138,7 @@ const queryPropertyData = async (params: any) => {
|
||||||
...params,
|
...params,
|
||||||
terms: {
|
terms: {
|
||||||
property: _props.data.id,
|
property: _props.data.id,
|
||||||
timestamp$BTW: _props.time?.length ? _props.time : [],
|
timestamp$BTW: _props.time,
|
||||||
},
|
},
|
||||||
sorts: { timestamp: 'desc' },
|
sorts: { timestamp: 'desc' },
|
||||||
}),
|
}),
|
||||||
|
@ -109,14 +148,20 @@ const queryPropertyData = async (params: any) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watchEffect(() => {
|
watch(
|
||||||
if (_props.data.id) {
|
() => [_props.data.id, _props.time],
|
||||||
queryPropertyData({
|
([newVal]) => {
|
||||||
pageSize: _props.data.valueType?.type === 'file' ? 5 : 10,
|
if (newVal) {
|
||||||
pageIndex: 0,
|
queryPropertyData({
|
||||||
});
|
pageSize: _props.data.valueType?.type === 'file' ? 5 : 10,
|
||||||
|
pageIndex: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true, immediate: true
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const onChange = (page: any) => {
|
const onChange = (page: any) => {
|
||||||
queryPropertyData({
|
queryPropertyData({
|
||||||
|
@ -141,3 +186,9 @@ const _download = (record: any) => {
|
||||||
document.body.removeChild(downNode);
|
document.body.removeChild(downNode);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
:deep(.ant-pagination-item) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,15 +2,15 @@
|
||||||
<a-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
<a-modal title="详情" visible width="50vw" @ok="onCancel" @cancel="onCancel">
|
||||||
<div style="margin-bottom: 10px"><TimeComponent v-model="dateValue" /></div>
|
<div style="margin-bottom: 10px"><TimeComponent v-model="dateValue" /></div>
|
||||||
<div>
|
<div>
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<a-tabs v-model:activeKey="activeKey" style="max-height: 600px; overflow-y: auto">
|
||||||
<a-tab-pane key="table" tab="列表">
|
<a-tab-pane key="table" tab="列表">
|
||||||
<Table :data="props.data" :time="_getTimes" />
|
<Table :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="charts" tab="图表">
|
<a-tab-pane key="charts" tab="图表">
|
||||||
<Charts />
|
<Charts :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="geo" tab="轨迹">
|
<a-tab-pane key="geo" tab="轨迹" v-if="data?.valueType?.type === 'geoPoint'">
|
||||||
<AMap />
|
<PropertyAMap :data="props.data" :time="_getTimes" />
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
import TimeComponent from './TimeComponent.vue'
|
import TimeComponent from './TimeComponent.vue'
|
||||||
import Charts from './Charts.vue'
|
import Charts from './Charts.vue'
|
||||||
import AMap from './AMap.vue'
|
import PropertyAMap from './PropertyAMap.vue'
|
||||||
import Table from './Table.vue'
|
import Table from './Table.vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<a-card :hoverable="true" class="card-box">
|
<a-card :hoverable="true" class="card-box">
|
||||||
<!-- <a-spin :spinning="loading"> -->
|
<!-- <a-spin :spinning="loading"> -->
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">{{ _props.data.name }}</div>
|
<div class="title">{{ _props.data.name }}</div>
|
||||||
<div class="extra">
|
<div class="extra">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
|
<template v-for="i in actions" :key="i.key">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
v-for="i in actions"
|
|
||||||
:key="i.key"
|
|
||||||
v-bind="i.tooltip"
|
v-bind="i.tooltip"
|
||||||
|
v-if="i.key !== 'edit'"
|
||||||
>
|
>
|
||||||
<a-button
|
<a-button
|
||||||
style="padding: 0; margin: 0"
|
style="padding: 0; margin: 0"
|
||||||
|
@ -17,26 +17,48 @@
|
||||||
:disabled="i.disabled"
|
:disabled="i.disabled"
|
||||||
@click="i.onClick && i.onClick(data)"
|
@click="i.onClick && i.onClick(data)"
|
||||||
>
|
>
|
||||||
<AIcon :type="i.icon" style="color: #323130; font-size: 12px" />
|
<AIcon
|
||||||
|
:type="i.icon"
|
||||||
|
style="color: #323130; font-size: 12px"
|
||||||
|
/>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-space>
|
<PermissionButton
|
||||||
</div>
|
:disabled="i.disabled"
|
||||||
</div>
|
v-else
|
||||||
<div class="value">
|
:popConfirm="i.popConfirm"
|
||||||
<ValueRender :data="data" :value="_props.data" type="card" />
|
:tooltip="i.tooltip"
|
||||||
</div>
|
@click="i.onClick && i.onClick(slotProps)"
|
||||||
<div class="bottom">
|
type="link"
|
||||||
<div style="color: rgba(0, 0, 0, .65); font-size: 12px">更新时间</div>
|
style="padding: 0px"
|
||||||
<div class="time-value">{{_props?.data?.timeString || '--'}}</div>
|
:hasPermission="'device/Instance:update'"
|
||||||
|
>
|
||||||
|
<template #icon
|
||||||
|
><AIcon :type="i.icon" style="color: #323130; font-size: 12px"
|
||||||
|
/></template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<ValueRender :data="data" :value="_props.data" type="card" />
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div style="color: rgba(0, 0, 0, 0.65); font-size: 12px">
|
||||||
|
更新时间
|
||||||
|
</div>
|
||||||
|
<div class="time-value">
|
||||||
|
{{ _props?.data?.timeString || '--' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- </a-spin> -->
|
<!-- </a-spin> -->
|
||||||
</a-card>
|
</a-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ValueRender from './ValueRender.vue'
|
import ValueRender from './ValueRender.vue';
|
||||||
const _props = defineProps({
|
const _props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -44,7 +66,7 @@ const _props = defineProps({
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// const loading = ref<boolean>(true);
|
// const loading = ref<boolean>(true);
|
||||||
|
|
|
@ -13,23 +13,18 @@
|
||||||
<a-image :src="value?.formatValue" />
|
<a-image :src="value?.formatValue" />
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
|
<template v-else-if="['.flv', '.m3u8', '.mp4'].includes(type)">
|
||||||
<!-- TODO 视频组件缺失 -->
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<!-- <json-viewer
|
<JsonViewer
|
||||||
:value="{
|
:expand-depth="5"
|
||||||
'id': '123'
|
:value="value?.formatValue"
|
||||||
}"
|
/>
|
||||||
copyable
|
|
||||||
boxed
|
|
||||||
sort
|
|
||||||
></json-viewer> -->
|
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// import JsonViewer from 'vue3-json-viewer';
|
import JsonViewer from 'vue-json-viewer';
|
||||||
|
|
||||||
const _data = defineProps({
|
const _data = defineProps({
|
||||||
type: {
|
type: {
|
||||||
|
@ -46,9 +41,6 @@ const handleCancel = () => {
|
||||||
_emit('close');
|
_emit('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
// watchEffect(() => {
|
|
||||||
// console.log(_data.value?.formatValue)
|
|
||||||
// })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<div v-if="value?.formatValue !== 0 && !value?.formatValue" :class="valueClass">--</div>
|
<div v-if="value?.formatValue !== 0 && !value?.formatValue" :class="valueClass">--</div>
|
||||||
<div v-else-if="data?.valueType?.type === 'file'">
|
<div v-else-if="_data.data?.valueType?.type === 'file'">
|
||||||
<template v-if="data?.valueType?.fileType === 'base64'">
|
<template v-if="data?.valueType?.fileType === 'base64'">
|
||||||
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
<div :class="valueClass" v-if="!!getType(value?.formatValue)">
|
||||||
<img :src="imgMap.get(_type)" @error="onError" />
|
<img :src="imgMap.get(_type)" @error="onError" />
|
||||||
|
@ -36,10 +36,10 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="data?.valueType?.type === 'object'" @click="getDetail('obj')" :class="valueClass">
|
<div v-else-if="_data.data?.valueType?.type === 'object'" @click="getDetail('obj')" :class="valueClass">
|
||||||
<img :src="imgMap.get('obj')" />
|
<img :src="imgMap.get('obj')" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="data?.valueType?.type === 'geoPoint' || data?.valueType?.type === 'array'" :class="valueClass">
|
<div v-else-if="_data.data?.valueType?.type === 'geoPoint' || _data.data?.valueType?.type === 'array'" :class="valueClass">
|
||||||
{{JSON.stringify(value?.formatValue)}}
|
{{JSON.stringify(value?.formatValue)}}
|
||||||
</div>
|
</div>
|
||||||
<div v-else :class="valueClass">
|
<div v-else :class="valueClass">
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
import { getImage } from "@/utils/comm";
|
import { getImage } from "@/utils/comm";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import ValueDetail from './ValueDetail.vue'
|
import ValueDetail from './ValueDetail.vue'
|
||||||
import {getType, imgMap} from './index'
|
import {getType, imgMap, imgList, videoList, fileList} from './index'
|
||||||
|
|
||||||
const _data = defineProps({
|
const _data = defineProps({
|
||||||
data: {
|
data: {
|
||||||
|
@ -115,7 +115,6 @@ const getDetail = (_type: string) => {
|
||||||
_types.value = flag
|
_types.value = flag
|
||||||
visible.value = true
|
visible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -32,20 +32,30 @@
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip
|
<template v-for="i in getActions(slotProps)" :key="i.key">
|
||||||
v-for="i in getActions(slotProps)"
|
<a-tooltip v-bind="i.tooltip" v-if="i.key !== 'edit'">
|
||||||
:key="i.key"
|
<a-button
|
||||||
v-bind="i.tooltip"
|
style="padding: 0"
|
||||||
>
|
type="link"
|
||||||
<a-button
|
:disabled="i.disabled"
|
||||||
style="padding: 0"
|
@click="i.onClick && i.onClick(slotProps)"
|
||||||
type="link"
|
>
|
||||||
|
<AIcon :type="i.icon" />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<PermissionButton
|
||||||
:disabled="i.disabled"
|
:disabled="i.disabled"
|
||||||
|
v-else
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="i.tooltip"
|
||||||
@click="i.onClick && i.onClick(slotProps)"
|
@click="i.onClick && i.onClick(slotProps)"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0px"
|
||||||
|
:hasPermission="'device/Instance:update'"
|
||||||
>
|
>
|
||||||
<AIcon :type="i.icon" />
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
</template>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #paginationRender>
|
<template #paginationRender>
|
||||||
|
@ -76,7 +86,11 @@
|
||||||
@close="indicatorVisible = false"
|
@close="indicatorVisible = false"
|
||||||
:data="currentInfo"
|
:data="currentInfo"
|
||||||
/>
|
/>
|
||||||
<Detail v-if="detailVisible" :data="currentInfo" @close="detailVisible = false" />
|
<Detail
|
||||||
|
v-if="detailVisible"
|
||||||
|
:data="currentInfo"
|
||||||
|
@close="detailVisible = false"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -240,11 +254,15 @@ const subscribeProperty = () => {
|
||||||
?.pipe(map((res: any) => res.payload))
|
?.pipe(map((res: any) => res.payload))
|
||||||
.subscribe((payload) => {
|
.subscribe((payload) => {
|
||||||
list.value = [...list.value, payload];
|
list.value = [...list.value, payload];
|
||||||
unref(list).sort((a: any, b: any) => a.timestamp - b.timestamp)
|
unref(list)
|
||||||
.forEach((item: any) => {
|
.sort((a: any, b: any) => a.timestamp - b.timestamp)
|
||||||
const { value } = item;
|
.forEach((item: any) => {
|
||||||
propertyValue.value[value?.property] = { ...item, ...value };
|
const { value } = item;
|
||||||
});
|
propertyValue.value[value?.property] = {
|
||||||
|
...item,
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
});
|
||||||
// list.value = [...list.value, payload];
|
// list.value = [...list.value, payload];
|
||||||
// throttle(valueChange(list.value), 500);
|
// throttle(valueChange(list.value), 500);
|
||||||
});
|
});
|
||||||
|
@ -337,8 +355,8 @@ const onSearch = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
subRef.value && subRef.value?.unsubscribe()
|
subRef.value && subRef.value?.unsubscribe();
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<page-container
|
<page-container
|
||||||
:tabList="list"
|
:tabList="list"
|
||||||
@back="onBack"
|
@back="onBack"
|
||||||
:tabActiveKey="instanceStore.active"
|
:tabActiveKey="instanceStore.tabActiveKey"
|
||||||
@tabChange="onTabChange"
|
@tabChange="onTabChange"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
|
|
|
@ -155,13 +155,12 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<h3
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
class="card-item-content-title"
|
<span style="font-size: 16px; font-weight: 600" @click.stop="handleView(slotProps.id)">
|
||||||
@click.stop="handleView(slotProps.id)"
|
{{ slotProps.name }}
|
||||||
>
|
</span>
|
||||||
{{ slotProps.name }}
|
</Ellipsis>
|
||||||
</h3>
|
<a-row style="margin-top: 20px">
|
||||||
<a-row>
|
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<div class="card-item-content-text">
|
<div class="card-item-content-text">
|
||||||
设备类型
|
设备类型
|
||||||
|
@ -172,7 +171,9 @@
|
||||||
<div class="card-item-content-text">
|
<div class="card-item-content-text">
|
||||||
产品名称
|
产品名称
|
||||||
</div>
|
</div>
|
||||||
<div>{{ slotProps.productName }}</div>
|
<Ellipsis style="width: 100%">
|
||||||
|
{{ slotProps.productName }}
|
||||||
|
</Ellipsis>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in New Issue