根据物模型ID将解析脚本更新

This commit is contained in:
ChenYi 2025-12-19 16:54:57 +08:00
parent 9d521d74a5
commit cfbe8f4df0
3 changed files with 232 additions and 125 deletions

View File

@ -171,11 +171,13 @@ const handleMenuClick = (e: any) => {
<Icon :icon="action.popConfirm.icon" /> <Icon :icon="action.popConfirm.icon" />
</template> </template>
<div <div
:class=" :class="[
action.disabled === true action.disabled === true
? 'cursor-not-allowed text-gray-300' ? 'cursor-not-allowed text-gray-300'
: '' : '',
" action.class,
]"
:style="action.style"
> >
<Icon v-if="action.icon" :icon="action.icon" /> <Icon v-if="action.icon" :icon="action.icon" />
<span class="ml-1">{{ action.text }}</span> <span class="ml-1">{{ action.text }}</span>
@ -184,11 +186,13 @@ const handleMenuClick = (e: any) => {
</template> </template>
<template v-else> <template v-else>
<div <div
:class=" :class="[
action.disabled === true action.disabled === true
? 'cursor-not-allowed text-gray-300' ? 'cursor-not-allowed text-gray-300'
: '' : '',
" action.class,
]"
:style="action.style"
> >
<Icon v-if="action.icon" :icon="action.icon" /> <Icon v-if="action.icon" :icon="action.icon" />
{{ action.label }} {{ action.label }}

View File

@ -7,21 +7,25 @@ import { useRoute } from 'vue-router';
import { Page, useVbenModal, z } from '@vben/common-ui'; import { Page, useVbenModal, z } from '@vben/common-ui';
import { message as Message, Tag } from 'ant-design-vue'; import { Button, message as Message, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form'; import { useVbenForm } from '#/adapter/form';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { import {
postDeviceThingModelManagementBuildAnalysisScriptAsync,
postDeviceThingModelManagementBuildAnalysisScriptByIdAsync, postDeviceThingModelManagementBuildAnalysisScriptByIdAsync,
postDeviceThingModelManagementCreateAsync, postDeviceThingModelManagementCreateAsync,
postDeviceThingModelManagementDeleteAsync, postDeviceThingModelManagementDeleteAsync,
postDeviceThingModelManagementMessageAnalysisTestAsync, postDeviceThingModelManagementMessageAnalysisTestAsync,
postDeviceThingModelManagementPageAsync, postDeviceThingModelManagementPageAsync,
postDeviceThingModelManagementUpdateAnalysisScriptByIdAsync,
postDeviceThingModelManagementUpdateAsync, postDeviceThingModelManagementUpdateAsync,
} from '#/api-client'; } from '#/api-client';
import { TableAction } from '#/components/table-action'; import { TableAction } from '#/components/table-action';
import { $t } from '#/locales'; import { $t } from '#/locales';
import DeviceThingModelCommandModal from './DeviceThingModelCommandModal.vue';
import DeviceThingModelPropertyModal from './DeviceThingModelPropertyModal.vue';
import { import {
addDeviceThingModelFormSchema, addDeviceThingModelFormSchema,
editDeviceThingModelFormSchema, editDeviceThingModelFormSchema,
@ -29,9 +33,6 @@ import {
tableSchema, tableSchema,
} from './schema'; } from './schema';
import DeviceThingModelPropertyModal from './DeviceThingModelPropertyModal.vue';
import DeviceThingModelCommandModal from './DeviceThingModelCommandModal.vue';
defineOptions({ defineOptions({
name: 'DeviceThingModelManagement', name: 'DeviceThingModelManagement',
}); });
@ -55,8 +56,7 @@ const formOptions: VbenFormProps = {
submitOnChange: false, submitOnChange: false,
handleValuesChange: async (values, changedFields) => { handleValuesChange: async (values, changedFields) => {
// //
if (changedFields.includes('ioTPlatform')) { if (changedFields.includes('ioTPlatform') && values.ioTPlatform) {
if (values.ioTPlatform) {
ioTPlatform.value = String(values.ioTPlatform); ioTPlatform.value = String(values.ioTPlatform);
if (gridApi?.formApi) { if (gridApi?.formApi) {
await gridApi.formApi.setValues({ await gridApi.formApi.setValues({
@ -65,15 +65,12 @@ const formOptions: VbenFormProps = {
} }
productId.value = ''; productId.value = '';
} }
}
// productId // productId
if (changedFields.includes('ioTPlatformProductId')) { if (changedFields.includes('ioTPlatformProductId')) {
if (values.ioTPlatformProductId) { productId.value = values.ioTPlatformProductId
productId.value = String(values.ioTPlatformProductId); ? String(values.ioTPlatformProductId)
} else { : '';
productId.value = '';
}
setTimeout(async () => { setTimeout(async () => {
if (gridApi && gridApi.reload) { if (gridApi && gridApi.reload) {
@ -121,9 +118,7 @@ const gridOptions: VxeGridProps<any> = {
// //
const latestFormValues = const latestFormValues =
formValues || formValues ||
(gridApi && gridApi.formApi (gridApi && gridApi.formApi ? await gridApi.formApi.getValues() : {});
? await gridApi.formApi.getValues()
: {});
// 使使 // 使使
const currentPlatform = const currentPlatform =
@ -266,21 +261,25 @@ const [TestScriptForm, testScriptFormApi] = useVbenForm({
component: 'Input', component: 'Input',
fieldName: 'functionName', fieldName: 'functionName',
label: '函数名称', label: '函数名称',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
placeholder: $t('common.pleaseInput') + '函数名称', placeholder: `${$t('common.pleaseInput')}函数名称`,
}, },
}, },
{ {
component: 'Textarea', component: 'Textarea',
fieldName: 'functionCode', fieldName: 'functionCode',
label: '函数代码', label: '函数代码',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
rows: 8, rows: 8,
placeholder: $t('common.pleaseInput') + '函数代码', placeholder: `${$t('common.pleaseInput')}函数代码`,
}, },
}, },
{ {
@ -289,7 +288,8 @@ const [TestScriptForm, testScriptFormApi] = useVbenForm({
label: '函数参数JSON格式', label: '函数参数JSON格式',
componentProps: { componentProps: {
rows: 6, rows: 6,
placeholder: '请输入函数参数JSON格式例如{"key1": "value1", "key2": "value2"}', placeholder:
'请输入函数参数JSON格式例如{"key1": "value1", "key2": "value2"}',
}, },
}, },
], ],
@ -301,6 +301,10 @@ const [TestScriptForm, testScriptFormApi] = useVbenForm({
// //
const testResult = ref<string>(''); const testResult = ref<string>('');
//
const testDeviceThingModelId = ref<string>('');
const testDeviceModelName = ref<string>('');
// / // /
async function submit() { async function submit() {
const isEdit = !!editRow.value.id; const isEdit = !!editRow.value.id;
@ -334,15 +338,11 @@ async function submit() {
await gridApi.reload(latestValues); await gridApi.reload(latestValues);
} }
} else { } else {
Message.error( Message.error(isEdit ? $t('common.editFail') : $t('common.addFail'));
isEdit ? $t('common.editFail') : $t('common.addFail'),
);
} }
} catch (error) { } catch (error) {
console.error('提交设备端物模型失败:', error); console.error('提交设备端物模型失败:', error);
Message.error( Message.error(isEdit ? $t('common.editFail') : $t('common.addFail'));
isEdit ? $t('common.editFail') : $t('common.addFail'),
);
} }
} }
@ -379,7 +379,15 @@ const openAddModal = async () => {
}; };
// //
function openTestScriptModal() { function openTestScriptModal(record?: any) {
// Id/
if (record && record.id) {
testDeviceThingModelId.value = record.id;
testDeviceModelName.value = record.deviceModelName || '';
} else {
testDeviceThingModelId.value = '';
testDeviceModelName.value = '';
}
testScriptFormApi.resetForm(); testScriptFormApi.resetForm();
testResult.value = ''; testResult.value = '';
testScriptModalApi.open(); testScriptModalApi.open();
@ -392,6 +400,12 @@ async function buildAndOpenTestScript(record: any) {
return; return;
} }
// true
if (record.functionAnalysisFlag === true) {
Message.warning('解析启用状态下不允许构建函数');
return;
}
try { try {
const resp = const resp =
await postDeviceThingModelManagementBuildAnalysisScriptByIdAsync({ await postDeviceThingModelManagementBuildAnalysisScriptByIdAsync({
@ -400,8 +414,8 @@ async function buildAndOpenTestScript(record: any) {
console.log('函数构建接口返回:', resp.data); console.log('函数构建接口返回:', resp.data);
// //
openTestScriptModal(); openTestScriptModal(record);
// //
await nextTick(); await nextTick();
@ -418,6 +432,38 @@ async function buildAndOpenTestScript(record: any) {
} }
} }
// /
async function enableAnalysisScript(record: any) {
if (!record.id) {
Message.warning('设备端物模型ID不存在');
return;
}
try {
const isEnabled = record.functionAnalysisFlag === true;
const resp =
await postDeviceThingModelManagementUpdateAnalysisScriptByIdAsync({
query: {
id: record.id,
},
});
if (resp.data) {
Message.success(isEnabled ? '禁用解析成功' : '启用解析成功');
//
if (gridApi && gridApi.reload) {
await gridApi.reload();
}
} else {
Message.error(isEnabled ? '禁用解析失败' : '启用解析失败');
}
} catch (error) {
console.error('解析启用失败:', error);
Message.error('解析状态更新失败');
}
}
// //
async function submitTestScript() { async function submitTestScript() {
const { valid } = await testScriptFormApi.validate(); const { valid } = await testScriptFormApi.validate();
@ -430,7 +476,7 @@ async function submitTestScript() {
if (formValues.parameters) { if (formValues.parameters) {
try { try {
parameters = JSON.parse(formValues.parameters); parameters = JSON.parse(formValues.parameters);
} catch (error) { } catch {
Message.error('函数参数格式错误请输入有效的JSON格式'); Message.error('函数参数格式错误请输入有效的JSON格式');
return; return;
} }
@ -457,6 +503,38 @@ async function submitTestScript() {
} }
} }
//
async function updateFunctionScript() {
if (!testDeviceThingModelId.value) {
Message.warning('当前未关联具体设备端物模型,无法更新函数');
return;
}
const { valid } = await testScriptFormApi.validate();
if (!valid) return;
const formValues = await testScriptFormApi.getValues();
try {
const resp = await postDeviceThingModelManagementBuildAnalysisScriptAsync({
body: {
id: testDeviceThingModelId.value,
scriptName: formValues.functionName,
functionScript: formValues.functionCode,
},
});
if (resp.data) {
Message.success('更新函数成功');
} else {
Message.error('更新函数失败');
}
} catch (error) {
console.error('更新函数失败:', error);
Message.error('更新函数失败');
}
}
// //
async function onDel(record: any) { async function onDel(record: any) {
try { try {
@ -514,8 +592,7 @@ onMounted(async () => {
<Page auto-content-height> <Page auto-content-height>
<Grid> <Grid>
<template #toolbar-actions> <template #toolbar-actions>
<TableAction <TableAction :actions="[
:actions="[
{ {
label: $t('common.add'), label: $t('common.add'),
type: 'primary', type: 'primary',
@ -530,26 +607,19 @@ onMounted(async () => {
onClick: openTestScriptModal, onClick: openTestScriptModal,
auth: ['AbpIdentity.Users.Create'], auth: ['AbpIdentity.Users.Create'],
}, },
]" ]" />
/>
</template> </template>
<!-- 解析启用列 --> <!-- 解析启用列 -->
<template #functionAnalysisFlag="{ row }"> <template #functionAnalysisFlag="{ row }">
<component <component :is="h(Tag, { color: row.functionAnalysisFlag ? 'green' : 'red' }, () =>
:is=" row.functionAnalysisFlag ? '是' : '否',
h(
Tag,
{ color: row.functionAnalysisFlag ? 'green' : 'red' },
() => (row.functionAnalysisFlag ? '是' : '否'),
) )
" " />
/>
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<TableAction <TableAction :actions="[
:actions="[
{ {
label: $t('common.edit'), label: $t('common.edit'),
type: 'link', type: 'link',
@ -564,6 +634,7 @@ onMounted(async () => {
auth: ['AbpIdentity.Users.Update'], auth: ['AbpIdentity.Users.Update'],
onClick: openPropertyModal.bind(null, row), onClick: openPropertyModal.bind(null, row),
}, },
]" :drop-down-actions="[
{ {
label: '指令管理', label: '指令管理',
type: 'link', type: 'link',
@ -571,27 +642,44 @@ onMounted(async () => {
auth: ['AbpIdentity.Users.Update'], auth: ['AbpIdentity.Users.Update'],
onClick: openCommandModal.bind(null, row), onClick: openCommandModal.bind(null, row),
}, },
{
label: row.functionAnalysisFlag ? '禁用解析' : '启用解析',
type: 'input',
size: 'small',
auth: ['AbpIdentity.Users.Update'],
style: row.functionAnalysisFlag
? { color: '#ff4d4f' } //
: { color: '#52c41a' }, // 绿
popConfirm: {
title: row.functionAnalysisFlag
? '确定要禁用该设备端物模型的解析吗?'
: '确定要启用该设备端物模型的解析吗?',
confirm: enableAnalysisScript.bind(null, row),
},
},
{ {
label: '函数构建', label: '函数构建',
type: 'link', type: 'link',
size: 'small', size: 'small',
icon: 'ant-design:code-outlined',
auth: ['AbpIdentity.Users.Update'], auth: ['AbpIdentity.Users.Update'],
onClick: buildAndOpenTestScript.bind(null, row), disabled: row.functionAnalysisFlag === true,
popConfirm: {
title: '确定要为该设备端物模型执行函数构建吗?',
confirm: buildAndOpenTestScript.bind(null, row),
},
}, },
{ {
label: $t('common.delete'), label: $t('common.delete'),
icon: 'ant-design:delete-outlined',
type: 'link', type: 'link',
size: 'small', size: 'small',
auth: ['AbpIdentity.Users.Delete'], auth: ['AbpIdentity.Users.Delete'],
style: { color: '#ff4d4f' },
popConfirm: { popConfirm: {
title: $t('common.askConfirmDelete'), title: $t('common.askConfirmDelete'),
confirm: onDel.bind(null, row), confirm: onDel.bind(null, row),
}, },
}, },
]" ]" />
/>
</template> </template>
</Grid> </Grid>
@ -601,22 +689,23 @@ onMounted(async () => {
<!-- 指令管理弹窗独立组件 --> <!-- 指令管理弹窗独立组件 -->
<CommandModal /> <CommandModal />
<ThingModelModal <ThingModelModal :title="editRow.id ? $t('common.edit') : $t('common.add')" class="w-[800px]">
:title="editRow.id ? $t('common.edit') : $t('common.add')"
class="w-[800px]"
>
<component :is="editRow.id ? EditForm : AddForm" /> <component :is="editRow.id ? EditForm : AddForm" />
</ThingModelModal> </ThingModelModal>
<!-- 函数脚本测试弹窗 --> <!-- 函数脚本测试弹窗 -->
<TestScriptModal title="函数脚本测试" class="w-[900px]"> <TestScriptModal title="函数脚本测试" class="w-[900px]">
<div v-if="testDeviceModelName" class="mb-2 flex items-center justify-between text-base font-semibold">
<span>设备端物模型{{ testDeviceModelName }}</span>
<Button v-if="testDeviceThingModelId" type="primary" @click="updateFunctionScript">
更新函数
</Button>
</div>
<TestScriptForm /> <TestScriptForm />
<div v-if="testResult" class="mt-4"> <div v-if="testResult" class="mt-4">
<div class="mb-2 font-semibold">测试结果</div> <div class="mb-2 font-semibold">测试结果</div>
<pre class="bg-gray-50 p-4 rounded border overflow-auto max-h-96 text-sm">{{ testResult }}</pre> <pre class="max-h-96 overflow-auto rounded border bg-gray-50 p-4 text-sm">{{ testResult }}</pre>
</div> </div>
</TestScriptModal> </TestScriptModal>
</Page> </Page>
</template> </template>

View File

@ -116,7 +116,7 @@ export const tableSchema = computed(() => [
minWidth: 120, minWidth: 120,
showOverflow: 'tooltip', showOverflow: 'tooltip',
formatter: ({ cellValue }: { cellValue: any }) => { formatter: ({ cellValue }: { cellValue: any }) => {
const map: Record<string | number, string> = { const map: Record<number | string, string> = {
1: 'CTWing', 1: 'CTWing',
2: 'OneNET', 2: 'OneNET',
}; };
@ -152,7 +152,7 @@ export const tableSchema = computed(() => [
{ {
field: 'action', field: 'action',
title: $t('common.action'), title: $t('common.action'),
width: 360, width: 160,
fixed: 'right', fixed: 'right',
slots: { default: 'action' }, slots: { default: 'action' },
}, },
@ -164,18 +164,22 @@ export const addDeviceThingModelFormSchema = computed(() => [
component: 'Input', component: 'Input',
fieldName: 'deviceModelName', fieldName: 'deviceModelName',
label: '设备端物模型名称', label: '设备端物模型名称',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
placeholder: $t('common.pleaseInput') + '设备端物模型名称', placeholder: `${$t('common.pleaseInput')}设备端物模型名称`,
}, },
}, },
{ {
component: 'ApiSelect', component: 'ApiSelect',
fieldName: 'ioTPlatform', fieldName: 'ioTPlatform',
label: $t('abp.deviceInfos.ioTPlatform'), label: $t('abp.deviceInfos.ioTPlatform'),
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
api: getCommonGetSelectList, api: getCommonGetSelectList,
params: { params: {
@ -208,8 +212,10 @@ export const addDeviceThingModelFormSchema = computed(() => [
component: 'ApiSelect', component: 'ApiSelect',
fieldName: 'ioTPlatformProductId', fieldName: 'ioTPlatformProductId',
label: $t('common.BelongingProductName'), label: $t('common.BelongingProductName'),
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: (formValues: any) => { componentProps: (formValues: any) => {
const platform = formValues?.ioTPlatform; const platform = formValues?.ioTPlatform;
@ -254,10 +260,12 @@ export const addDeviceThingModelFormSchema = computed(() => [
component: 'Input', component: 'Input',
fieldName: 'scriptName', fieldName: 'scriptName',
label: '脚本函数名称', label: '脚本函数名称',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
placeholder: $t('common.pleaseInput') + '脚本函数名称', placeholder: `${$t('common.pleaseInput')}脚本函数名称`,
}, },
}, },
{ {
@ -277,18 +285,22 @@ export const editDeviceThingModelFormSchema = computed(() => [
component: 'Input', component: 'Input',
fieldName: 'deviceModelName', fieldName: 'deviceModelName',
label: '设备端物模型名称', label: '设备端物模型名称',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
placeholder: $t('common.pleaseInput') + '设备端物模型名称', placeholder: `${$t('common.pleaseInput')}设备端物模型名称`,
}, },
}, },
{ {
component: 'ApiSelect', component: 'ApiSelect',
fieldName: 'ioTPlatform', fieldName: 'ioTPlatform',
label: $t('abp.deviceInfos.ioTPlatform'), label: $t('abp.deviceInfos.ioTPlatform'),
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
api: getCommonGetSelectList, api: getCommonGetSelectList,
params: { params: {
@ -322,8 +334,10 @@ export const editDeviceThingModelFormSchema = computed(() => [
component: 'ApiSelect', component: 'ApiSelect',
fieldName: 'ioTPlatformProductId', fieldName: 'ioTPlatformProductId',
label: $t('common.BelongingProductName'), label: $t('common.BelongingProductName'),
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: (formValues: any) => { componentProps: (formValues: any) => {
const platform = formValues?.ioTPlatform; const platform = formValues?.ioTPlatform;
@ -369,10 +383,12 @@ export const editDeviceThingModelFormSchema = computed(() => [
component: 'Input', component: 'Input',
fieldName: 'scriptName', fieldName: 'scriptName',
label: '脚本函数名称', label: '脚本函数名称',
rules: z rules: z.preprocess(
.preprocess((v) => (v == null ? '' : v), z.string().min(1, $t('common.required'))), (v) => (v == null ? '' : v),
z.string().min(1, $t('common.required')),
),
componentProps: { componentProps: {
placeholder: $t('common.pleaseInput') + '脚本函数名称', placeholder: `${$t('common.pleaseInput')}脚本函数名称`,
}, },
}, },
{ {
@ -385,5 +401,3 @@ export const editDeviceThingModelFormSchema = computed(() => [
}, },
}, },
]); ]);