391 lines
11 KiB
Vue
Raw Normal View History

2025-12-18 22:05:55 +08:00
<script setup lang="ts">
import { nextTick, ref } from 'vue';
2025-12-18 22:05:55 +08:00
import { useVbenModal, z } from '@vben/common-ui';
2025-12-18 22:05:55 +08:00
import { message as Message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
2025-12-18 22:05:55 +08:00
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
postDeviceThingModelManagementCommandCreateAsync,
postDeviceThingModelManagementCommandDeleteAsync,
postDeviceThingModelManagementCommandFindByIdAsync,
postDeviceThingModelManagementCommandPageAsync,
postDeviceThingModelManagementCommandUpdateAsync,
postDeviceThingModelManagementPropertyPageAsync,
} from '#/api-client';
import { TableAction } from '#/components/table-action';
2025-12-18 22:05:55 +08:00
import { $t } from '#/locales';
defineOptions({
name: 'DeviceThingModelCommandModal',
});
const deviceThingModelId = ref<string>('');
const deviceModelName = ref<string>('');
const editRow: Record<string, any> = ref({});
const propertyOptions = ref<Array<{ label: string; value: string }>>([]);
// 指令列表
2025-12-18 22:05:55 +08:00
const [Grid, gridApi] = useVbenVxeGrid({
formOptions: {
schema: [],
showDefaultActions: false,
},
gridOptions: {
columns: [
{
field: 'commandName',
title: '指令名称',
minWidth: 160,
showOverflow: 'tooltip',
},
{
field: 'issueCommand',
title: '下发指令',
minWidth: 200,
showOverflow: 'tooltip',
2025-12-18 22:05:55 +08:00
},
{
field: 'propertyArray',
title: '属性标识符集合',
minWidth: 200,
2025-12-18 22:05:55 +08:00
showOverflow: 'tooltip',
formatter: ({ cellValue }: { cellValue: any }) => {
if (Array.isArray(cellValue)) {
return cellValue.join(', ');
}
if (typeof cellValue === 'string') {
try {
const parsed = JSON.parse(cellValue);
if (Array.isArray(parsed)) {
return parsed.join(', ');
}
} catch {
return cellValue;
}
}
return cellValue || '-';
},
2025-12-18 22:05:55 +08:00
},
{
field: 'action',
title: $t('common.action'),
width: 200,
fixed: 'right',
slots: { default: 'action' },
},
],
height: 400,
pagerConfig: {},
toolbarConfig: {
custom: true,
},
2025-12-18 22:05:55 +08:00
proxyConfig: {
ajax: {
query: async ({ page }) => {
if (!deviceThingModelId.value) {
return { items: [], totalCount: 0 };
}
const { data } = await postDeviceThingModelManagementCommandPageAsync({
body: {
pageIndex: page.currentPage,
pageSize: page.pageSize,
deviceThingModelId: deviceThingModelId.value,
},
});
return data || { items: [], totalCount: 0 };
},
},
},
},
});
// 指令新增/编辑弹窗
const [CommandFormModal, commandFormModalApi] = useVbenModal({
draggable: true,
footer: true,
showCancelButton: true,
showConfirmButton: true,
onConfirm: submitCommand,
onBeforeClose: () => {
return true;
},
onOpenChange: async (isOpen: boolean) => {
if (isOpen) {
// 加载当前设备端物模型的属性列表,用于选择属性标识符
await loadPropertyOptions();
if (editRow.value.id) {
// 编辑模式下,加载指令详情
nextTick(async () => {
try {
const { data } = await postDeviceThingModelManagementCommandFindByIdAsync({
body: { id: editRow.value.id },
});
if (data) {
const values: any = { ...data };
// 处理 propertyArray 字段
if (values.propertyArray) {
if (typeof values.propertyArray === 'string') {
try {
values.propertyArray = JSON.parse(values.propertyArray);
} catch {
values.propertyArray = [values.propertyArray];
}
}
if (!Array.isArray(values.propertyArray)) {
values.propertyArray = [];
}
} else {
values.propertyArray = [];
}
commandFormApi.setValues(values);
}
} catch (error) {
console.error('加载指令详情失败:', error);
}
});
} else {
// 新增模式下,清空表单
commandFormApi.resetForm();
}
}
},
onCancel: () => {
commandFormModalApi.close();
},
});
// 加载属性选项(用于指令的属性标识符选择)
async function loadPropertyOptions() {
if (!deviceThingModelId.value) {
return;
}
try {
const { data } = await postDeviceThingModelManagementPropertyPageAsync({
body: {
pageIndex: 1,
pageSize: 1000,
deviceThingModelId: deviceThingModelId.value,
isPage:false
},
});
const items = data?.items || [];
propertyOptions.value = items.map((item: any) => ({
label: `${item.standardFieldDisplayName || item.standardFieldName} (${item.standardFieldName})`,
2025-12-22 17:28:38 +08:00
value: item.ioTPlatformRawFieldName
}));
} catch (error) {
console.error('加载属性列表失败:', error);
}
}
// 指令表单
const [CommandForm, commandFormApi] = useVbenForm({
collapsed: false,
commonConfig: {
labelWidth: 140,
componentProps: {
class: 'w-4/5',
},
},
layout: 'horizontal',
schema: [
{
component: 'Input',
fieldName: 'commandName',
label: '指令名称',
rules: z
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: {
placeholder: $t('common.pleaseInput') + '指令名称',
},
},
{
component: 'Textarea',
fieldName: 'issueCommand',
label: '下发指令',
rules: z
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))),
componentProps: {
rows: 4,
placeholder: $t('common.pleaseInput') + '下发指令(完整的单个下发指令)',
},
},
{
component: 'Select',
fieldName: 'propertyArray',
label: '属性标识符集合',
rules: z
.preprocess((v) => (v == null ? [] : v), z.array(z.string()).min(1, '请至少选择一个属性标识符')),
// 使用函数形式,保证 options 能随着 propertyOptions 的变化而更新
componentProps: () => ({
mode: 'multiple',
placeholder: '请选择属性标识符',
options: propertyOptions.value,
showSearch: true,
filterOption: (input: string, option: any) => {
return String(option.label)
.toLowerCase()
.includes(input.toLowerCase());
},
}),
},
],
showCollapseButton: false,
showDefaultActions: false,
wrapperClass: 'grid-cols-1',
});
// 打开新增指令弹窗
function openAddCommandModal() {
editRow.value = {};
commandFormModalApi.open();
}
// 打开编辑指令弹窗
function openEditCommandModal(record: any) {
editRow.value = record;
commandFormModalApi.open();
}
// 提交指令(新增/编辑)
async function submitCommand() {
const isEdit = !!editRow.value.id;
const { valid } = await commandFormApi.validate();
if (!valid) return;
const formValues = await commandFormApi.getValues();
const fetchParams: any = {
...formValues,
deviceThingModelId: deviceThingModelId.value,
// 确保 propertyArray 是数组格式
propertyArray: Array.isArray(formValues.propertyArray)
? formValues.propertyArray
: formValues.propertyArray
? [formValues.propertyArray]
: [],
...(isEdit && { id: editRow.value.id }),
};
try {
const api = isEdit
? postDeviceThingModelManagementCommandUpdateAsync
: postDeviceThingModelManagementCommandCreateAsync;
const resp = await api({ body: fetchParams });
if (resp.data) {
Message.success(
isEdit ? $t('common.editSuccess') : $t('common.addSuccess'),
);
commandFormModalApi.close();
editRow.value = {};
await nextTick();
if (gridApi && gridApi.reload) {
await gridApi.reload();
}
} else {
Message.error(
isEdit ? $t('common.editFail') : $t('common.addFail'),
);
}
} catch (error) {
console.error('提交指令失败:', error);
Message.error(
isEdit ? $t('common.editFail') : $t('common.addFail'),
);
}
}
// 删除指令
async function onDeleteCommand(record: any) {
try {
const resp = await postDeviceThingModelManagementCommandDeleteAsync({
body: { id: record.id },
});
if (resp.data) {
Message.success($t('common.deleteSuccess'));
await nextTick();
if (gridApi && gridApi.reload) {
await gridApi.reload();
}
} else {
Message.error($t('common.deleteFail'));
}
} catch (error) {
console.error('删除指令失败:', error);
Message.error($t('common.deleteFail'));
}
}
2025-12-18 22:05:55 +08:00
const [Modal, modalApi] = useVbenModal({
onOpenChange(isOpen: boolean) {
if (isOpen) {
const data = modalApi.getData<Record<string, any>>();
deviceThingModelId.value = data?.deviceThingModelId || '';
deviceModelName.value = data?.deviceModelName || '';
// 使用 nextTick 确保 Grid 组件已完全初始化
nextTick(() => {
if (gridApi && gridApi.reload) {
gridApi.reload();
}
});
2025-12-18 22:05:55 +08:00
}
},
});
</script>
<template>
<Modal :title="`指令管理 - ${deviceModelName || ''}`" class="w-[900px]">
<Grid>
<template #toolbar-actions>
<TableAction
:actions="[
{
label: $t('common.add'),
type: 'primary',
icon: 'ant-design:plus-outlined',
onClick: openAddCommandModal,
auth: ['AbpIdentity.Users.Create'],
},
]"
/>
</template>
<template #action="{ row }">
<TableAction
:actions="[
{
label: $t('common.edit'),
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
onClick: openEditCommandModal.bind(null, row),
},
{
label: $t('common.delete'),
icon: 'ant-design:delete-outlined',
type: 'link',
size: 'small',
auth: ['AbpIdentity.Users.Delete'],
popConfirm: {
title: $t('common.askConfirmDelete'),
confirm: onDeleteCommand.bind(null, row),
},
},
]"
/>
</template>
2025-12-18 22:05:55 +08:00
</Grid>
<!-- 指令新增/编辑弹窗 -->
<CommandFormModal
:title="editRow.id ? $t('common.edit') : $t('common.add')"
class="w-[800px]"
>
<CommandForm />
</CommandFormModal>
2025-12-18 22:05:55 +08:00
</Modal>
</template>