fix(iot): 修复 6 处 bug(P1×5 + P3×1)

- product/product/data.ts: 网关子设备不显示联网方式
  show 条件由 != GATEWAY 改为 != GATEWAY_SUB(原写法漏判子设备)
- home/message-trend-card.vue: onMounted 兜底首次拉取
  ShortcutDateRangePicker 早期 emit 触发的请求落在 useEcharts
  isActiveRef=false 阶段会被静默丢弃;并加 isFirstMount guard 跳过
  子组件首次 emit,消除首次进入的双请求
- ota/firmware/data.ts: FileUpload accept 改回 ['bin','zip','pdf']
  并同步 helpText,对齐 vue3 源约定
- device/device/index.vue: 批量删除补 confirm 二次确认弹窗,
  与 system 模块批删风格一致,避免一键误删
- device/device/detail/modules/modbus-point-form.vue: 字节序仅在
  「为空 / 不属于新 rawDataType 合法选项」才重置,避免编辑回填时
  setValues 触发 handleValuesChange 把已保存字节序覆盖

antd + ele 两端同步。
pull/348/head
YunaiV 2026-05-23 22:04:08 +08:00
parent 152964395d
commit ef57c96b2f
10 changed files with 62 additions and 14 deletions

View File

@ -246,10 +246,20 @@ const [Form, formApi] = useVbenForm({
if (option && option.registerCount > 0) { if (option && option.registerCount > 0) {
await formApi.setFieldValue('registerCount', option.registerCount); await formApi.setFieldValue('registerCount', option.registerCount);
} }
// // rawDataType
// setValues
const byteOrderOptions = getByteOrderOptions(rawDataType); const byteOrderOptions = getByteOrderOptions(rawDataType);
if (byteOrderOptions.length > 0) { if (byteOrderOptions.length > 0) {
await formApi.setFieldValue('byteOrder', byteOrderOptions[0]!.value); const currentByteOrder = values.byteOrder;
const isCurrentValid =
!!currentByteOrder &&
byteOrderOptions.some((opt) => opt.value === currentByteOrder);
if (!isCurrentValid) {
await formApi.setFieldValue(
'byteOrder',
byteOrderOptions[0]!.value,
);
}
} }
} }
} }

View File

@ -9,7 +9,7 @@ import type { IotProductApi } from '#/api/iot/product/product';
import { nextTick, onMounted, ref } from 'vue'; import { nextTick, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui'; import { confirm, Page, useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
@ -169,6 +169,7 @@ async function handleDeleteBatch() {
message.warning('请选择要删除的设备'); message.warning('请选择要删除的设备');
return; return;
} }
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
const hideLoading = message.loading({ const hideLoading = message.loading({
content: $t('ui.actionMessage.deletingBatch'), content: $t('ui.actionMessage.deletingBatch'),
duration: 0, duration: 0,

View File

@ -3,7 +3,7 @@ import type { Dayjs } from 'dayjs';
import type { IotStatisticsApi } from '#/api/iot/statistics'; import type { IotStatisticsApi } from '#/api/iot/statistics';
import { computed, nextTick, reactive, ref } from 'vue'; import { computed, nextTick, onMounted, reactive, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@ -26,6 +26,7 @@ const loading = ref(false); // 加载状态
const messageData = ref<IotStatisticsApi.DeviceMessageSummaryByDateRespVO[]>( const messageData = ref<IotStatisticsApi.DeviceMessageSummaryByDateRespVO[]>(
[], [],
); // ); //
const isFirstMount = ref(true); // mount emit
/** 时间范围(仅日期,不包含时分秒) */ /** 时间范围(仅日期,不包含时分秒) */
const dateRange = ref<[string, string]>([ const dateRange = ref<[string, string]>([
@ -74,6 +75,11 @@ function handleDateRangeChange(times?: [Dayjs, Dayjs]) {
]; ];
// 00:00:00 23:59:59 // 00:00:00 23:59:59
queryParams.times = formatDateRangeWithTime(dateRange.value); queryParams.times = formatDateRangeWithTime(dateRange.value);
if (isFirstMount.value) {
// ShortcutDateRangePicker mount emit fetch
// onMounted
return;
}
handleQuery(); handleQuery();
} }
@ -123,6 +129,13 @@ async function renderChartWhenReady() {
initChart(); initChart();
} }
//
// ShortcutDateRangePicker emit useEcharts isActiveRef = false renderEcharts
// handleDateRangeChange isFirstMount=true fetch
onMounted(() => {
fetchMessageData();
isFirstMount.value = false;
});
</script> </script>
<template> <template>

View File

@ -101,9 +101,9 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'FileUpload', component: 'FileUpload',
componentProps: { componentProps: {
maxNumber: 1, maxNumber: 1,
accept: ['bin', 'hex', 'zip'], accept: ['bin', 'zip', 'pdf'],
maxSize: 50, maxSize: 50,
helpText: '支持上传 .bin、.hex、.zip 格式的固件文件,最大 50MB', helpText: '支持上传 .bin、.zip、.pdf 格式的固件文件,最大 50MB',
}, },
rules: 'required', rules: 'required',
dependencies: { dependencies: {

View File

@ -131,7 +131,7 @@ export function useBasicFormSchema(
// 网关子设备走网关联网,不需要联网方式 // 网关子设备走网关联网,不需要联网方式
dependencies: { dependencies: {
triggerFields: ['deviceType'], triggerFields: ['deviceType'],
show: (values) => values.deviceType !== DeviceTypeEnum.GATEWAY, show: (values) => values.deviceType !== DeviceTypeEnum.GATEWAY_SUB,
}, },
rules: 'required', rules: 'required',
}, },

View File

@ -241,10 +241,20 @@ const [Form, formApi] = useVbenForm({
if (option && option.registerCount > 0) { if (option && option.registerCount > 0) {
await formApi.setFieldValue('registerCount', option.registerCount); await formApi.setFieldValue('registerCount', option.registerCount);
} }
// // rawDataType
// setValues
const byteOrderOptions = getByteOrderOptions(rawDataType); const byteOrderOptions = getByteOrderOptions(rawDataType);
if (byteOrderOptions.length > 0) { if (byteOrderOptions.length > 0) {
await formApi.setFieldValue('byteOrder', byteOrderOptions[0]!.value); const currentByteOrder = values.byteOrder;
const isCurrentValid =
!!currentByteOrder &&
byteOrderOptions.some((opt) => opt.value === currentByteOrder);
if (!isCurrentValid) {
await formApi.setFieldValue(
'byteOrder',
byteOrderOptions[0]!.value,
);
}
} }
} }
} }

View File

@ -9,7 +9,7 @@ import type { IotProductApi } from '#/api/iot/product/product';
import { nextTick, onMounted, ref } from 'vue'; import { nextTick, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui'; import { confirm, Page, useVbenModal } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
@ -169,6 +169,7 @@ async function handleDeleteBatch() {
ElMessage.warning('请选择要删除的设备'); ElMessage.warning('请选择要删除的设备');
return; return;
} }
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
const loadingInstance = ElLoading.service({ const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deletingBatch'), text: $t('ui.actionMessage.deletingBatch'),
}); });

View File

@ -3,7 +3,7 @@ import type { Dayjs } from 'dayjs';
import type { IotStatisticsApi } from '#/api/iot/statistics'; import type { IotStatisticsApi } from '#/api/iot/statistics';
import { computed, nextTick, reactive, ref } from 'vue'; import { computed, nextTick, onMounted, reactive, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants'; import { DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
@ -26,6 +26,7 @@ const loading = ref(false); // 加载状态
const messageData = ref<IotStatisticsApi.DeviceMessageSummaryByDateRespVO[]>( const messageData = ref<IotStatisticsApi.DeviceMessageSummaryByDateRespVO[]>(
[], [],
); // ); //
const isFirstMount = ref(true); // mount emit
/** 时间范围(仅日期,不包含时分秒) */ /** 时间范围(仅日期,不包含时分秒) */
const dateRange = ref<[string, string]>([ const dateRange = ref<[string, string]>([
@ -74,6 +75,11 @@ function handleDateRangeChange(times?: [Dayjs, Dayjs]) {
]; ];
// 00:00:00 23:59:59 // 00:00:00 23:59:59
queryParams.times = formatDateRangeWithTime(dateRange.value); queryParams.times = formatDateRangeWithTime(dateRange.value);
if (isFirstMount.value) {
// ShortcutDateRangePicker mount emit fetch
// onMounted
return;
}
handleQuery(); handleQuery();
} }
@ -123,6 +129,13 @@ async function renderChartWhenReady() {
initChart(); initChart();
} }
//
// ShortcutDateRangePicker emit useEcharts isActiveRef = false renderEcharts
// handleDateRangeChange isFirstMount=true fetch
onMounted(() => {
fetchMessageData();
isFirstMount.value = false;
});
</script> </script>
<template> <template>

View File

@ -101,9 +101,9 @@ export function useFormSchema(): VbenFormSchema[] {
component: 'FileUpload', component: 'FileUpload',
componentProps: { componentProps: {
maxNumber: 1, maxNumber: 1,
accept: ['bin', 'hex', 'zip'], accept: ['bin', 'zip', 'pdf'],
maxSize: 50, maxSize: 50,
helpText: '支持上传 .bin、.hex、.zip 格式的固件文件,最大 50MB', helpText: '支持上传 .bin、.zip、.pdf 格式的固件文件,最大 50MB',
}, },
rules: 'required', rules: 'required',
dependencies: { dependencies: {

View File

@ -128,7 +128,7 @@ export function useBasicFormSchema(
// 网关子设备走网关联网,不需要联网方式 // 网关子设备走网关联网,不需要联网方式
dependencies: { dependencies: {
triggerFields: ['deviceType'], triggerFields: ['deviceType'],
show: (values) => values.deviceType !== DeviceTypeEnum.GATEWAY, show: (values) => values.deviceType !== DeviceTypeEnum.GATEWAY_SUB,
}, },
rules: 'required', rules: 'required',
}, },