fix: 产品、设备功能配置bug修复
This commit is contained in:
@@ -11,7 +11,7 @@ export namespace IotDeviceApi {
|
||||
productId: number; // 产品编号
|
||||
productKey?: string; // 产品标识
|
||||
deviceType?: number; // 设备类型
|
||||
nickname?: string; // 设备备注名称
|
||||
nickname?: string; // 设备设备名称
|
||||
gatewayId?: number; // 网关设备 ID
|
||||
state?: number; // 设备状态
|
||||
status?: number; // 设备状态(兼容字段)
|
||||
|
||||
@@ -37,15 +37,15 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
{
|
||||
fieldName: 'deviceName',
|
||||
label: 'DeviceName',
|
||||
label: '设备标识',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入 DeviceName',
|
||||
placeholder: '请输入设备标识',
|
||||
},
|
||||
rules: z
|
||||
.string()
|
||||
.min(4, 'DeviceName 长度不能少于 4 个字符')
|
||||
.max(32, 'DeviceName 长度不能超过 32 个字符')
|
||||
.min(4, '设备标识长度不能少于 4 个字符')
|
||||
.max(32, '设备标识长度不能超过 32 个字符')
|
||||
.regex(
|
||||
/^[\w.\-:@]{4,32}$/,
|
||||
'支持英文字母、数字、下划线(_)、中划线(-)、点号(.)、半角冒号(:)和特殊字符@',
|
||||
@@ -68,18 +68,18 @@ export function useFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
{
|
||||
fieldName: 'nickname',
|
||||
label: '备注名称',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注名称',
|
||||
placeholder: '请输入设备名称',
|
||||
},
|
||||
rules: z
|
||||
.string()
|
||||
.min(4, '备注名称长度限制为 4~64 个字符')
|
||||
.max(64, '备注名称长度限制为 4~64 个字符')
|
||||
.min(4, '设备名称长度限制为 4~64 个字符')
|
||||
.max(64, '设备名称长度限制为 4~64 个字符')
|
||||
.regex(
|
||||
/^[\u4E00-\u9FA5\u3040-\u30FF\w]+$/,
|
||||
'备注名称只能包含中文、英文字母、日文、数字和下划线(_)',
|
||||
'设备名称只能包含中文、英文字母、日文、数字和下划线(_)',
|
||||
)
|
||||
.optional()
|
||||
.or(z.literal('')),
|
||||
@@ -208,19 +208,19 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
{
|
||||
fieldName: 'deviceName',
|
||||
label: 'DeviceName',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入 DeviceName',
|
||||
placeholder: '请输入设备名称',
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldName: 'nickname',
|
||||
label: '备注名称',
|
||||
label: '设备名称',
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入备注名称',
|
||||
placeholder: '请输入设备名称',
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
@@ -265,12 +265,12 @@ export function useGridColumns(): VxeTableGridOptions['columns'] {
|
||||
{ type: 'checkbox', width: 40 },
|
||||
{
|
||||
field: 'deviceName',
|
||||
title: 'DeviceName',
|
||||
title: '设备名称',
|
||||
minWidth: 150,
|
||||
},
|
||||
{
|
||||
field: 'nickname',
|
||||
title: '备注名称',
|
||||
title: '设备名称',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -275,14 +275,14 @@ onMounted(async () => {
|
||||
</Select>
|
||||
<Input
|
||||
v-model:value="searchParams.deviceName"
|
||||
placeholder="请输入 DeviceName"
|
||||
placeholder="请输入设备标识"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
@press-enter="handleSearch"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="searchParams.nickname"
|
||||
placeholder="请输入备注名称"
|
||||
placeholder="请输入设备名称"
|
||||
allow-clear
|
||||
style="width: 200px"
|
||||
@press-enter="handleSearch"
|
||||
|
||||
@@ -84,12 +84,12 @@ const queryFormRef = ref(); // 搜索的表单
|
||||
const columns = computed(() => {
|
||||
const baseColumns = [
|
||||
{
|
||||
title: 'DeviceName',
|
||||
title: '设备标识',
|
||||
dataIndex: 'deviceName',
|
||||
key: 'deviceName',
|
||||
},
|
||||
{
|
||||
title: '备注名称',
|
||||
title: '设备名称',
|
||||
dataIndex: 'nickname',
|
||||
key: 'nickname',
|
||||
},
|
||||
@@ -250,19 +250,19 @@ onMounted(async () => {
|
||||
</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item label="DeviceName" name="deviceName">
|
||||
<Form.Item label="设备标识" name="deviceName">
|
||||
<Input
|
||||
v-model:value="queryParams.deviceName"
|
||||
placeholder="请输入 DeviceName"
|
||||
placeholder="请输入设备标识"
|
||||
allow-clear
|
||||
@press-enter="handleQuery"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注名称" name="nickname">
|
||||
<Form.Item label="设备名称" name="nickname">
|
||||
<Input
|
||||
v-model:value="queryParams.nickname"
|
||||
placeholder="请输入备注名称"
|
||||
placeholder="请输入设备名称"
|
||||
allow-clear
|
||||
@press-enter="handleQuery"
|
||||
style="width: 240px"
|
||||
|
||||
@@ -98,10 +98,10 @@ function handleAuthInfoDialogClose() {
|
||||
:value="product.deviceType"
|
||||
/>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="DeviceName">
|
||||
<Descriptions.Item label="设备标识">
|
||||
{{ device.deviceName }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="备注名称">
|
||||
<Descriptions.Item label="设备名称">
|
||||
{{ device.nickname || '--' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="当前状态">
|
||||
@@ -122,7 +122,7 @@ function handleAuthInfoDialogClose() {
|
||||
<Descriptions.Item label="最后离线时间">
|
||||
{{ formatDate(device.offlineTime) }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="MQTT 连接参数">
|
||||
<Descriptions.Item label="连接参数">
|
||||
<Button
|
||||
type="link"
|
||||
@click="handleAuthInfoDialogOpen"
|
||||
@@ -168,7 +168,7 @@ function handleAuthInfoDialogClose() {
|
||||
<!-- 认证信息弹框 -->
|
||||
<Modal
|
||||
v-model:open="authDialogVisible"
|
||||
title="MQTT 连接参数"
|
||||
title="连接参数"
|
||||
width="640px"
|
||||
:footer="null"
|
||||
>
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { ContentWrap } from '@vben/common-ui';
|
||||
import { DICT_TYPE } from '@vben/constants';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { formatDate } from '@vben/utils';
|
||||
import { formatDateTime } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -229,7 +229,7 @@ defineExpose({
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'ts'">
|
||||
{{ formatDate(record.ts) }}
|
||||
{{ formatDateTime(record.ts) }}
|
||||
</template>
|
||||
<template v-else-if="column.key === 'upstream'">
|
||||
<Tag :color="record.upstream ? 'blue' : 'green'">
|
||||
|
||||
@@ -266,12 +266,18 @@ defineExpose({
|
||||
</div>
|
||||
|
||||
<!-- 设备名称 -->
|
||||
<div class="device-name" :title="item.deviceName">
|
||||
{{ item.deviceName }}
|
||||
<div class="device-name" :title="item.nickname">
|
||||
{{ item.nickname }}
|
||||
</div>
|
||||
|
||||
<!-- 信息区域 -->
|
||||
<div class="info-section">
|
||||
<div class="info-item">
|
||||
<span class="label">设备标识</span>
|
||||
<span class="value code" :title="item.deviceName">
|
||||
{{ item.deviceName }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">所属产品</span>
|
||||
<a
|
||||
@@ -286,6 +292,7 @@ defineExpose({
|
||||
{{ getProductName(item.productId) }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="label">设备类型</span>
|
||||
<Tag
|
||||
|
||||
@@ -129,17 +129,17 @@ export function useFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '产品状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
// {
|
||||
// fieldName: 'status',
|
||||
// label: '产品状态',
|
||||
// component: 'RadioGroup',
|
||||
// componentProps: {
|
||||
// options: getDictOptions(DICT_TYPE.IOT_PRODUCT_STATUS, 'number'),
|
||||
// buttonStyle: 'solid',
|
||||
// optionType: 'button',
|
||||
// },
|
||||
// rules: 'required',
|
||||
// },
|
||||
{
|
||||
fieldName: 'icon',
|
||||
label: '产品图标',
|
||||
@@ -278,17 +278,17 @@ export function useBasicFormSchema(formApi?: any): VbenFormSchema[] {
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '产品状态',
|
||||
component: 'RadioGroup',
|
||||
componentProps: {
|
||||
options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'),
|
||||
buttonStyle: 'solid',
|
||||
optionType: 'button',
|
||||
},
|
||||
rules: 'required',
|
||||
},
|
||||
// {
|
||||
// fieldName: 'status',
|
||||
// label: '产品状态',
|
||||
// component: 'RadioGroup',
|
||||
// componentProps: {
|
||||
// options: getDictOptions(DICT_TYPE.IOT_PRODUCT_STATUS, 'number'),
|
||||
// buttonStyle: 'solid',
|
||||
// optionType: 'button',
|
||||
// },
|
||||
// rules: 'required',
|
||||
// },
|
||||
{
|
||||
fieldName: 'locationType',
|
||||
label: '定位类型',
|
||||
|
||||
@@ -48,7 +48,7 @@ function formatDate(date?: Date | string) {
|
||||
{{ product.codecType || '-' }}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="产品状态">
|
||||
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="product.status" />
|
||||
<DictTag :type="DICT_TYPE.IOT_PRODUCT_STATUS" :value="product.status" />
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item
|
||||
v-if="
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { getProductPage } from '#/api/iot/product/product';
|
||||
import { DictTag } from '#/components/dict-tag';
|
||||
|
||||
// TODO @haohao:应该是 card-view.vue;
|
||||
|
||||
@@ -88,6 +89,15 @@ function getDeviceTypeColor(deviceType: number) {
|
||||
return colors[deviceType] || 'default';
|
||||
}
|
||||
|
||||
// 获取产品状态颜色
|
||||
function getProductStatusColor(status: number) {
|
||||
const colors: Record<number, string> = {
|
||||
0: 'gray',
|
||||
1: 'green',
|
||||
};
|
||||
return colors[status] || 'default';
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reload: getList,
|
||||
search: () => {
|
||||
@@ -134,6 +144,15 @@ onMounted(() => {
|
||||
<!-- 内容区域 -->
|
||||
<div class="mb-4 flex items-start">
|
||||
<div class="info-list flex-1">
|
||||
<div class="info-item">
|
||||
<span class="info-label">产品标识</span>
|
||||
<!-- TODO @haohao:展示 ?有点奇怪,要不小手? -->
|
||||
<Tooltip :title="item.productKey || item.id" placement="top">
|
||||
<span class="info-value product-key">
|
||||
{{ item.productKey || item.id }}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">产品分类</span>
|
||||
<span class="info-value text-primary">
|
||||
@@ -154,14 +173,13 @@ onMounted(() => {
|
||||
}}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<div class="info-item">
|
||||
<span class="info-label">产品标识</span>
|
||||
<!-- TODO @haohao:展示 ?有点奇怪,要不小手? -->
|
||||
<Tooltip :title="item.productKey || item.id" placement="top">
|
||||
<span class="info-value product-key">
|
||||
{{ item.productKey || item.id }}
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span class="info-label">产品状态</span>
|
||||
<DictTag
|
||||
:type="DICT_TYPE.IOT_PRODUCT_STATUS"
|
||||
:value="item.status"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO @haohao:这里是不是有 image?然后默认 icon -->
|
||||
|
||||
@@ -23,13 +23,20 @@ const props = defineProps<{ isStructDataSpecs?: boolean; modelValue: any }>();
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
const thingModelEvent = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||
|
||||
/** 默认选中,INFO 信息 */
|
||||
/** 确保 event 对象存在并初始化 type */
|
||||
watch(
|
||||
() => thingModelEvent.value.type,
|
||||
(val: string | undefined) =>
|
||||
isEmpty(val) &&
|
||||
(thingModelEvent.value.type = IoTThingModelEventTypeEnum.INFO.value),
|
||||
{ immediate: true },
|
||||
() => thingModelEvent.value,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
thingModelEvent.value = {
|
||||
type: IoTThingModelEventTypeEnum.INFO.value,
|
||||
outputParams: [],
|
||||
};
|
||||
} else if (isEmpty(val.type)) {
|
||||
val.type = IoTThingModelEventTypeEnum.INFO.value;
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -37,7 +44,7 @@ watch(
|
||||
<Form.Item
|
||||
:rules="[{ required: true, message: '请选择事件类型', trigger: 'change' }]"
|
||||
label="事件类型"
|
||||
name="event.type"
|
||||
:name="['event', 'type']"
|
||||
>
|
||||
<Radio.Group v-model:value="thingModelEvent.type">
|
||||
<Radio
|
||||
|
||||
@@ -51,10 +51,12 @@ const formData = ref<any>({
|
||||
},
|
||||
},
|
||||
service: {
|
||||
callType: 'async', // 默认异步调用
|
||||
inputParams: [],
|
||||
outputParams: [],
|
||||
},
|
||||
event: {
|
||||
type: 'info', // 默认信息类型
|
||||
outputParams: [],
|
||||
},
|
||||
});
|
||||
@@ -110,10 +112,15 @@ async function open(type: string, id?: number) {
|
||||
Object.keys(formData.value.service).length === 0
|
||||
) {
|
||||
formData.value.service = {
|
||||
callType: 'async', // 默认异步调用
|
||||
inputParams: [],
|
||||
outputParams: [],
|
||||
};
|
||||
} else {
|
||||
// 确保 callType 存在
|
||||
if (!formData.value.service.callType) {
|
||||
formData.value.service.callType = 'async';
|
||||
}
|
||||
// 确保参数数组存在
|
||||
if (!formData.value.service.inputParams) {
|
||||
formData.value.service.inputParams = [];
|
||||
@@ -128,9 +135,14 @@ async function open(type: string, id?: number) {
|
||||
Object.keys(formData.value.event).length === 0
|
||||
) {
|
||||
formData.value.event = {
|
||||
type: 'info', // 默认信息类型
|
||||
outputParams: [],
|
||||
};
|
||||
} else {
|
||||
// 确保 type 存在
|
||||
if (!formData.value.event.type) {
|
||||
formData.value.event.type = 'info';
|
||||
}
|
||||
// 确保参数数组存在
|
||||
if (!formData.value.event.outputParams) {
|
||||
formData.value.event.outputParams = [];
|
||||
|
||||
@@ -23,13 +23,21 @@ const props = defineProps<{ isStructDataSpecs?: boolean; modelValue: any }>();
|
||||
const emits = defineEmits(['update:modelValue']);
|
||||
const service = useVModel(props, 'modelValue', emits) as Ref<any>;
|
||||
|
||||
/** 默认选中,ASYNC 异步 */
|
||||
/** 确保 service 对象存在并初始化 callType */
|
||||
watch(
|
||||
() => service.value.callType,
|
||||
(val: string | undefined) =>
|
||||
isEmpty(val) &&
|
||||
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
|
||||
{ immediate: true },
|
||||
() => service.value,
|
||||
(val) => {
|
||||
if (!val) {
|
||||
service.value = {
|
||||
callType: IoTThingModelServiceCallTypeEnum.ASYNC.value,
|
||||
inputParams: [],
|
||||
outputParams: [],
|
||||
};
|
||||
} else if (isEmpty(val.callType)) {
|
||||
val.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value;
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -37,7 +45,7 @@ watch(
|
||||
<Form.Item
|
||||
:rules="[{ required: true, message: '请选择调用方式', trigger: 'change' }]"
|
||||
label="调用方式"
|
||||
name="service.callType"
|
||||
:name="['service', 'callType']"
|
||||
>
|
||||
<Radio.Group v-model:value="service.callType">
|
||||
<Radio
|
||||
|
||||
Reference in New Issue
Block a user