feat: 添加设备日志查询功能
- 新增设备日志查询 API - 实现事件、功能、上下线日志查询 - 优化属性日志查询接口 - 修复事件和功能模拟相关问题
This commit is contained in:
parent
8d0b69cd4c
commit
2925fce7fb
|
@ -34,6 +34,46 @@ export function deviceInfo(id: ID) {
|
|||
return requestClient.get<DeviceVO>(`/device/device/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备日志数据(属性/事件)
|
||||
* @param params
|
||||
* @returns 设备日志数据
|
||||
*/
|
||||
export function deviceLogList(params?: DeviceQuery) {
|
||||
return requestClient.post('/device/device/data/field', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备功能操作日志列表
|
||||
* @param params
|
||||
* @returns 设备功能操作日志
|
||||
*/
|
||||
export function functionLogList(params?: DeviceQuery) {
|
||||
return requestClient.get('/device/functionLog/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备上下线日志列表
|
||||
* @param params
|
||||
* @returns 设备上下线日志
|
||||
*/
|
||||
export function onlineLogList(params?: DeviceQuery) {
|
||||
return requestClient.get('/device/onlineLog/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询设备日志数据(上报/下发)
|
||||
* @param params
|
||||
* @returns 设备日志数据
|
||||
*/
|
||||
export function deviceUpDownLogList(params?: DeviceQuery) {
|
||||
return requestClient.post('/device/device/log', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增设备
|
||||
* @param data
|
||||
|
|
|
@ -4,14 +4,15 @@ import { onMounted, ref } from 'vue';
|
|||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
Empty,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
} from 'ant-design-vue';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
import { deviceUpDownLogList } from '#/api/device/device';
|
||||
|
||||
interface Props {
|
||||
deviceId: string;
|
||||
|
@ -32,15 +33,15 @@ const pagination = ref({ current: 1, pageSize: 10, total: 0 });
|
|||
// 日志类型选项
|
||||
const logTypeOptions = [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '上报', value: 'upload' },
|
||||
{ label: '下发', value: 'download' },
|
||||
{ label: '上报', value: 'up' },
|
||||
{ label: '下发', value: 'down' },
|
||||
];
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 200 },
|
||||
{ title: '类型', dataIndex: 'logType', key: 'logType', width: 120 },
|
||||
{ title: '名称内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 200 },
|
||||
{ title: '操作', key: 'action', width: 100 },
|
||||
];
|
||||
|
||||
|
@ -49,7 +50,13 @@ const viewVisible = ref(false);
|
|||
const viewRecord = ref<any>(null);
|
||||
|
||||
const openView = (record: any) => {
|
||||
viewRecord.value = record;
|
||||
let content = '';
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(record.content), null, 2);
|
||||
} catch {
|
||||
content = record.content;
|
||||
}
|
||||
viewRecord.value = content;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
|
@ -62,53 +69,52 @@ const closeView = () => {
|
|||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const [start, end] = timeRange.value || [];
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (timeRange.value?.length === 2) {
|
||||
startTime = timeRange.value[0].valueOf();
|
||||
endTime = timeRange.value[1].valueOf();
|
||||
}
|
||||
const params = {
|
||||
deviceId: props.deviceId,
|
||||
startTime: start?.format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: end?.format('YYYY-MM-DD HH:mm:ss'),
|
||||
logType: logType.value || undefined,
|
||||
pageNo: pagination.value.current,
|
||||
pageSize: pagination.value.pageSize,
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
// direction: logType.value,
|
||||
orderType: 2,
|
||||
current: pagination.value.current,
|
||||
size: pagination.value.pageSize,
|
||||
listWhere: [],
|
||||
};
|
||||
if (startTime && endTime) {
|
||||
params.startTime = startTime;
|
||||
params.endTime = endTime;
|
||||
}
|
||||
if (logType.value) {
|
||||
params.listWhere.push({
|
||||
field: 'direction',
|
||||
val: logType.value,
|
||||
operator: 'eq',
|
||||
valType: 'string',
|
||||
});
|
||||
}
|
||||
// if (messageId.value) {
|
||||
// params.listWhere.push({
|
||||
// field: 'msg_id',
|
||||
// val: messageId.value?.trim(),
|
||||
// operator: 'eq',
|
||||
// valType: 'string',
|
||||
// });
|
||||
// }
|
||||
console.log('query logs with', params);
|
||||
|
||||
// TODO: 替换为真实接口
|
||||
const mock = [
|
||||
{
|
||||
id: 1,
|
||||
timestamp: '2025-01-20 10:30:15',
|
||||
logType: '上报',
|
||||
content: '设备启动成功,固件版本:v1.0.0',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
timestamp: '2025-01-20 10:30:20',
|
||||
logType: '下发',
|
||||
content: '{"switch": "on"}',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
timestamp: '2025-01-20 10:30:25',
|
||||
logType: '上报',
|
||||
content: '网络连接不稳定,重试次数:3',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
timestamp: '2025-01-20 10:30:30',
|
||||
logType: '下发',
|
||||
content:
|
||||
'功能执行失败,错误码:E001,错误信息:参数无效功能执行失败,错误码:E001,错误信息:参数无效功能执行失败,错误码:E001,错误信息:参数无效功能执行失败,错误码:E001,错误信息:参数无效',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
timestamp: '2025-01-20 10:30:35',
|
||||
logType: '上报',
|
||||
content: '调试信息:设备响应时间 150ms',
|
||||
},
|
||||
];
|
||||
dataSource.value = mock;
|
||||
pagination.value.total = mock.length;
|
||||
const data = await deviceUpDownLogList(params);
|
||||
dataSource.value = data?.records.map((item: any) => {
|
||||
return {
|
||||
messageId: item.msg_id,
|
||||
logType: item.direction,
|
||||
timestamp: dayjs(item.time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
content: item.log_data,
|
||||
};
|
||||
});
|
||||
pagination.value.total = data.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
@ -141,7 +147,7 @@ onMounted(() => {
|
|||
<div class="log-management">
|
||||
<!-- 查询区 -->
|
||||
<div class="query-bar">
|
||||
<Space>
|
||||
<Space wrap>
|
||||
<span>日志类型:</span>
|
||||
<Select
|
||||
v-model:value="logType"
|
||||
|
@ -173,8 +179,11 @@ onMounted(() => {
|
|||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'logType'">
|
||||
<Tag :color="record.logType === '上报' ? 'blue' : 'green'">
|
||||
{{ record.logType }}
|
||||
<Tag :color="record.logType === 'up' ? 'blue' : 'green'">
|
||||
{{
|
||||
logTypeOptions.find((item) => item.value === record.logType)
|
||||
?.label
|
||||
}}
|
||||
</Tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
|
@ -185,10 +194,6 @@ onMounted(() => {
|
|||
</template>
|
||||
</Table>
|
||||
|
||||
<div v-if="!loading && dataSource.length === 0" class="empty-wrap">
|
||||
<Empty />
|
||||
</div>
|
||||
|
||||
<!-- 查看内容弹窗 -->
|
||||
<Modal
|
||||
v-model:open="viewVisible"
|
||||
|
@ -197,22 +202,7 @@ onMounted(() => {
|
|||
@cancel="closeView"
|
||||
:footer="null"
|
||||
>
|
||||
<div class="log-detail">
|
||||
<div class="detail-item">
|
||||
<span class="label">类型:</span>
|
||||
<Tag :color="viewRecord?.logType === '上报' ? 'blue' : 'green'">
|
||||
{{ viewRecord?.logType }}
|
||||
</Tag>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">时间:</span>
|
||||
<span>{{ viewRecord?.timestamp }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="label">内容:</span>
|
||||
<pre class="content-view">{{ viewRecord?.content }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<pre class="json-view">{{ viewRecord }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -253,4 +243,15 @@ onMounted(() => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,6 +4,9 @@ import { computed } from 'vue';
|
|||
import { TabPane, Tabs } from 'ant-design-vue';
|
||||
|
||||
import EventsPanel from './running/EventsPanel.vue';
|
||||
import FunctionPanel from './running/FunctionPanel.vue';
|
||||
import OnlineStatusPanel from './running/OnlineStatusPanel.vue';
|
||||
import PropertyPanel from './running/PropertyPanel.vue';
|
||||
import RealtimePanel from './running/RealtimePanel.vue';
|
||||
|
||||
interface Props {
|
||||
|
@ -17,23 +20,35 @@ const props = defineProps<Props>();
|
|||
const metadata = computed(() => {
|
||||
try {
|
||||
const raw = props.deviceInfo?.productObj?.metadata;
|
||||
if (!raw) return { properties: [], propertyGroups: [], events: [] } as any;
|
||||
if (!raw)
|
||||
return {
|
||||
properties: [],
|
||||
propertyGroups: [],
|
||||
events: [],
|
||||
functions: [],
|
||||
} as any;
|
||||
const obj = JSON.parse(raw || '{}');
|
||||
return {
|
||||
properties: obj?.properties || [],
|
||||
propertyGroups: obj?.propertyGroups || [],
|
||||
events: obj?.events || [],
|
||||
functions: obj?.functions || [],
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn('parse metadata error', error);
|
||||
return { properties: [], propertyGroups: [], events: [] } as any;
|
||||
return {
|
||||
properties: [],
|
||||
propertyGroups: [],
|
||||
events: [],
|
||||
functions: [],
|
||||
} as any;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="running-status">
|
||||
<Tabs type="card">
|
||||
<Tabs type="card" :destroy-inactive-tab-pane="true">
|
||||
<TabPane key="realtime" tab="实时数据">
|
||||
<RealtimePanel
|
||||
:device-id="props.deviceId"
|
||||
|
@ -41,8 +56,33 @@ const metadata = computed(() => {
|
|||
:device-info="deviceInfo"
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane key="property" tab="属性">
|
||||
<PropertyPanel
|
||||
:device-id="props.deviceId"
|
||||
:metadata="metadata"
|
||||
:device-info="deviceInfo"
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane key="events" tab="事件">
|
||||
<EventsPanel :device-id="props.deviceId" :metadata="metadata" />
|
||||
<EventsPanel
|
||||
:device-id="props.deviceId"
|
||||
:metadata="metadata"
|
||||
:device-info="deviceInfo"
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane key="functions" tab="功能">
|
||||
<FunctionPanel
|
||||
:device-id="props.deviceId"
|
||||
:metadata="metadata"
|
||||
:device-info="deviceInfo"
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane key="online" tab="上下线">
|
||||
<OnlineStatusPanel
|
||||
:device-id="props.deviceId"
|
||||
:metadata="metadata"
|
||||
:device-info="deviceInfo"
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,6 @@ import { computed, onMounted, ref } from 'vue';
|
|||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
Empty,
|
||||
Input,
|
||||
Modal,
|
||||
Select,
|
||||
|
@ -13,9 +12,12 @@ import {
|
|||
} from 'ant-design-vue';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
import { deviceLogList } from '#/api/device/device';
|
||||
|
||||
interface Props {
|
||||
deviceId: string;
|
||||
metadata: any;
|
||||
deviceInfo: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
@ -41,18 +43,21 @@ const eventNameMap = computed<Record<string, string>>(() => {
|
|||
|
||||
// 下拉选项(来自物模型)
|
||||
const eventOptions = computed(() => {
|
||||
return (props.metadata?.events || []).map((e: any) => ({
|
||||
label: e.name || e.id,
|
||||
value: e.id,
|
||||
}));
|
||||
return [
|
||||
{ label: '全部', value: '' },
|
||||
...(props.metadata?.events || []).map((e: any) => ({
|
||||
label: e.name || e.id,
|
||||
value: e.id,
|
||||
})),
|
||||
];
|
||||
});
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{ title: '消息ID', dataIndex: 'messageId', key: 'messageId', width: 220 },
|
||||
{ title: '名称', dataIndex: 'eventName', key: 'eventName', width: 160 },
|
||||
{ title: '标识', dataIndex: 'eventId', key: 'eventId', width: 160 },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 200 },
|
||||
{ title: '消息ID', dataIndex: 'messageId', key: 'messageId', width: 140 },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 160 },
|
||||
{ title: '事件', dataIndex: 'eventName', key: 'eventName', width: 200 },
|
||||
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 100 },
|
||||
];
|
||||
|
||||
|
@ -61,7 +66,13 @@ const viewVisible = ref(false);
|
|||
const viewRecord = ref<any>(null);
|
||||
|
||||
const openView = (record: any) => {
|
||||
viewRecord.value = record;
|
||||
let content = '';
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(record.content), null, 2);
|
||||
} catch {
|
||||
content = record.content;
|
||||
}
|
||||
viewRecord.value = content;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
|
@ -74,30 +85,53 @@ const closeView = () => {
|
|||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const [start, end] = timeRange.value || [];
|
||||
const params = {
|
||||
deviceId: props.deviceId,
|
||||
startTime: start?.format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: end?.format('YYYY-MM-DD HH:mm:ss'),
|
||||
messageId: messageId.value?.trim() || undefined,
|
||||
eventId: selectedEventId.value || undefined,
|
||||
pageNo: pagination.value.current,
|
||||
pageSize: pagination.value.pageSize,
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (timeRange.value?.length === 2) {
|
||||
startTime = timeRange.value[0].valueOf();
|
||||
endTime = timeRange.value[1].valueOf();
|
||||
}
|
||||
const params: any = {
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
fields: selectedEventId.value || undefined,
|
||||
orderType: 2,
|
||||
current: pagination.value.current,
|
||||
size: pagination.value.pageSize,
|
||||
metaDataType: 'event',
|
||||
listWhere: [],
|
||||
};
|
||||
console.log('query events with', params);
|
||||
|
||||
// TODO: 替换为真实接口
|
||||
const eid =
|
||||
selectedEventId.value || props.metadata?.events?.[0]?.id || 'temple';
|
||||
const mock = Array.from({ length: 5 }).map((_, i) => ({
|
||||
messageId: `${Date.now()}_${i}`,
|
||||
eventId: eid,
|
||||
eventName: eventNameMap.value[eid] || '事件',
|
||||
payload: { level: 'info', msg: '模拟事件' },
|
||||
timestamp: dayjs().format('YYYY-MM-DD HH:mm:ss.SSS'),
|
||||
}));
|
||||
dataSource.value = mock;
|
||||
pagination.value.total = mock.length;
|
||||
if (startTime && endTime) {
|
||||
params.startTime = startTime;
|
||||
params.endTime = endTime;
|
||||
}
|
||||
if (messageId.value) {
|
||||
params.listWhere.push({
|
||||
field: 'msg_id',
|
||||
val: messageId.value?.trim(),
|
||||
operator: 'eq',
|
||||
valType: 'string',
|
||||
});
|
||||
}
|
||||
if (selectedEventId.value) {
|
||||
params.listWhere.push({
|
||||
field: 'eventId',
|
||||
val: selectedEventId.value?.trim(),
|
||||
operator: 'eq',
|
||||
valType: 'string',
|
||||
});
|
||||
}
|
||||
const data = await deviceLogList(params);
|
||||
console.log('eventNameMap', eventNameMap);
|
||||
dataSource.value = data.records.map((item: any) => {
|
||||
return {
|
||||
messageId: item.msg_id,
|
||||
eventName: eventNameMap.value[item.eventId],
|
||||
timestamp: dayjs(item.time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
content: item.raw_data,
|
||||
};
|
||||
});
|
||||
pagination.value.total = data.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
@ -131,7 +165,7 @@ onMounted(() => {
|
|||
<div class="events-panel">
|
||||
<!-- 查询区 -->
|
||||
<div class="query-bar">
|
||||
<Space>
|
||||
<Space wrap>
|
||||
<span>消息ID:</span>
|
||||
<Input
|
||||
v-model:value="messageId"
|
||||
|
@ -146,6 +180,7 @@ onMounted(() => {
|
|||
allow-clear
|
||||
placeholder="请选择事件"
|
||||
style="width: 220px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<span>时间范围:</span>
|
||||
<DatePicker.RangePicker
|
||||
|
@ -153,6 +188,7 @@ onMounted(() => {
|
|||
:show-time="true"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Button @click="handleReset">重置</Button>
|
||||
<Button type="primary" @click="handleSearch">查询</Button>
|
||||
|
@ -177,10 +213,6 @@ onMounted(() => {
|
|||
</template>
|
||||
</Table>
|
||||
|
||||
<div v-if="!loading && dataSource.length === 0" class="empty-wrap">
|
||||
<Empty />
|
||||
</div>
|
||||
|
||||
<!-- 查看内容弹窗 -->
|
||||
<Modal
|
||||
v-model:open="viewVisible"
|
||||
|
@ -189,9 +221,7 @@ onMounted(() => {
|
|||
@cancel="closeView"
|
||||
:footer="null"
|
||||
>
|
||||
<pre class="json-view">{{
|
||||
JSON.stringify(viewRecord?.payload ?? {}, null, 2)
|
||||
}}</pre>
|
||||
<pre class="json-view">{{ viewRecord }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -206,14 +236,14 @@ onMounted(() => {
|
|||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
Input,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Table,
|
||||
} from 'ant-design-vue';
|
||||
import { Dayjs } from 'dayjs';
|
||||
|
||||
import { functionLogList } from '#/api/device/device';
|
||||
|
||||
interface Props {
|
||||
deviceId: string;
|
||||
metadata: any;
|
||||
deviceInfo: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// 查询条件
|
||||
const messageId = ref<string>('');
|
||||
const timeRange = ref<[Dayjs, Dayjs] | undefined>();
|
||||
const selectedFuncId = ref<string | undefined>();
|
||||
|
||||
// 列表数据
|
||||
const loading = ref(false);
|
||||
const dataSource = ref<any[]>([]);
|
||||
const pagination = ref({ current: 1, pageSize: 10, total: 0 });
|
||||
|
||||
// 功能映射(id -> name)
|
||||
const funcNameMap = computed<Record<string, string>>(() => {
|
||||
const map: Record<string, string> = {};
|
||||
(props.metadata?.functions || []).forEach((f: any) => {
|
||||
map[f.id] = f.name || f.id;
|
||||
});
|
||||
return {
|
||||
defaultWrite: '设备写入',
|
||||
defaultRead: '设备读取',
|
||||
...map,
|
||||
};
|
||||
});
|
||||
|
||||
// 下拉选项(来自物模型)
|
||||
const funcOptions = computed(() => {
|
||||
return [
|
||||
{ label: '全部', value: '' },
|
||||
{ label: '设备写入', value: 'defaultWrite' },
|
||||
{ label: '设备读取', value: 'defaultRead' },
|
||||
...(props.metadata?.functions || []).map((f: any) => ({
|
||||
label: f.name || f.id,
|
||||
value: f.id,
|
||||
})),
|
||||
];
|
||||
});
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{ title: '消息ID', dataIndex: 'messageId', key: 'messageId', width: 180 },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 160 },
|
||||
{ title: '功能', dataIndex: 'funcName', key: 'funcName', width: 200 },
|
||||
{ title: '操作人', dataIndex: 'operator', key: 'operator', width: 120 },
|
||||
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 100 },
|
||||
];
|
||||
|
||||
// 详情弹窗
|
||||
const viewVisible = ref(false);
|
||||
const viewRecord = ref<any>(null);
|
||||
|
||||
const openView = (record: any) => {
|
||||
let content = '';
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(record.content), null, 2);
|
||||
} catch {
|
||||
content = record.content;
|
||||
}
|
||||
viewRecord.value = content;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
const closeView = () => {
|
||||
viewVisible.value = false;
|
||||
viewRecord.value = null;
|
||||
};
|
||||
|
||||
// 加载功能日志列表
|
||||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (timeRange.value?.length === 2) {
|
||||
startTime = timeRange.value[0].format('YYYY-MM-DD HH:mm:ss');
|
||||
endTime = timeRange.value[1].format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
const params: any = {
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
msgId: messageId.value?.trim() || undefined,
|
||||
funcId: selectedFuncId.value || undefined,
|
||||
isAsc: 'desc',
|
||||
pageNum: pagination.value.current,
|
||||
pageSize: pagination.value.pageSize,
|
||||
listWhere: [],
|
||||
params: {
|
||||
beginTime: undefined,
|
||||
endTime: undefined,
|
||||
},
|
||||
};
|
||||
if (startTime && endTime) {
|
||||
params.params.beginTime = startTime;
|
||||
params.params.endTime = endTime;
|
||||
}
|
||||
const data = await functionLogList(params);
|
||||
dataSource.value = (data.rows || []).map((item: any) => {
|
||||
return {
|
||||
messageId: item.msgId,
|
||||
funcName: funcNameMap.value[item.funcId] || item.funcId,
|
||||
timestamp: item.serverTime,
|
||||
operator: item.operatorName || '-',
|
||||
content: item.funcParams || '-',
|
||||
};
|
||||
});
|
||||
pagination.value.total = data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
messageId.value = '';
|
||||
timeRange.value = undefined;
|
||||
selectedFuncId.value = undefined;
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleTableChange = (page: any) => {
|
||||
pagination.value.current = page.current;
|
||||
pagination.value.pageSize = page.pageSize;
|
||||
loadList();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="function-panel">
|
||||
<!-- 查询区 -->
|
||||
<div class="query-bar">
|
||||
<Space wrap>
|
||||
<span>消息ID:</span>
|
||||
<Input
|
||||
v-model:value="messageId"
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
allow-clear
|
||||
/>
|
||||
<span>功能:</span>
|
||||
<Select
|
||||
v-model:value="selectedFuncId"
|
||||
:options="funcOptions"
|
||||
allow-clear
|
||||
placeholder="请选择功能"
|
||||
style="width: 220px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<span>时间范围:</span>
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="timeRange"
|
||||
:show-time="true"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Button @click="handleReset">重置</Button>
|
||||
<Button type="primary" @click="handleSearch">查询</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
row-key="messageId"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="openView(record)">
|
||||
查看
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<!-- 查看内容弹窗 -->
|
||||
<Modal
|
||||
v-model:open="viewVisible"
|
||||
title="功能日志内容"
|
||||
width="720px"
|
||||
@cancel="closeView"
|
||||
:footer="null"
|
||||
>
|
||||
<pre class="json-view">{{ viewRecord }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.function-panel {
|
||||
.query-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,204 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
Button,
|
||||
DatePicker,
|
||||
Input,
|
||||
Modal,
|
||||
Space,
|
||||
Table,
|
||||
Tag,
|
||||
} from 'ant-design-vue';
|
||||
import { Dayjs } from 'dayjs';
|
||||
|
||||
import { onlineLogList } from '#/api/device/device';
|
||||
|
||||
interface Props {
|
||||
deviceId: string;
|
||||
metadata: any;
|
||||
deviceInfo: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// 查询条件
|
||||
const messageId = ref<string>('');
|
||||
const timeRange = ref<[Dayjs, Dayjs] | undefined>();
|
||||
|
||||
// 列表数据
|
||||
const loading = ref(false);
|
||||
const dataSource = ref<any[]>([]);
|
||||
const pagination = ref({ current: 1, pageSize: 10, total: 0 });
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{ title: '消息ID', dataIndex: 'messageId', key: 'messageId', width: 180 },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 160 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 120 },
|
||||
];
|
||||
|
||||
// 详情弹窗
|
||||
const viewVisible = ref(false);
|
||||
const viewRecord = ref<any>(null);
|
||||
|
||||
const openView = (record: any) => {
|
||||
let content = '';
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(record.content), null, 2);
|
||||
} catch {
|
||||
content = record.content;
|
||||
}
|
||||
viewRecord.value = content;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
const closeView = () => {
|
||||
viewVisible.value = false;
|
||||
viewRecord.value = null;
|
||||
};
|
||||
|
||||
// 加载上下线日志列表
|
||||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (timeRange.value?.length === 2) {
|
||||
startTime = timeRange.value[0].format('YYYY-MM-DD HH:mm:ss');
|
||||
endTime = timeRange.value[1].format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
const params: any = {
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
msgId: messageId.value?.trim() || undefined,
|
||||
isAsc: 'desc',
|
||||
pageNum: pagination.value.current,
|
||||
pageSize: pagination.value.pageSize,
|
||||
params: {
|
||||
beginTime: undefined,
|
||||
endTime: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
if (startTime && endTime) {
|
||||
params.params.beginTime = startTime;
|
||||
params.params.endTime = endTime;
|
||||
}
|
||||
|
||||
const data = await onlineLogList(params);
|
||||
dataSource.value = (data.rows || []).map((item: any) => {
|
||||
return {
|
||||
messageId: item.msgId,
|
||||
timestamp: item.createTime,
|
||||
type: item.deviceStatus,
|
||||
content: item.raw_data ?? '-',
|
||||
};
|
||||
});
|
||||
pagination.value.total = data.total || 0;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
messageId.value = '';
|
||||
timeRange.value = undefined;
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleTableChange = (page: any) => {
|
||||
pagination.value.current = page.current;
|
||||
pagination.value.pageSize = page.pageSize;
|
||||
loadList();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="online-status-panel">
|
||||
<!-- 查询区 -->
|
||||
<div class="query-bar">
|
||||
<Space wrap>
|
||||
<span>消息ID:</span>
|
||||
<Input
|
||||
v-model:value="messageId"
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
allow-clear
|
||||
/>
|
||||
<span>时间范围:</span>
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="timeRange"
|
||||
:show-time="true"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Button @click="handleReset">重置</Button>
|
||||
<Button type="primary" @click="handleSearch">查询</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
row-key="messageId"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'type'">
|
||||
<Tag color="processing" v-if="record.type === 'online'">上线</Tag>
|
||||
<Tag color="error" v-else-if="record.type === 'offline'">下线</Tag>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="openView(record)">
|
||||
查看
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<!-- 查看内容弹窗 -->
|
||||
<Modal
|
||||
v-model:open="viewVisible"
|
||||
title="上下线日志内容"
|
||||
width="720px"
|
||||
@cancel="closeView"
|
||||
:footer="null"
|
||||
>
|
||||
<pre class="json-view">{{ viewRecord }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.online-status-panel {
|
||||
.query-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,202 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { Button, DatePicker, Input, Modal, Space, Table } from 'ant-design-vue';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
|
||||
import { deviceLogList } from '#/api/device/device';
|
||||
|
||||
interface Props {
|
||||
deviceId: string;
|
||||
metadata: any;
|
||||
deviceInfo: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
// 查询条件
|
||||
const messageId = ref<string>('');
|
||||
const timeRange = ref<[Dayjs, Dayjs] | undefined>();
|
||||
// 属性分组选择
|
||||
const selectedGroup = ref('all');
|
||||
|
||||
// 列表数据
|
||||
const loading = ref(false);
|
||||
const dataSource = ref<any[]>([]);
|
||||
const pagination = ref({ current: 1, pageSize: 10, total: 0 });
|
||||
|
||||
// 表格列
|
||||
const columns = [
|
||||
{ title: '消息ID', dataIndex: 'messageId', key: 'messageId', width: 140 },
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 160 },
|
||||
{ title: '内容', dataIndex: 'content', key: 'content', ellipsis: true },
|
||||
{ title: '操作', key: 'action', width: 100 },
|
||||
];
|
||||
|
||||
// 详情弹窗
|
||||
const viewVisible = ref(false);
|
||||
const viewRecord = ref<any>(null);
|
||||
|
||||
const openView = (record: any) => {
|
||||
let content = '';
|
||||
try {
|
||||
content = JSON.stringify(JSON.parse(record.content), null, 2);
|
||||
} catch {
|
||||
content = record.content;
|
||||
}
|
||||
viewRecord.value = content;
|
||||
viewVisible.value = true;
|
||||
};
|
||||
|
||||
const closeView = () => {
|
||||
viewVisible.value = false;
|
||||
viewRecord.value = null;
|
||||
};
|
||||
|
||||
// 加载属性列表(占位,后续接入API)
|
||||
const loadList = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
let startTime = null;
|
||||
let endTime = null;
|
||||
if (timeRange.value?.length === 2) {
|
||||
startTime = timeRange.value[0].valueOf();
|
||||
endTime = timeRange.value[1].valueOf();
|
||||
}
|
||||
|
||||
const params: any = {
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
orderType: 2,
|
||||
current: pagination.value.current,
|
||||
size: pagination.value.pageSize,
|
||||
metaDataType: 'property',
|
||||
listWhere: [],
|
||||
};
|
||||
if (startTime && endTime) {
|
||||
params.startTime = startTime;
|
||||
params.endTime = endTime;
|
||||
}
|
||||
if (messageId.value) {
|
||||
params.listWhere.push({
|
||||
field: 'msg_id',
|
||||
val: messageId.value?.trim(),
|
||||
operator: 'eq',
|
||||
valType: 'string',
|
||||
});
|
||||
}
|
||||
const data = await deviceLogList(params);
|
||||
dataSource.value = data.records.map((item: any) => {
|
||||
return {
|
||||
messageId: item.msg_id,
|
||||
timestamp: dayjs(item.time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
content: item.raw_data,
|
||||
};
|
||||
});
|
||||
pagination.value.total = data.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
messageId.value = '';
|
||||
timeRange.value = undefined;
|
||||
selectedGroup.value = 'all';
|
||||
pagination.value.current = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
const handleTableChange = (page: any) => {
|
||||
pagination.value.current = page.current;
|
||||
pagination.value.pageSize = page.pageSize;
|
||||
loadList();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="events-panel">
|
||||
<!-- 查询区 -->
|
||||
<div class="query-bar">
|
||||
<Space wrap>
|
||||
<span>消息ID:</span>
|
||||
<Input
|
||||
v-model:value="messageId"
|
||||
placeholder="请输入"
|
||||
style="width: 220px"
|
||||
allow-clear
|
||||
/>
|
||||
<span>时间范围:</span>
|
||||
<DatePicker.RangePicker
|
||||
v-model:value="timeRange"
|
||||
:show-time="true"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 360px"
|
||||
@change="handleSearch"
|
||||
/>
|
||||
<Button @click="handleReset">重置</Button>
|
||||
<Button type="primary" @click="handleSearch">查询</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<!-- 列表 -->
|
||||
<Table
|
||||
:columns="columns"
|
||||
:data-source="dataSource"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
row-key="messageId"
|
||||
@change="handleTableChange"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'action'">
|
||||
<Button type="link" size="small" @click="openView(record)">
|
||||
查看
|
||||
</Button>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
|
||||
<!-- 查看内容弹窗 -->
|
||||
<Modal
|
||||
v-model:open="viewVisible"
|
||||
title="属性内容"
|
||||
width="720px"
|
||||
@cancel="closeView"
|
||||
:footer="null"
|
||||
>
|
||||
<pre class="json-view">{{ viewRecord }}</pre>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.events-panel {
|
||||
.query-bar {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.empty-wrap {
|
||||
padding: 40px 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.json-view {
|
||||
max-height: 420px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
|
@ -9,11 +9,11 @@ import {
|
|||
Card,
|
||||
Checkbox,
|
||||
Col,
|
||||
DatePicker,
|
||||
Empty,
|
||||
Modal,
|
||||
RadioButton,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Row,
|
||||
Select,
|
||||
Space,
|
||||
|
@ -22,6 +22,7 @@ import {
|
|||
} from 'ant-design-vue';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { deviceLogList } from '#/api/device/device';
|
||||
import { getWebSocket } from '#/utils/websocket';
|
||||
|
||||
import RealtimeChart from './RealtimeChart.vue';
|
||||
|
@ -129,10 +130,33 @@ const logModalVisible = ref(false);
|
|||
const currentProperty = ref<any>(null);
|
||||
const logTabActiveKey = ref('list');
|
||||
const activeQuickTime = ref('today');
|
||||
const dates = ref<any>();
|
||||
const logDateRange = ref([dayjs().startOf('day'), dayjs()]);
|
||||
const logLoading = ref(false);
|
||||
const logData = ref<any[]>([]);
|
||||
|
||||
const disabledDate = (current: Dayjs) => {
|
||||
const now = dayjs();
|
||||
// 不能选择未来时间
|
||||
if (current.isAfter(now, 'second')) return true;
|
||||
if (!dates.value || (dates.value as any).length === 0) {
|
||||
return false;
|
||||
}
|
||||
const tooLate = dates.value[0] && current.diff(dates.value[0], 'days') > 60;
|
||||
const tooEarly = dates.value[1] && dates.value[1].diff(current, 'days') > 60;
|
||||
return tooEarly || tooLate;
|
||||
};
|
||||
|
||||
const onOpenChange = (open) => {
|
||||
if (open) {
|
||||
dates.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const onCalendarChange = (val: any) => {
|
||||
dates.value = val;
|
||||
};
|
||||
|
||||
const logColumns = [
|
||||
{ title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 200 },
|
||||
{
|
||||
|
@ -147,46 +171,52 @@ const logColumns = [
|
|||
|
||||
const openPropertyLog = (property: any) => {
|
||||
currentProperty.value = property;
|
||||
logTabActiveKey.value = 'list';
|
||||
activeQuickTime.value = 'today';
|
||||
logModalVisible.value = true;
|
||||
loadPropertyLog();
|
||||
const startTime = dayjs().startOf('day').valueOf();
|
||||
const endTime = dayjs().valueOf();
|
||||
loadPropertyLog(property.id, startTime, endTime);
|
||||
};
|
||||
|
||||
const loadPropertyLog = async () => {
|
||||
const loadPropertyLog = async (field, startTime, endTime) => {
|
||||
if (!currentProperty.value) return;
|
||||
try {
|
||||
logLoading.value = true;
|
||||
// TODO: 调用属性日志接口
|
||||
logData.value = [
|
||||
{ timestamp: '2025-08-19 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-19 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-19 16:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-20 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-20 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-20 16:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-21 14:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-21 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-21 18:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-22 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-22 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-22 16:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-23 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-23 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-23 16:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-24 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-24 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-24 16:17:40', value: '0.3' },
|
||||
{ timestamp: '2025-08-25 16:19:40', value: '0.5' },
|
||||
{ timestamp: '2025-08-25 16:18:40', value: '0.4' },
|
||||
{ timestamp: '2025-08-25 16:17:40', value: '0.3' },
|
||||
];
|
||||
const data = await deviceLogList({
|
||||
deviceKey: props.deviceInfo.deviceKey,
|
||||
productKey: props.deviceInfo.productObj.productKey,
|
||||
fields: field,
|
||||
startTime,
|
||||
endTime,
|
||||
orderType: 2,
|
||||
metaDataType: 'property',
|
||||
});
|
||||
logData.value = data.records.map((item: any) => {
|
||||
const itemValue = formatValue({
|
||||
...currentProperty.value,
|
||||
value: item[field],
|
||||
});
|
||||
return {
|
||||
timestamp: dayjs(item.time).format('YYYY-MM-DD HH:mm:ss'),
|
||||
value: itemValue,
|
||||
};
|
||||
});
|
||||
} finally {
|
||||
logLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dates: any) => {
|
||||
console.log('选择日期范围', dates);
|
||||
logDateRange.value = dates;
|
||||
if (dates && dates.length === 2) loadPropertyLog();
|
||||
const [start, end] = dates;
|
||||
if (dates && dates.length === 2) {
|
||||
const startTime = start.valueOf();
|
||||
const endTime = end.valueOf();
|
||||
loadPropertyLog(currentProperty.value.id, startTime, endTime);
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuickTimeSelect = (e: any) => {
|
||||
|
@ -204,7 +234,9 @@ const handleQuickTimeSelect = (e: any) => {
|
|||
logDateRange.value = [now.startOf('day'), now];
|
||||
}
|
||||
}
|
||||
loadPropertyLog();
|
||||
const startTime = logDateRange.value[0].valueOf();
|
||||
const endTime = logDateRange.value[1].valueOf();
|
||||
loadPropertyLog(currentProperty.value.id, startTime, endTime);
|
||||
};
|
||||
|
||||
const formatValue = (property: any) => {
|
||||
|
@ -361,11 +393,14 @@ watch(
|
|||
<RadioButton value="week">近一周</RadioButton>
|
||||
<RadioButton value="month">近一月</RadioButton>
|
||||
</RadioGroup>
|
||||
<DatePicker.RangePicker
|
||||
<RangePicker
|
||||
v-model:value="logDateRange"
|
||||
:show-time="true"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
:disabled-date="disabledDate"
|
||||
@change="handleDateRangeChange"
|
||||
@open-change="onOpenChange"
|
||||
@calendar-change="onCalendarChange"
|
||||
/>
|
||||
</Space>
|
||||
</div>
|
||||
|
|
|
@ -132,18 +132,22 @@ const initializeSelectedRows = () => {
|
|||
const initializeFormData = () => {
|
||||
const defaultData = {};
|
||||
currentOutputs.value.forEach((output) => {
|
||||
const { formType } = output.valueParams;
|
||||
const { formType, scale } = output.valueParams;
|
||||
|
||||
// 为不同类型的组件设置默认值
|
||||
switch (formType) {
|
||||
case 'number': {
|
||||
// 数字输入框默认为最小值或0
|
||||
defaultData[output.id] = output.valueParams.min || 0;
|
||||
defaultData[output.id] = scale
|
||||
? Number.parseFloat((output.valueParams.min || 0).toFixed(scale))
|
||||
: output.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'progress': {
|
||||
// 滚动条默认为最小值
|
||||
defaultData[output.id] = output.valueParams.min || 0;
|
||||
defaultData[output.id] = scale
|
||||
? Number.parseFloat((output.valueParams.min || 0).toFixed(scale))
|
||||
: output.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'select': {
|
||||
|
@ -184,17 +188,20 @@ watch(selectedEventId, (newEventId) => {
|
|||
currentOutputs.value = selectedEvt.outputs || [];
|
||||
// 初始化选中行
|
||||
initializeSelectedRows();
|
||||
resetForm();
|
||||
// 初始化表单默认值
|
||||
initializeFormData();
|
||||
}
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
resetForm();
|
||||
generateDefaultContent();
|
||||
});
|
||||
|
||||
// 监听模式切换,重置表单
|
||||
watch(currentMode, () => {
|
||||
resetForm();
|
||||
initializeFormData();
|
||||
generateDefaultContent();
|
||||
});
|
||||
|
||||
|
@ -285,7 +292,7 @@ const executeSubmit = async () => {
|
|||
(isSwitch && value === false)
|
||||
) {
|
||||
if (output) {
|
||||
const { dataType, formType } = output.valueParams;
|
||||
const { dataType, formType, format } = output.valueParams;
|
||||
let processedValue = value;
|
||||
|
||||
// 根据dataType和formType处理数据类型转换
|
||||
|
@ -295,16 +302,18 @@ const executeSubmit = async () => {
|
|||
processedValue = Boolean(value);
|
||||
} else if (dataType === 'string') {
|
||||
// string类型的开关,提交字符串值
|
||||
processedValue = value
|
||||
? output.valueParams.trueValue || 'true'
|
||||
: output.valueParams.falseValue || 'false';
|
||||
processedValue = value;
|
||||
}
|
||||
} else if (formType === 'time' && dataType === 'date') {
|
||||
// date类型的时间选择器,提交时间戳
|
||||
if (value && typeof value === 'object' && value.valueOf) {
|
||||
processedValue = value.valueOf(); // 转换为时间戳
|
||||
} else if (value && typeof value === 'string') {
|
||||
processedValue = new Date(value).getTime(); // 字符串转时间戳
|
||||
let timeValue = value;
|
||||
if (format === 'YYYY-MM-DD') {
|
||||
timeValue += ' 00:00:00';
|
||||
}
|
||||
processedValue = new Date(timeValue).getTime(); // 字符串转时间戳
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,9 +509,15 @@ onMounted(() => {
|
|||
record.valueParams.dataType === 'string'
|
||||
"
|
||||
v-model:checked="formData[record.id]"
|
||||
:checked-value="
|
||||
record?.valueParams?.trueValue || 'true'
|
||||
"
|
||||
:checked-children="
|
||||
record?.valueParams?.trueText || '是'
|
||||
"
|
||||
:un-checked-value="
|
||||
record?.valueParams?.falseValue || 'false'
|
||||
"
|
||||
:un-checked-children="
|
||||
record?.valueParams?.falseText || '否'
|
||||
"
|
||||
|
@ -517,6 +532,7 @@ onMounted(() => {
|
|||
record?.valueParams?.format?.includes('HH:mm:ss')
|
||||
"
|
||||
:format="record?.valueParams?.format"
|
||||
:value-format="record?.valueParams?.format"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<TimePicker
|
||||
|
@ -526,6 +542,7 @@ onMounted(() => {
|
|||
"
|
||||
v-model:value="formData[record.id]"
|
||||
:format="record?.valueParams?.format"
|
||||
:value-format="record?.valueParams?.format"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
|
@ -146,30 +146,34 @@ const initializeSelectedRows = () => {
|
|||
const initializeFormData = () => {
|
||||
const defaultData = {};
|
||||
currentInputs.value.forEach((input) => {
|
||||
const { formType } = input.valueParams;
|
||||
const { formType, scale } = input.valueParams;
|
||||
|
||||
// 为不同类型的组件设置默认值
|
||||
switch (formType) {
|
||||
// case 'number': {
|
||||
// // 数字输入框默认为最小值或0
|
||||
// defaultData[input.id] = input.valueParams.min || 0;
|
||||
// break;
|
||||
// }
|
||||
case 'progress': {
|
||||
// 滚动条默认为最小值
|
||||
defaultData[input.id] = input.valueParams.min || 0;
|
||||
case 'number': {
|
||||
// 数字输入框默认为最小值或0
|
||||
defaultData[input.id] = scale
|
||||
? Number.parseFloat((input.valueParams.min || 0).toFixed(scale))
|
||||
: input.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'progress': {
|
||||
// 滚动条默认为最小值
|
||||
defaultData[input.id] = scale
|
||||
? Number.parseFloat((input.valueParams.min || 0).toFixed(scale))
|
||||
: input.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'select': {
|
||||
// 下拉框默认为第一个选项
|
||||
if (
|
||||
input.valueParams.enumConf &&
|
||||
input.valueParams.enumConf.length > 0
|
||||
) {
|
||||
defaultData[input.id] = input.valueParams.enumConf[0].value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// case 'select': {
|
||||
// // 下拉框默认为第一个选项
|
||||
// if (
|
||||
// input.valueParams.enumConf &&
|
||||
// input.valueParams.enumConf.length > 0
|
||||
// ) {
|
||||
// defaultData[input.id] = input.valueParams.enumConf[0].value;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
case 'switch': {
|
||||
// 开关默认值根据数据类型设置
|
||||
defaultData[input.id] =
|
||||
|
@ -236,10 +240,9 @@ watch(selectedFunctionId, (newFunctionId) => {
|
|||
currentInputs.value = selectedFunc.inputs || [];
|
||||
// 初始化选中行
|
||||
initializeSelectedRows();
|
||||
resetForm();
|
||||
// 初始化表单默认值
|
||||
initializeFormData();
|
||||
} else {
|
||||
resetForm();
|
||||
}
|
||||
} else {
|
||||
resetForm();
|
||||
|
@ -251,6 +254,7 @@ watch(selectedFunctionId, (newFunctionId) => {
|
|||
// 监听模式切换,重置表单
|
||||
watch(currentMode, () => {
|
||||
resetForm();
|
||||
initializeFormData();
|
||||
generateDefaultContent();
|
||||
});
|
||||
|
||||
|
@ -370,7 +374,7 @@ const executeSubmit = async (checkValue?: string) => {
|
|||
(isSwitch && value === false)
|
||||
) {
|
||||
if (input) {
|
||||
const { dataType, formType } = input.valueParams;
|
||||
const { dataType, formType, format } = input.valueParams;
|
||||
let processedValue = value;
|
||||
|
||||
// 根据dataType和formType处理数据类型转换
|
||||
|
@ -380,9 +384,7 @@ const executeSubmit = async (checkValue?: string) => {
|
|||
processedValue = Boolean(value);
|
||||
} else if (dataType === 'string') {
|
||||
// string类型的开关,提交字符串值
|
||||
processedValue = value
|
||||
? input.valueParams.trueValue || 'true'
|
||||
: input.valueParams.falseValue || 'false';
|
||||
processedValue = value;
|
||||
}
|
||||
} else if (formType === 'time' && dataType === 'date') {
|
||||
// date类型的时间选择器,提交时间戳
|
||||
|
@ -392,7 +394,11 @@ const executeSubmit = async (checkValue?: string) => {
|
|||
`时间字段 ${key} (date): ${value} -> ${processedValue}`,
|
||||
);
|
||||
} else if (value && typeof value === 'string') {
|
||||
processedValue = new Date(value).getTime(); // 字符串转时间戳
|
||||
let timeValue = value;
|
||||
if (format === 'YYYY-MM-DD') {
|
||||
timeValue += ' 00:00:00';
|
||||
}
|
||||
processedValue = new Date(timeValue).getTime(); // 字符串转时间戳
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ const generateDefaultContent = () => {
|
|||
if (currentMode.value === 'advanced') {
|
||||
// 高级模式:状态参数key:null
|
||||
const params = {
|
||||
status: null,
|
||||
status: '1',
|
||||
};
|
||||
jsonContent.value = JSON.stringify(params, null, 2);
|
||||
parameterContent.value = '';
|
||||
|
|
|
@ -156,6 +156,22 @@ const columns = [
|
|||
},
|
||||
];
|
||||
|
||||
// 表格列配置
|
||||
const readColumns = [
|
||||
{
|
||||
title: '属性名称',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: '数据类型',
|
||||
dataIndex: 'dataType',
|
||||
key: 'dataType',
|
||||
width: 100,
|
||||
},
|
||||
];
|
||||
|
||||
// 表格数据源
|
||||
const tableDataSource = computed(() => {
|
||||
return filteredProperties.value.map((property) => ({
|
||||
|
@ -225,6 +241,13 @@ const initializeSelectedRows = () => {
|
|||
.map((row) => row.key);
|
||||
};
|
||||
|
||||
const sidebarClick = (item: any) => {
|
||||
if (selectedPropertyFunction.value !== item.value) {
|
||||
selectedPropertyFunction.value = item.value;
|
||||
selectedGroup.value = 'all';
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化表单默认值
|
||||
const initializeFormData = () => {
|
||||
const defaultData = {};
|
||||
|
@ -232,13 +255,32 @@ const initializeFormData = () => {
|
|||
const property = filteredProperties.value.find((p) => p.id === row.id);
|
||||
if (!property) return;
|
||||
|
||||
const { formType } = property.valueParams;
|
||||
const { formType, scale } = property.valueParams;
|
||||
|
||||
// 为不同类型的组件设置默认值
|
||||
switch (formType) {
|
||||
case 'number': {
|
||||
// 数字输入框默认为最小值或0
|
||||
defaultData[row.id] = scale
|
||||
? Number.parseFloat((property.valueParams.min || 0).toFixed(scale))
|
||||
: property.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'progress': {
|
||||
// 滚动条默认为最小值
|
||||
defaultData[row.id] = property.valueParams?.min || 0;
|
||||
defaultData[row.id] = scale
|
||||
? Number.parseFloat((property.valueParams.min || 0).toFixed(scale))
|
||||
: property.valueParams.min || 0;
|
||||
break;
|
||||
}
|
||||
case 'select': {
|
||||
// 下拉框默认为第一个选项
|
||||
if (
|
||||
property.valueParams.enumConf &&
|
||||
property.valueParams.enumConf.length > 0
|
||||
) {
|
||||
defaultData[row.id] = property.valueParams.enumConf[0].value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'switch': {
|
||||
|
@ -262,6 +304,7 @@ const initializeFormData = () => {
|
|||
// 监听属性功能切换,重置表单和内容
|
||||
watch(selectedPropertyFunction, () => {
|
||||
resetForm();
|
||||
initializeFormData();
|
||||
initializeSelectedRows();
|
||||
generateDefaultContent();
|
||||
});
|
||||
|
@ -269,6 +312,7 @@ watch(selectedPropertyFunction, () => {
|
|||
// 监听模式切换,重置表单
|
||||
watch(currentMode, () => {
|
||||
resetForm();
|
||||
initializeFormData();
|
||||
generateDefaultContent();
|
||||
});
|
||||
|
||||
|
@ -298,10 +342,24 @@ const generateDefaultContent = () => {
|
|||
if (currentMode.value === 'advanced') {
|
||||
// 高级模式:所有属性key:null
|
||||
const params = {};
|
||||
filteredProperties.value.forEach((property) => {
|
||||
params[property.id] = null;
|
||||
});
|
||||
jsonContent.value = JSON.stringify(params, null, 2);
|
||||
if (selectedPropertyFunction.value === 'read') {
|
||||
const propertyStr = filteredProperties.value
|
||||
.map((item) => item.id)
|
||||
.join(',');
|
||||
jsonContent.value = JSON.stringify(
|
||||
{
|
||||
propertyStr,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
);
|
||||
} else {
|
||||
filteredProperties.value.forEach((property) => {
|
||||
params[property.id] = null;
|
||||
});
|
||||
jsonContent.value = JSON.stringify(params, null, 2);
|
||||
}
|
||||
|
||||
parameterContent.value = '';
|
||||
} else {
|
||||
// 精简模式:清空参数框
|
||||
|
@ -321,72 +379,83 @@ const executeSubmit = async () => {
|
|||
return;
|
||||
}
|
||||
|
||||
// 精简模式:验证表单并获取值
|
||||
await formRef.value.validate();
|
||||
const formValues = formData.value;
|
||||
console.log('表单值:', formValues);
|
||||
if (selectedPropertyFunction.value === 'read') {
|
||||
const value = selectedRowKeys.value.join(',');
|
||||
parameters = {
|
||||
propertyStr: value,
|
||||
};
|
||||
} else {
|
||||
// 精简模式:验证表单并获取值
|
||||
await formRef.value.validate();
|
||||
const formValues = formData.value;
|
||||
console.log('表单值:', selectedRowKeys.value);
|
||||
|
||||
// 验证选中的属性
|
||||
for (const row of tableDataSource.value) {
|
||||
if (selectedRowKeys.value.includes(row.key)) {
|
||||
const property = filteredProperties.value.find(
|
||||
(p) => p.id === row.id,
|
||||
);
|
||||
if (!property) continue;
|
||||
|
||||
const value = formValues[row.id];
|
||||
const { formType } = property.valueParams;
|
||||
|
||||
// 根据不同的表单类型判断是否为空值
|
||||
let isEmpty = false;
|
||||
|
||||
isEmpty =
|
||||
formType === 'switch'
|
||||
? value === undefined || value === null
|
||||
: value === undefined ||
|
||||
value === null ||
|
||||
value === '' ||
|
||||
(typeof value === 'string' && value.trim() === '');
|
||||
|
||||
if (isEmpty) {
|
||||
message.error(`请填写属性:${row.name}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只提交选中的属性,构造key:value对象
|
||||
parameters = {};
|
||||
selectedRowKeys.value.forEach((key) => {
|
||||
const row = tableDataSource.value.find((r) => r.key === key);
|
||||
if (!row) return;
|
||||
|
||||
// 验证选中的属性
|
||||
for (const row of tableDataSource.value) {
|
||||
if (selectedRowKeys.value.includes(row.key)) {
|
||||
const property = filteredProperties.value.find(
|
||||
(p) => p.id === row.id,
|
||||
);
|
||||
if (!property) continue;
|
||||
if (!property) return;
|
||||
|
||||
const value = formValues[row.id];
|
||||
const { formType } = property.valueParams;
|
||||
const { dataType, formType, format } = property.valueParams;
|
||||
let processedValue = value;
|
||||
|
||||
// 根据不同的表单类型判断是否为空值
|
||||
let isEmpty = false;
|
||||
|
||||
isEmpty =
|
||||
formType === 'switch'
|
||||
? value === undefined || value === null
|
||||
: value === undefined ||
|
||||
value === null ||
|
||||
value === '' ||
|
||||
(typeof value === 'string' && value.trim() === '');
|
||||
|
||||
if (isEmpty) {
|
||||
message.error(`请填写属性:${row.name}`);
|
||||
return;
|
||||
// 根据dataType和formType处理数据类型转换
|
||||
if (formType === 'switch') {
|
||||
if (dataType === 'boolean') {
|
||||
processedValue = Boolean(value);
|
||||
} else if (dataType === 'string') {
|
||||
processedValue = value;
|
||||
}
|
||||
} else if (formType === 'time' && dataType === 'date') {
|
||||
if (value && typeof value === 'object' && value.valueOf) {
|
||||
processedValue = value.valueOf();
|
||||
} else if (value && typeof value === 'string') {
|
||||
let timeValue = value;
|
||||
if (format === 'YYYY-MM-DD') {
|
||||
timeValue += ' 00:00:00';
|
||||
}
|
||||
processedValue = new Date(timeValue).getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameters[row.id] = processedValue;
|
||||
});
|
||||
}
|
||||
|
||||
// 只提交选中的属性,构造key:value对象
|
||||
parameters = {};
|
||||
selectedRowKeys.value.forEach((key) => {
|
||||
const row = tableDataSource.value.find((r) => r.key === key);
|
||||
if (!row) return;
|
||||
|
||||
const property = filteredProperties.value.find((p) => p.id === row.id);
|
||||
if (!property) return;
|
||||
|
||||
const value = formValues[row.id];
|
||||
const { dataType, formType } = property.valueParams;
|
||||
let processedValue = value;
|
||||
|
||||
// 根据dataType和formType处理数据类型转换
|
||||
if (formType === 'switch') {
|
||||
if (dataType === 'boolean') {
|
||||
processedValue = Boolean(value);
|
||||
} else if (dataType === 'string') {
|
||||
processedValue = value
|
||||
? property.valueParams?.trueValue || 'true'
|
||||
: property.valueParams?.falseValue || 'false';
|
||||
}
|
||||
} else if (formType === 'time' && dataType === 'date') {
|
||||
if (value && typeof value === 'object' && value.valueOf) {
|
||||
processedValue = value.valueOf();
|
||||
} else if (value && typeof value === 'string') {
|
||||
processedValue = new Date(value).getTime();
|
||||
}
|
||||
}
|
||||
|
||||
parameters[row.id] = processedValue;
|
||||
});
|
||||
} else {
|
||||
// 高级模式:验证JSON格式
|
||||
try {
|
||||
|
@ -477,7 +546,7 @@ onMounted(() => {
|
|||
:key="func.value"
|
||||
class="sidebar-item"
|
||||
:class="{ active: selectedPropertyFunction === func.value }"
|
||||
@click="selectedPropertyFunction = func.value"
|
||||
@click="sidebarClick(func)"
|
||||
>
|
||||
<div class="function-name">{{ func.label }}</div>
|
||||
</div>
|
||||
|
@ -487,7 +556,7 @@ onMounted(() => {
|
|||
<div class="property-main">
|
||||
<!-- 筛选控制面板 -->
|
||||
<div class="control-panel">
|
||||
<div class="control-item">
|
||||
<div class="control-item" v-if="selectedPropertyFunction !== 'read'">
|
||||
<span class="control-label">属性分组:</span>
|
||||
<Select
|
||||
v-model:value="selectedGroup"
|
||||
|
@ -516,12 +585,18 @@ onMounted(() => {
|
|||
<div class="mode-info">
|
||||
<ExclamationCircleOutlined />
|
||||
精简模式下属性只支持输入框的方式录入
|
||||
<span v-if="selectedPropertyFunction === 'read'">
|
||||
请选择要读取的属性
|
||||
</span>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane key="advanced" tab="高级模式">
|
||||
<div class="mode-info">
|
||||
<ExclamationCircleOutlined />
|
||||
高级模式下支持JSON格式直接编辑
|
||||
<span v-if="selectedPropertyFunction === 'read'">
|
||||
填写读取属性id,使用逗号分开
|
||||
</span>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
@ -530,9 +605,10 @@ onMounted(() => {
|
|||
<div class="content-area">
|
||||
<!-- 左侧属性输入 -->
|
||||
<div class="input-section">
|
||||
<div v-if="currentMode === 'simple'" class="simple-form">
|
||||
<div class="simple-form" v-if="currentMode === 'simple'">
|
||||
<Form ref="formRef" :model="formData" layout="vertical">
|
||||
<Table
|
||||
v-if="selectedPropertyFunction !== 'read'"
|
||||
:columns="columns"
|
||||
:data-source="tableDataSource"
|
||||
:row-selection="rowSelection"
|
||||
|
@ -666,9 +742,52 @@ onMounted(() => {
|
|||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
<Table
|
||||
v-else-if="selectedPropertyFunction === 'read'"
|
||||
:columns="readColumns"
|
||||
:data-source="tableDataSource"
|
||||
:row-selection="rowSelection"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<span>{{ record.name }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataType'">
|
||||
<span>{{ record.dataType }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<!-- <div
|
||||
v-else-if="
|
||||
currentMode === 'simple' && selectedPropertyFunction === 'read'
|
||||
"
|
||||
class="simple-form"
|
||||
>
|
||||
<Form ref="formRef" :model="formData" layout="vertical">
|
||||
<Table
|
||||
:columns="readColumns"
|
||||
:data-source="tableDataSource"
|
||||
:row-selection="rowSelection"
|
||||
:pagination="false"
|
||||
:scroll="{ y: 300 }"
|
||||
size="small"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<span>{{ record.name }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'dataType'">
|
||||
<span>{{ record.dataType }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</Table>
|
||||
</Form>
|
||||
</div> -->
|
||||
<div v-else class="advanced-form">
|
||||
<MonacoEditor
|
||||
:model-value="jsonContent"
|
||||
|
|
|
@ -21,6 +21,7 @@ import { columns, querySchema } from './data';
|
|||
import productCategoryDrawer from './productCategory-drawer.vue';
|
||||
|
||||
const formOptions: VbenFormProps = {
|
||||
collapsed: true,
|
||||
commonConfig: {
|
||||
labelWidth: 80,
|
||||
componentProps: {
|
||||
|
|
Loading…
Reference in New Issue