From aeff25209d03c19027bc9f2ae8b238ab926467b9 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 24 May 2026 10:11:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(iot):=20=E4=BF=AE=E5=A4=8D=2013=20=E5=A4=84?= =?UTF-8?q?=20bug=20=E5=B9=B6=E5=AE=8C=E6=88=90=20codex=20=E4=B8=89?= =?UTF-8?q?=E8=BD=AE=E6=94=B6=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按 codex 两轮 review 分批处理 IoT 模块 13 处 bug,对第二轮反馈中 B42/B47 的类型/字段问题做最终收尾,所有修复 web-antd / web-ele 两端同步。 主要修复: - B91 设备分组:删除前校验 deviceCount,分组下有设备时弹警告 - B40 物模型 array 数据规格:从 Radio.Group @change 事件正确取值(antd) - B42 物模型属性历史:list 写入时按 idx 生成 _rowKey,模板 row-key="_rowKey" list 类型改 IotDeviceApi.DeviceProperty & { _rowKey: string } 匹配后端 IotDevicePropertyMapper.xml 实际返回的字段 (修掉 codex 指出的 antd row-key TS2322 与 ele 同毫秒撞键) - B119 物模型表单:edit 模式禁用 identifier 编辑 - B47 场景规则主条件:产品/设备切换时清 deviceId/identifier/operator/value (修掉 codex 指出的 condition.value.param TS2339,Trigger 无 param) - B44 数据目的数据库配置:SQL 复制按钮 setTimeout 在 onBeforeUnmount 中清理 - B51 场景规则首页统计:total 取接口 result.total,其余基于当前页 - B29 产品卡片视图:图标为 URL 时改用 渲染,复用 @vben/utils 的 isHttpUrl - B43 首页设备地图:移除过度设计的 AbortController,回归 vue3 源项目同款 InfoWindow 监听写法,querySelector 限定到 .BMap_bubble_content 子树 - B105 场景规则设备选择器:productId 变化后旧 deviceId 不在新列表则清空 - B45 通用 key-value 编辑器:v-for key 改用递增的 _uid,避免编辑/删除时 DOM 复用错乱 - B132 设备导入表单:beforeUpload 校验 .xls/.xlsx - B126 设备详情:四个 tab 子组件 v-if 增加 device.id 守卫 附带工具收敛: - @vben-core/shared/utils 新增 formatDayjs,统一 antd TimePicker/DatePicker value-format 后回传的 Dayjs|string 归一 - 场景规则首页 updateStatistics 补回 JSDoc,对齐文件内其他 function 风格 验证: - 改动文件 pnpm exec eslint 0 error - pnpm -F @vben/web-antd / @vben/web-ele exec vue-tsc --noEmit --skipLibCheck 过滤 src/views/iot/|src/api/iot/ 均 0 hit --- apps/web-antd/package.json | 1 + .../api/mes/dv/checkplan/machinery/index.ts | 4 ++- .../src/api/mes/md/autocode/rule/index.ts | 4 +-- apps/web-antd/src/api/mes/md/item/index.ts | 4 +-- .../views/iot/device/device/detail/index.vue | 8 ++--- .../modules/thing-model-property-history.vue | 10 +++--- .../iot/device/device/modules/import-form.vue | 5 +++ .../src/views/iot/device/group/index.vue | 4 +++ .../iot/product/product/modules/card-view.vue | 8 +++++ .../config/components/key-value-editor.vue | 15 +++++---- .../data/sink/config/database-config-form.vue | 18 ++++++++-- .../configs/main-condition-inner-config.vue | 19 ++++++++--- .../scene/form/selectors/device-selector.vue | 22 +++++++++---- .../src/views/iot/rule/scene/index.vue | 8 ++--- .../thingmodel/modules/data-specs/array.vue | 3 +- .../src/views/iot/thingmodel/modules/form.vue | 6 +++- .../components/md-client-select-dialog.vue | 32 ++++++++++++++---- apps/web-ele/package.json | 1 + .../api/mes/dv/checkplan/machinery/index.ts | 4 ++- .../src/api/mes/dv/checkplan/subject/index.ts | 4 ++- .../src/api/mes/md/autocode/rule/index.ts | 4 +-- apps/web-ele/src/api/mes/md/item/index.ts | 4 +-- .../web-ele/src/api/mes/tm/tool/type/index.ts | 33 +++++++++++++------ .../views/iot/device/device/detail/index.vue | 8 ++--- .../modules/thing-model-property-history.vue | 11 ++++--- .../iot/device/device/modules/import-form.vue | 5 +++ .../src/views/iot/device/group/index.vue | 4 +++ .../iot/home/modules/device-map-card.vue | 25 +++++++------- .../iot/product/product/modules/card-view.vue | 8 +++++ .../config/components/key-value-editor.vue | 15 +++++---- .../data/sink/config/database-config-form.vue | 18 ++++++++-- .../configs/main-condition-inner-config.vue | 19 ++++++++--- .../scene/form/selectors/device-selector.vue | 22 +++++++++---- .../src/views/iot/rule/scene/index.vue | 8 ++--- .../src/views/iot/thingmodel/modules/form.vue | 6 +++- packages/@core/base/shared/src/utils/date.ts | 19 +++++++++++ pnpm-lock.yaml | 11 +++++++ pnpm-workspace.yaml | 1 + 38 files changed, 293 insertions(+), 108 deletions(-) diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 904f69a77..a3ae11913 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -60,6 +60,7 @@ "pinia": "catalog:", "steady-xml": "catalog:", "tinymce": "catalog:", + "tyme4ts": "catalog:", "video.js": "catalog:", "vue": "catalog:", "vue-dompurify-html": "catalog:", diff --git a/apps/web-antd/src/api/mes/dv/checkplan/machinery/index.ts b/apps/web-antd/src/api/mes/dv/checkplan/machinery/index.ts index 7e4d517c7..240533975 100644 --- a/apps/web-antd/src/api/mes/dv/checkplan/machinery/index.ts +++ b/apps/web-antd/src/api/mes/dv/checkplan/machinery/index.ts @@ -16,7 +16,9 @@ export namespace MesDvCheckPlanMachineryApi { /** 查询指定方案的设备列表 */ export function getCheckPlanMachineryListByPlan(planId: number) { - return requestClient.get(`/mes/dv/check-plan-machinery/list-by-plan?planId=${planId}`); + return requestClient.get( + `/mes/dv/check-plan-machinery/list-by-plan?planId=${planId}`, + ); } /** 新增方案设备关联 */ diff --git a/apps/web-antd/src/api/mes/md/autocode/rule/index.ts b/apps/web-antd/src/api/mes/md/autocode/rule/index.ts index be4d81f2f..7a2950360 100644 --- a/apps/web-antd/src/api/mes/md/autocode/rule/index.ts +++ b/apps/web-antd/src/api/mes/md/autocode/rule/index.ts @@ -54,7 +54,5 @@ export function deleteAutoCodeRule(id: number) { /** 导出编码规则 */ export function exportAutoCodeRule(params: PageParam) { - return requestClient.download('/mes/md/auto-code-rule/export-excel', { - params, - }); + return requestClient.download('/mes/md/auto-code-rule/export-excel', { params }); } diff --git a/apps/web-antd/src/api/mes/md/item/index.ts b/apps/web-antd/src/api/mes/md/item/index.ts index f2a23c0e6..9d3b107f8 100644 --- a/apps/web-antd/src/api/mes/md/item/index.ts +++ b/apps/web-antd/src/api/mes/md/item/index.ts @@ -34,9 +34,7 @@ export namespace MesMdItemApi { /** 查询物料产品分页 */ export function getItemPage(params: PageParam) { - return requestClient.get>('/mes/md/item/page', { - params, - }); + return requestClient.get>('/mes/md/item/page', { params }); } /** 查询物料产品详情 */ diff --git a/apps/web-antd/src/views/iot/device/device/detail/index.vue b/apps/web-antd/src/views/iot/device/device/detail/index.vue index 26457759e..4d7c29889 100644 --- a/apps/web-antd/src/views/iot/device/device/detail/index.vue +++ b/apps/web-antd/src/views/iot/device/device/detail/index.vue @@ -97,7 +97,7 @@ onMounted(async () => { @@ -127,7 +127,7 @@ onMounted(async () => { { @@ -151,7 +151,7 @@ onMounted(async () => { tab="Modbus 配置" > (); const dialogVisible = ref(false); // 弹窗的是否展示 const loading = ref(false); const viewMode = ref<'chart' | 'list'>('chart'); // 视图模式状态 -const list = ref([]); // 列表的数据 +const list = ref>([]); // 列表的数据 const total = ref(0); // 总数据量 const thingModelDataType = ref(''); // 物模型数据类型 const propertyIdentifier = ref(''); // 属性标识符 @@ -151,9 +151,11 @@ const paginationConfig = computed(() => ({ async function getList() { loading.value = true; try { - // 后端直接返回数组 const data = await getHistoryDevicePropertyList(queryParams); - list.value = (data || []) as IotDeviceApi.DevicePropertyDetail[]; + list.value = (data || []).map((item, idx) => ({ + ...item, + _rowKey: `${item.updateTime ?? ''}-${idx}`, // 后端直接返回数组,仅含 value/updateTime,给每行补 _rowKey 保证唯一 + })); total.value = list.value.length; // 如果是图表模式且支持图表展示,等待渲染图表 @@ -438,7 +440,7 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗 :data-source="list" :pagination="paginationConfig" :scroll="{ y: 500 }" - row-key="updateTime" + row-key="_rowKey" size="small" >