diff --git a/.vscode/settings.json b/.vscode/settings.json index f145f386afc3d73989d21f10df7d9c8b10361608..cc1dae50d6ae1c1e1fcf2e4e750444dd81cc2ca4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -87,7 +87,7 @@ "source.fixAll.stylelint": "explicit" }, "[vue]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "octref.vetur" }, "i18n-ally.localesPaths": ["src/locales"], "i18n-ally.keystyle": "nested", diff --git a/package.json b/package.json index aace42834532ee973bc4dde57b14c722f32db73d..7673d65dc92f85020507c5cbba98bc2d4615a7ac 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "sortablejs": "^1.15.3", "steady-xml": "^0.1.0", "url": "^0.11.3", + "v3-jsoneditor": "^0.0.6", "video.js": "^7.21.5", "vue": "3.5.12", "vue-dompurify-html": "^4.1.4", diff --git a/src/api/iot/device/index.ts b/src/api/iot/device/device/index.ts similarity index 36% rename from src/api/iot/device/index.ts rename to src/api/iot/device/device/index.ts index 903874b756f36b82a9827d285af6f920245cd232..252ea433c7ac1f97a3c590fefa6ce8790f3111f7 100644 --- a/src/api/iot/device/index.ts +++ b/src/api/iot/device/device/index.ts @@ -10,10 +10,9 @@ export interface DeviceVO { deviceType: number // 设备类型 nickname: string // 设备备注名称 gatewayId: number // 网关设备 ID - status: number // 设备状态 - statusLastUpdateTime: Date // 设备状态最后更新时间 - lastOnlineTime: Date // 最后上线时间 - lastOfflineTime: Date // 最后离线时间 + state: number // 设备状态 + onlineTime: Date // 最后上线时间 + offlineTime: Date // 最后离线时间 activeTime: Date // 设备激活时间 createTime: Date // 创建时间 ip: string // 设备的 IP 地址 @@ -28,11 +27,57 @@ export interface DeviceVO { areaId: number // 地区编码 address: string // 设备详细地址 serialNumber: string // 设备序列号 + config: string // 设备配置 + groupIds?: number[] // 添加分组 ID } -export interface DeviceUpdateStatusVO { - id: number // 设备 ID,主键,自增 - status: number // 设备状态 +// IoT 设备数据 VO +export interface DeviceDataVO { + deviceId: number // 设备编号 + thinkModelFunctionId: number // 物模型编号 + productKey: string // 产品标识 + deviceName: string // 设备名称 + identifier: string // 属性标识符 + name: string // 属性名称 + dataType: string // 数据类型 + updateTime: Date // 更新时间 + value: string // 最新值 +} + +// IoT 设备数据 VO +export interface DeviceHistoryDataVO { + time: number // 时间 + data: string // 数据 +} + +// IoT 设备状态枚举 +export enum DeviceStateEnum { + INACTIVE = 0, // 未激活 + ONLINE = 1, // 在线 + OFFLINE = 2 // 离线 +} + +// IoT 设备上行 Request VO +export interface IotDeviceUpstreamReqVO { + id: number // 设备编号 + type: string // 消息类型 + identifier: string // 标识符 + data: any // 请求参数 +} + +// IoT 设备下行 Request VO +export interface IotDeviceDownstreamReqVO { + id: number // 设备编号 + type: string // 消息类型 + identifier: string // 标识符 + data: any // 请求参数 +} + +// MQTT 连接参数 VO +export interface MqttConnectionParamsVO { + mqttClientId: string // MQTT 客户端 ID + mqttUsername: string // MQTT 用户名 + mqttPassword: string // MQTT 密码 } // 设备 API @@ -57,18 +102,68 @@ export const DeviceApi = { return await request.put({ url: `/iot/device/update`, data }) }, - // 修改设备状态 - updateDeviceStatus: async (data: DeviceUpdateStatusVO) => { - return await request.put({ url: `/iot/device/update-status`, data }) + // 修改设备分组 + updateDeviceGroup: async (data: { ids: number[]; groupIds: number[] }) => { + return await request.put({ url: `/iot/device/update-group`, data }) }, - // 删除设备 + // 删除单个设备 deleteDevice: async (id: number) => { return await request.delete({ url: `/iot/device/delete?id=` + id }) }, + // 删除多个设备 + deleteDeviceList: async (ids: number[]) => { + return await request.delete({ url: `/iot/device/delete-list`, params: { ids: ids.join(',') } }) + }, + + // 导出设备 + exportDeviceExcel: async (params: any) => { + return await request.download({ url: `/iot/device/export-excel`, params }) + }, + // 获取设备数量 getDeviceCount: async (productId: number) => { return await request.get({ url: `/iot/device/count?productId=` + productId }) + }, + + // 获取设备的精简信息列表 + getSimpleDeviceList: async (deviceType?: number) => { + return await request.get({ url: `/iot/device/simple-list?`, params: { deviceType } }) + }, + + // 获取导入模板 + importDeviceTemplate: async () => { + return await request.download({ url: `/iot/device/get-import-template` }) + }, + + // 设备上行 + upstreamDevice: async (data: IotDeviceUpstreamReqVO) => { + return await request.post({ url: `/iot/device/upstream`, data }) + }, + + // 设备下行 + downstreamDevice: async (data: IotDeviceDownstreamReqVO) => { + return await request.post({ url: `/iot/device/downstream`, data }) + }, + + // 获取设备属性最新数据 + getLatestDeviceProperties: async (params: any) => { + return await request.get({ url: `/iot/device/property/latest`, params }) + }, + + // 获取设备属性历史数据 + getHistoryDevicePropertyPage: async (params: any) => { + return await request.get({ url: `/iot/device/property/history-page`, params }) + }, + + // 查询设备日志分页 + getDeviceLogPage: async (params: any) => { + return await request.get({ url: `/iot/device/log/page`, params }) + }, + + // 获取设备MQTT连接参数 + getMqttConnectionParams: async (deviceId: number) => { + return await request.get({ url: `/iot/device/mqtt-connection-params`, params: { deviceId } }) } } diff --git a/src/api/iot/device/group/index.ts b/src/api/iot/device/group/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4debe8b1fdb8d75e8b6bc4acf2ee2d8968d13b2f --- /dev/null +++ b/src/api/iot/device/group/index.ts @@ -0,0 +1,43 @@ +import request from '@/config/axios' + +// IoT 设备分组 VO +export interface DeviceGroupVO { + id: number // 分组 ID + name: string // 分组名字 + status: number // 分组状态 + description: string // 分组描述 + deviceCount?: number // 设备数量 +} + +// IoT 设备分组 API +export const DeviceGroupApi = { + // 查询设备分组分页 + getDeviceGroupPage: async (params: any) => { + return await request.get({ url: `/iot/device-group/page`, params }) + }, + + // 查询设备分组详情 + getDeviceGroup: async (id: number) => { + return await request.get({ url: `/iot/device-group/get?id=` + id }) + }, + + // 新增设备分组 + createDeviceGroup: async (data: DeviceGroupVO) => { + return await request.post({ url: `/iot/device-group/create`, data }) + }, + + // 修改设备分组 + updateDeviceGroup: async (data: DeviceGroupVO) => { + return await request.put({ url: `/iot/device-group/update`, data }) + }, + + // 删除设备分组 + deleteDeviceGroup: async (id: number) => { + return await request.delete({ url: `/iot/device-group/delete?id=` + id }) + }, + + // 获取设备分组的精简信息列表 + getSimpleDeviceGroupList: async () => { + return await request.get({ url: `/iot/device-group/simple-list` }) + } +} diff --git a/src/api/iot/plugin/index.ts b/src/api/iot/plugin/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f68b5f941369c7df11690b8b472230ad69716a23 --- /dev/null +++ b/src/api/iot/plugin/index.ts @@ -0,0 +1,51 @@ +import request from '@/config/axios' + +// IoT 插件配置 VO +export interface PluginConfigVO { + id: number // 主键ID + pluginKey: string // 插件标识 + name: string // 插件名称 + description: string // 描述 + deployType: number // 部署方式 + fileName: string // 插件包文件名 + version: string // 插件版本 + type: number // 插件类型 + protocol: string // 设备插件协议类型 + status: number // 状态 + configSchema: string // 插件配置项描述信息 + config: string // 插件配置信息 + script: string // 插件脚本 +} + +// IoT 插件配置 API +export const PluginConfigApi = { + // 查询插件配置分页 + getPluginConfigPage: async (params: any) => { + return await request.get({ url: `/iot/plugin-config/page`, params }) + }, + + // 查询插件配置详情 + getPluginConfig: async (id: number) => { + return await request.get({ url: `/iot/plugin-config/get?id=` + id }) + }, + + // 新增插件配置 + createPluginConfig: async (data: PluginConfigVO) => { + return await request.post({ url: `/iot/plugin-config/create`, data }) + }, + + // 修改插件配置 + updatePluginConfig: async (data: PluginConfigVO) => { + return await request.put({ url: `/iot/plugin-config/update`, data }) + }, + + // 删除插件配置 + deletePluginConfig: async (id: number) => { + return await request.delete({ url: `/iot/plugin-config/delete?id=` + id }) + }, + + // 修改插件状态 + updatePluginStatus: async (data: any) => { + return await request.put({ url: `/iot/plugin-config/update-status`, data }) + } +} diff --git a/src/api/iot/product/category/index.ts b/src/api/iot/product/category/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..cad17f5c529f51d9452f5964c6bf1f2cd2962fa9 --- /dev/null +++ b/src/api/iot/product/category/index.ts @@ -0,0 +1,43 @@ +import request from '@/config/axios' + +// IoT 产品分类 VO +export interface ProductCategoryVO { + id: number // 分类 ID + name: string // 分类名字 + sort: number // 分类排序 + status: number // 分类状态 + description: string // 分类描述 +} + +// IoT 产品分类 API +export const ProductCategoryApi = { + // 查询产品分类分页 + getProductCategoryPage: async (params: any) => { + return await request.get({ url: `/iot/product-category/page`, params }) + }, + + // 查询产品分类详情 + getProductCategory: async (id: number) => { + return await request.get({ url: `/iot/product-category/get?id=` + id }) + }, + + // 新增产品分类 + createProductCategory: async (data: ProductCategoryVO) => { + return await request.post({ url: `/iot/product-category/create`, data }) + }, + + // 修改产品分类 + updateProductCategory: async (data: ProductCategoryVO) => { + return await request.put({ url: `/iot/product-category/update`, data }) + }, + + // 删除产品分类 + deleteProductCategory: async (id: number) => { + return await request.delete({ url: `/iot/product-category/delete?id=` + id }) + }, + + /** 获取产品分类精简列表 */ + getSimpleProductCategoryList: () => { + return request.get({ url: '/iot/product-category/simple-list' }) + } +} diff --git a/src/api/iot/product/index.ts b/src/api/iot/product/product/index.ts similarity index 72% rename from src/api/iot/product/index.ts rename to src/api/iot/product/product/index.ts index 1ffa490d511ded6989fbe8cc3cd5dec467ac900d..496fb049b5e055879fdc6573e5948655e60ac446 100644 --- a/src/api/iot/product/index.ts +++ b/src/api/iot/product/product/index.ts @@ -7,6 +7,9 @@ export interface ProductVO { productKey: string // 产品标识 protocolId: number // 协议编号 categoryId: number // 产品所属品类标识符 + categoryName?: string // 产品所属品类名称 + icon: string // 产品图标 + picUrl: string // 产品图片 description: string // 产品描述 validateType: number // 数据校验级别 status: number // 产品状态 @@ -18,6 +21,23 @@ export interface ProductVO { createTime: Date // 创建时间 } +// IOT 数据校验级别枚举类 +export enum ValidateTypeEnum { + WEAK = 0, // 弱校验 + NONE = 1 // 免校验 +} +// IOT 产品设备类型枚举类 0: 直连设备, 1: 网关子设备, 2: 网关设备 +export enum DeviceTypeEnum { + DEVICE = 0, // 直连设备 + GATEWAY_SUB = 1, // 网关子设备 + GATEWAY = 2 // 网关设备 +} +// IOT 数据格式枚举类 +export enum DataFormatEnum { + JSON = 0, // 标准数据格式(JSON) + CUSTOMIZE = 1 // 透传/自定义 +} + // IoT 产品 API export const ProductApi = { // 查询产品分页 @@ -57,6 +77,6 @@ export const ProductApi = { // 查询产品(精简)列表 getSimpleProductList() { - return request.get({ url: '/iot/product/list-all-simple' }) + return request.get({ url: '/iot/product/simple-list' }) } } diff --git a/src/api/iot/rule/databridge/index.ts b/src/api/iot/rule/databridge/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4eb6366886c88ec814222f4de6845d22710a667 --- /dev/null +++ b/src/api/iot/rule/databridge/index.ts @@ -0,0 +1,127 @@ +import request from '@/config/axios' + +// IoT 数据桥梁 VO +export interface DataBridgeVO { + id?: number // 桥梁编号 + name?: string // 桥梁名称 + description?: string // 桥梁描述 + status?: number // 桥梁状态 + direction?: number // 桥梁方向 + type?: number // 桥梁类型 + config?: + | HttpConfig + | MqttConfig + | RocketMQConfig + | KafkaMQConfig + | RabbitMQConfig + | RedisStreamMQConfig // 桥梁配置 +} + +interface Config { + type: string +} + +/** HTTP 配置 */ +export interface HttpConfig extends Config { + url: string + method: string + headers: Record + query: Record + body: string +} + +/** MQTT 配置 */ +export interface MqttConfig extends Config { + url: string + username: string + password: string + clientId: string + topic: string +} + +/** RocketMQ 配置 */ +export interface RocketMQConfig extends Config { + nameServer: string + accessKey: string + secretKey: string + group: string + topic: string + tags: string +} + +/** Kafka 配置 */ +export interface KafkaMQConfig extends Config { + bootstrapServers: string + username: string + password: string + ssl: boolean + topic: string +} + +/** RabbitMQ 配置 */ +export interface RabbitMQConfig extends Config { + host: string + port: number + virtualHost: string + username: string + password: string + exchange: string + routingKey: string + queue: string +} + +/** Redis Stream MQ 配置 */ +export interface RedisStreamMQConfig extends Config { + host: string + port: number + password: string + database: number + topic: string +} + +/** 数据桥梁类型 */ +// TODO @puhui999:枚举用 number 可以么? +export const IoTDataBridgeConfigType = { + HTTP: '1', + TCP: '2', + WEBSOCKET: '3', + MQTT: '10', + DATABASE: '20', + REDIS_STREAM: '21', + ROCKETMQ: '30', + RABBITMQ: '31', + KAFKA: '32' +} as const + +// 数据桥梁 API +export const DataBridgeApi = { + // 查询数据桥梁分页 + getDataBridgePage: async (params: any) => { + return await request.get({ url: `/iot/data-bridge/page`, params }) + }, + + // 查询数据桥梁详情 + getDataBridge: async (id: number) => { + return await request.get({ url: `/iot/data-bridge/get?id=` + id }) + }, + + // 新增数据桥梁 + createDataBridge: async (data: DataBridgeVO) => { + return await request.post({ url: `/iot/data-bridge/create`, data }) + }, + + // 修改数据桥梁 + updateDataBridge: async (data: DataBridgeVO) => { + return await request.put({ url: `/iot/data-bridge/update`, data }) + }, + + // 删除数据桥梁 + deleteDataBridge: async (id: number) => { + return await request.delete({ url: `/iot/data-bridge/delete?id=` + id }) + }, + + // 导出数据桥梁 Excel + exportDataBridge: async (params) => { + return await request.download({ url: `/iot/data-bridge/export-excel`, params }) + } +} diff --git a/src/api/iot/statistics/index.ts b/src/api/iot/statistics/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ca00d65dc8707d33bcebc305077bb733bdf4621 --- /dev/null +++ b/src/api/iot/statistics/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +/** IoT 统计数据类型 */ +export interface IotStatisticsSummaryRespVO { + productCategoryCount: number + productCount: number + deviceCount: number + deviceMessageCount: number + productCategoryTodayCount: number + productTodayCount: number + deviceTodayCount: number + deviceMessageTodayCount: number + deviceOnlineCount: number + deviceOfflineCount: number + deviceInactiveCount: number + productCategoryDeviceCounts: Record +} + +/** IoT 消息统计数据类型 */ +export interface IotStatisticsDeviceMessageSummaryRespVO { + upstreamCounts: Record + downstreamCounts: Record +} + +// IoT 数据统计 API +export const ProductCategoryApi = { + // 查询基础的数据统计 + getIotStatisticsSummary: async () => { + return await request.get({ + url: `/iot/statistics/get-summary` + }) + }, + + // 查询设备上下行消息的数据统计 + getIotStatisticsDeviceMessageSummary: async (params: { startTime: number; endTime: number }) => { + return await request.get({ + url: `/iot/statistics/get-log-summary`, + params + }) + } +} diff --git a/src/api/iot/thingmodel/index.ts b/src/api/iot/thingmodel/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5deaaad5b034e0b1b29f2f9e24eb76bd919863a5 --- /dev/null +++ b/src/api/iot/thingmodel/index.ts @@ -0,0 +1,88 @@ +import request from '@/config/axios' + +/** + * IoT 产品物模型 + */ +export interface ThingModelData { + id?: number // 物模型功能编号 + identifier?: string // 功能标识 + name?: string // 功能名称 + description?: string // 功能描述 + productId?: number // 产品编号 + productKey?: string // 产品标识 + dataType: string // 数据类型,与 dataSpecs 的 dataType 保持一致 + type: number // 功能类型 + property: ThingModelProperty // 属性 + event?: ThingModelEvent // 事件 + service?: ThingModelService // 服务 +} + +/** + * IoT 模拟设备 + */ +// TODO @super:和 ThingModelSimulatorData 会不会好点 +export interface SimulatorData extends ThingModelData { + simulateValue?: string | number // 用于存储模拟值 TODO @super:字段使用 value 会不会好点 +} + +/** + * ThingModelProperty 类型 + */ +export interface ThingModelProperty { + [key: string]: any +} + +/** + * ThingModelEvent 类型 + */ +export interface ThingModelEvent { + [key: string]: any +} + +/** + * ThingModelService 类型 + */ +export interface ThingModelService { + [key: string]: any +} + +// IoT 产品物模型 API +export const ThingModelApi = { + // 查询产品物模型分页 + getThingModelPage: async (params: any) => { + return await request.get({ url: `/iot/thing-model/page`, params }) + }, + + // 获得产品物模型列表 + getThingModelList: async (params: any) => { + return await request.get({ url: `/iot/thing-model/list`, params }) + }, + + // 获得产品物模型 + getThingModelListByProductId: async (params: any) => { + return await request.get({ + url: `/iot/thing-model/list-by-product-id`, + params + }) + }, + + // 查询产品物模型详情 + getThingModel: async (id: number) => { + return await request.get({ url: `/iot/thing-model/get?id=` + id }) + }, + + // 新增产品物模型 + createThingModel: async (data: ThingModelData) => { + return await request.post({ url: `/iot/thing-model/create`, data }) + }, + + // 修改产品物模型 + updateThingModel: async (data: ThingModelData) => { + return await request.put({ url: `/iot/thing-model/update`, data }) + }, + + // 删除产品物模型 + deleteThingModel: async (id: number) => { + return await request.delete({ url: `/iot/thing-model/delete?id=` + id }) + } +} diff --git a/src/api/iot/thinkmodelfunction/index.ts b/src/api/iot/thinkmodelfunction/index.ts deleted file mode 100644 index bd2e2d0f0283325fe274159e0099f0db6e642f40..0000000000000000000000000000000000000000 --- a/src/api/iot/thinkmodelfunction/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import request from '@/config/axios' - -// IoT 产品物模型 VO -export interface ThinkModelFunctionVO { - id: number // 物模型功能编号 - identifier: string // 功能标识 - name: string // 功能名称 - description: string // 功能描述 - productId: number // 产品编号 - productKey: string // 产品标识 - type: number // 功能类型 - property: string // 属性 - event: string // 事件 - service: string // 服务 -} - -// IoT 产品物模型 API -export const ThinkModelFunctionApi = { - // 查询产品物模型分页 - getThinkModelFunctionPage: async (params: any) => { - return await request.get({ url: `/iot/think-model-function/page`, params }) - }, - // 获得产品物模型 - getThinkModelFunctionListByProductId: async (params: any) => { - return await request.get({ - url: `/iot/think-model-function/list-by-product-id`, - params - }) - }, - - // 查询产品物模型详情 - getThinkModelFunction: async (id: number) => { - return await request.get({ url: `/iot/think-model-function/get?id=` + id }) - }, - - // 新增产品物模型 - createThinkModelFunction: async (data: ThinkModelFunctionVO) => { - return await request.post({ url: `/iot/think-model-function/create`, data }) - }, - - // 修改产品物模型 - updateThinkModelFunction: async (data: ThinkModelFunctionVO) => { - return await request.put({ url: `/iot/think-model-function/update`, data }) - }, - - // 删除产品物模型 - deleteThinkModelFunction: async (id: number) => { - return await request.delete({ url: `/iot/think-model-function/delete?id=` + id }) - }, - - // 导出产品物模型 Excel - exportThinkModelFunction: async (params) => { - return await request.download({ url: `/iot/think-model-function/export-excel`, params }) - } -} diff --git a/src/assets/imgs/iot/device.png b/src/assets/imgs/iot/device.png new file mode 100644 index 0000000000000000000000000000000000000000..79339cdf486e64bff9f5c3f89f16a7c20a8aadeb Binary files /dev/null and b/src/assets/imgs/iot/device.png differ diff --git a/src/assets/svgs/iot/card-fill.svg b/src/assets/svgs/iot/card-fill.svg new file mode 100644 index 0000000000000000000000000000000000000000..4c74ecdc148aafeb318af6b11a5ae9d3651a93a8 --- /dev/null +++ b/src/assets/svgs/iot/card-fill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/svgs/iot/cube.svg b/src/assets/svgs/iot/cube.svg new file mode 100644 index 0000000000000000000000000000000000000000..200ac1b1c0ce4d456bab9b59403a8f2a4fcf19e0 --- /dev/null +++ b/src/assets/svgs/iot/cube.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Table/src/Table.vue b/src/components/Table/src/Table.vue index 279a9faca0f10b6bb4b1a40cb25688694345653d..e9e50af3e353acee1c2b579490e2dccee6652566 100644 --- a/src/components/Table/src/Table.vue +++ b/src/components/Table/src/Table.vue @@ -56,7 +56,7 @@ export default defineComponent({ // 注册 onMounted(() => { const tableRef = unref(elTableRef) - emit('register', tableRef?.$parent, elTableRef) + emit('register', tableRef?.$parent, elTableRef.value) }) const pageSizeRef = ref(props.pageSize) diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 2a8b5e72013bbd9d94fb6dc5257b15f2e63412f3..ad8aaa1848e89c546131d3139e0c5687fc62b621 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -689,15 +689,15 @@ const remainingRouter: AppRouteRecordRaw[] = [ }, children: [ { - path: 'product/detail/:id', + path: 'product/product/detail/:id', name: 'IoTProductDetail', meta: { title: '产品详情', noCache: true, hidden: true, - activeMenu: '/iot/product' + activeMenu: '/iot/device/product' }, - component: () => import('@/views/iot/product/detail/index.vue') + component: () => import('@/views/iot/product/product/detail/index.vue') }, { path: 'device/detail/:id', @@ -706,9 +706,20 @@ const remainingRouter: AppRouteRecordRaw[] = [ title: '设备详情', noCache: true, hidden: true, - activeMenu: '/iot/device' + activeMenu: '/iot/device/device' + }, + component: () => import('@/views/iot/device/device/detail/index.vue') + }, + { + path: 'plugin/detail/:id', + name: 'IoTPluginDetail', + meta: { + title: '插件详情', + noCache: true, + hidden: true, + activeMenu: '/iot/plugin' }, - component: () => import('@/views/iot/device/detail/index.vue') + component: () => import('@/views/iot/plugin/detail/index.vue') } ] } diff --git a/src/utils/dict.ts b/src/utils/dict.ts index f572bbe70a23880ebc3a024a7a1306e00f8716e0..8211499232d9654e63e723068a4030020ca66f9a 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -236,9 +236,14 @@ export enum DICT_TYPE { IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型 IOT_DATA_FORMAT = 'iot_data_format', // IOT 数据格式 IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 接入网关协议 - IOT_DEVICE_STATUS = 'iot_device_status', // IOT 设备状态 - IOT_PRODUCT_FUNCTION_TYPE = 'iot_product_function_type', // IOT 产品功能类型 + IOT_DEVICE_STATE = 'iot_device_state', // IOT 设备状态 + IOT_THING_MODEL_TYPE = 'iot_thing_model_type', // IOT 产品功能类型 IOT_DATA_TYPE = 'iot_data_type', // IOT 数据类型 - IOT_UNIT_TYPE = 'iot_unit_type', // IOT 单位类型 - IOT_RW_TYPE = 'iot_rw_type' // IOT 读写类型 + IOT_THING_MODEL_UNIT = 'iot_thing_model_unit', // IOT 物模型单位 + IOT_RW_TYPE = 'iot_rw_type', // IOT 读写类型 + IOT_PLUGIN_DEPLOY_TYPE = 'iot_plugin_deploy_type', // IOT 插件部署类型 + IOT_PLUGIN_STATUS = 'iot_plugin_status', // IOT 插件状态 + IOT_PLUGIN_TYPE = 'iot_plugin_type', // IOT 插件类型 + IOT_DATA_BRIDGE_DIRECTION_ENUM = 'iot_data_bridge_direction_enum', // 桥梁方向 + IOT_DATA_BRIDGE_TYPE_ENUM = 'iot_data_bridge_type_enum' // 桥梁类型 } diff --git a/src/utils/index.ts b/src/utils/index.ts index fe72c4ef81ef7c47627026fd01229fb3bd6fecba..5c5088386e650d1b4e3eb4514e6782eee27bfe61 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -116,9 +116,23 @@ export function toAnyString() { return str } +/** + * 生成指定长度的随机字符串 + * + * @param length 字符串长度 + */ +export function generateRandomStr(length: number): string { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + let result = '' + for (let i = 0; i < length; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)) + } + return result +} + /** * 根据支持的文件类型生成 accept 属性值 - * + * * @param supportedFileTypes 支持的文件类型数组,如 ['PDF', 'DOC', 'DOCX'] * @returns 用于文件上传组件 accept 属性的字符串 */ @@ -503,7 +517,7 @@ export function jsonParse(str: string) { try { return JSON.parse(str) } catch (e) { - console.error(`str[${str}] 不是一个 JSON 字符串`) + console.log(`str[${str}] 不是一个 JSON 字符串`) return '' } } diff --git a/src/views/iot/device/detail/DeviceDetailsInfo.vue b/src/views/iot/device/detail/DeviceDetailsInfo.vue deleted file mode 100644 index 59b9d2542fa0d046dfa6dfd1ce8a6c4d4c39fec5..0000000000000000000000000000000000000000 --- a/src/views/iot/device/detail/DeviceDetailsInfo.vue +++ /dev/null @@ -1,123 +0,0 @@ - - diff --git a/src/views/iot/device/DeviceForm.vue b/src/views/iot/device/device/DeviceForm.vue similarity index 49% rename from src/views/iot/device/DeviceForm.vue rename to src/views/iot/device/device/DeviceForm.vue index cb02601253de78b746297e4877c620e86e4fd5f9..cf6e92ac0d5c86c3a98c49fab1b837ff7b9eaa6f 100644 --- a/src/views/iot/device/DeviceForm.vue +++ b/src/views/iot/device/device/DeviceForm.vue @@ -13,6 +13,7 @@ placeholder="请选择产品" :disabled="formType === 'update'" clearable + @change="handleProductChange" > + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/iot/device/device/DeviceGroupForm.vue b/src/views/iot/device/device/DeviceGroupForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..322e8a73a61bdd630d946d7bd8107135b0ee0025 --- /dev/null +++ b/src/views/iot/device/device/DeviceGroupForm.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/views/iot/device/device/DeviceImportForm.vue b/src/views/iot/device/device/DeviceImportForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..a6412f6a5f19203bfd0ee9a08e72ec4c5010c318 --- /dev/null +++ b/src/views/iot/device/device/DeviceImportForm.vue @@ -0,0 +1,139 @@ + + + diff --git a/src/views/iot/device/device/detail/DeviceDataDetail.vue b/src/views/iot/device/device/detail/DeviceDataDetail.vue new file mode 100644 index 0000000000000000000000000000000000000000..ced2a8a4af1a3bff49fedc102a5a2e4bbaf15719 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDataDetail.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/views/iot/device/device/detail/DeviceDetailConfig.vue b/src/views/iot/device/device/detail/DeviceDetailConfig.vue new file mode 100644 index 0000000000000000000000000000000000000000..13906b570e733591e30ceefc94e63c5bc2c228d8 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailConfig.vue @@ -0,0 +1,119 @@ + + + + diff --git a/src/views/iot/device/detail/DeviceDetailsHeader.vue b/src/views/iot/device/device/detail/DeviceDetailsHeader.vue similarity index 74% rename from src/views/iot/device/detail/DeviceDetailsHeader.vue rename to src/views/iot/device/device/detail/DeviceDetailsHeader.vue index 62960529d62127880f2801fbbf4f8431ca371f68..6fc876bc808a9658b3ec5caa15ebec68382f37b6 100644 --- a/src/views/iot/device/detail/DeviceDetailsHeader.vue +++ b/src/views/iot/device/device/detail/DeviceDetailsHeader.vue @@ -1,3 +1,4 @@ + diff --git a/src/views/iot/device/device/detail/DeviceDetailsLog.vue b/src/views/iot/device/device/detail/DeviceDetailsLog.vue new file mode 100644 index 0000000000000000000000000000000000000000..8bbad400834736a9e77c6322b896714b98183757 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailsLog.vue @@ -0,0 +1,166 @@ + + + + diff --git a/src/views/iot/device/device/detail/DeviceDetailsModel.vue b/src/views/iot/device/device/detail/DeviceDetailsModel.vue new file mode 100644 index 0000000000000000000000000000000000000000..26ddaa5447fb345b08dead382ad73c1ea8010819 --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailsModel.vue @@ -0,0 +1,134 @@ + + + diff --git a/src/views/iot/device/device/detail/DeviceDetailsSimulator.vue b/src/views/iot/device/device/detail/DeviceDetailsSimulator.vue new file mode 100644 index 0000000000000000000000000000000000000000..0ce918aa06d09ea23160ad5e1f9832e3c360f3bb --- /dev/null +++ b/src/views/iot/device/device/detail/DeviceDetailsSimulator.vue @@ -0,0 +1,331 @@ + + + + diff --git a/src/views/iot/device/detail/index.vue b/src/views/iot/device/device/detail/index.vue similarity index 39% rename from src/views/iot/device/detail/index.vue rename to src/views/iot/device/device/detail/index.vue index d56859bd3ae9dc9990588502e0e3fdf6a9bd2d70..a2bd3dec3591a78e9024a43272a57e5843f76795 100644 --- a/src/views/iot/device/detail/index.vue +++ b/src/views/iot/device/device/detail/index.vue @@ -6,38 +6,62 @@ @refresh="getDeviceData(id)" /> - - - + + + - - + + + + + + + + + + + + + + diff --git a/src/views/iot/device/device/index.vue b/src/views/iot/device/device/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..af382747942bc73da28d7c499632151c0fe73a51 --- /dev/null +++ b/src/views/iot/device/device/index.vue @@ -0,0 +1,516 @@ + + + diff --git a/src/views/iot/device/group/DeviceGroupForm.vue b/src/views/iot/device/group/DeviceGroupForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..648725730e8b1cf37d889aafa63758c628dc6ae3 --- /dev/null +++ b/src/views/iot/device/group/DeviceGroupForm.vue @@ -0,0 +1,112 @@ + + diff --git a/src/views/iot/product/index.vue b/src/views/iot/device/group/index.vue similarity index 59% rename from src/views/iot/product/index.vue rename to src/views/iot/device/group/index.vue index fa285819efb9eb50b9a72d46b7b5b3f917668d0d..ea2e4bea7f740f5b372f692efeee3cfb422671c7 100644 --- a/src/views/iot/product/index.vue +++ b/src/views/iot/device/group/index.vue @@ -8,22 +8,24 @@ :inline="true" label-width="68px" > - + - - + @@ -33,7 +35,7 @@ type="primary" plain @click="openForm('create')" - v-hasPermi="['iot:product:create']" + v-hasPermi="['iot:device-group:create']" > 新增 @@ -44,17 +46,14 @@ - - - - - + + + + - - - - + + + + diff --git a/src/views/iot/plugin/PluginConfigForm.vue b/src/views/iot/plugin/PluginConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..b4f5102922f58f65ef734ec6615a9dc0363967a0 --- /dev/null +++ b/src/views/iot/plugin/PluginConfigForm.vue @@ -0,0 +1,106 @@ + + diff --git a/src/views/iot/plugin/detail/PluginImportForm.vue b/src/views/iot/plugin/detail/PluginImportForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..2d3b75172ac5e8ca2a5b2d66a4283181f5b2e681 --- /dev/null +++ b/src/views/iot/plugin/detail/PluginImportForm.vue @@ -0,0 +1,99 @@ + + diff --git a/src/views/iot/plugin/detail/index.vue b/src/views/iot/plugin/detail/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..31ed167b3bf9868e9a6a8c518a4fddc725703089 --- /dev/null +++ b/src/views/iot/plugin/detail/index.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/views/iot/plugin/index.vue b/src/views/iot/plugin/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..a5d6545aec05005d6a9ef47fb2e649819c4ee900 --- /dev/null +++ b/src/views/iot/plugin/index.vue @@ -0,0 +1,329 @@ + + + + diff --git a/src/views/iot/product/category/ProductCategoryForm.vue b/src/views/iot/product/category/ProductCategoryForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..ff7219225115d29e48ffa0a9b9209f35e1edb610 --- /dev/null +++ b/src/views/iot/product/category/ProductCategoryForm.vue @@ -0,0 +1,119 @@ + + diff --git a/src/views/iot/product/category/index.vue b/src/views/iot/product/category/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..e78262217cd1d5d3ccde49b03d33545759d19871 --- /dev/null +++ b/src/views/iot/product/category/index.vue @@ -0,0 +1,170 @@ + + + diff --git a/src/views/iot/product/detail/ProductDetailsInfo.vue b/src/views/iot/product/detail/ProductDetailsInfo.vue deleted file mode 100644 index 5153045e70c831e6d8566e79f451169fd5c73ec2..0000000000000000000000000000000000000000 --- a/src/views/iot/product/detail/ProductDetailsInfo.vue +++ /dev/null @@ -1,44 +0,0 @@ - - diff --git a/src/views/iot/product/detail/ThinkModelFunctionForm.vue b/src/views/iot/product/detail/ThinkModelFunctionForm.vue deleted file mode 100644 index 895692e00a918ccd7ed8ec49af41b7741e670e7f..0000000000000000000000000000000000000000 --- a/src/views/iot/product/detail/ThinkModelFunctionForm.vue +++ /dev/null @@ -1,229 +0,0 @@ - - - diff --git a/src/views/iot/product/ProductForm.vue b/src/views/iot/product/product/ProductForm.vue similarity index 58% rename from src/views/iot/product/ProductForm.vue rename to src/views/iot/product/product/ProductForm.vue index 75d6efcc07c5cffefd781cc20e6c1d55a1cbafdf..83706fc6c8161e53e5a8d257acf8d4b3ffb81a75 100644 --- a/src/views/iot/product/ProductForm.vue +++ b/src/views/iot/product/product/ProductForm.vue @@ -4,30 +4,48 @@ ref="formRef" :model="formData" :rules="formRules" - label-width="100px" + label-width="110px" v-loading="formLoading" > + + + + + - - - + + - + + + + {{ dict.label }} + + + @@ -44,8 +62,11 @@ /> - - + - - - + - + :label="dict.value" + > + {{ dict.label }} + + - - - - - + + + + + + + + + + + + + - diff --git a/src/views/iot/product/detail/ProductDetailsHeader.vue b/src/views/iot/product/product/detail/ProductDetailsHeader.vue similarity index 80% rename from src/views/iot/product/detail/ProductDetailsHeader.vue rename to src/views/iot/product/product/detail/ProductDetailsHeader.vue index ba009516a22b03d1d28b37d1f91f6a10551b34fb..841aef9f0117816d8a4cda24ac8ce35a828b3b0a 100644 --- a/src/views/iot/product/detail/ProductDetailsHeader.vue +++ b/src/views/iot/product/product/detail/ProductDetailsHeader.vue @@ -45,8 +45,8 @@ - {{ product.deviceCount }} - 前往管理 + {{ product.deviceCount ?? '加载中...' }} + 前往管理 @@ -54,32 +54,37 @@ diff --git a/src/views/iot/product/detail/ProductTopic.vue b/src/views/iot/product/product/detail/ProductTopic.vue similarity index 92% rename from src/views/iot/product/detail/ProductTopic.vue rename to src/views/iot/product/product/detail/ProductTopic.vue index c327bb65de153c9f8665f69cd4809ccdbc541b0b..a691a614a124e4284510250d1ba7a3d06ea9099d 100644 --- a/src/views/iot/product/detail/ProductTopic.vue +++ b/src/views/iot/product/product/detail/ProductTopic.vue @@ -3,9 +3,9 @@
diff --git a/src/views/iot/product/product/index.vue b/src/views/iot/product/product/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..ea7c94ae7dc43755e1d2295ffc9c0beed5a542b1 --- /dev/null +++ b/src/views/iot/product/product/index.vue @@ -0,0 +1,355 @@ + + + diff --git a/src/views/iot/rule/databridge/IoTDataBridgeForm.vue b/src/views/iot/rule/databridge/IoTDataBridgeForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..4e69c026fcdbd7a08873a9cbf52cf31b8510cef0 --- /dev/null +++ b/src/views/iot/rule/databridge/IoTDataBridgeForm.vue @@ -0,0 +1,207 @@ + + diff --git a/src/views/iot/rule/databridge/config/HttpConfigForm.vue b/src/views/iot/rule/databridge/config/HttpConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..0dabd97f103ac57f836edcd66c6ce169b3842d18 --- /dev/null +++ b/src/views/iot/rule/databridge/config/HttpConfigForm.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/views/iot/rule/databridge/config/KafkaMQConfigForm.vue b/src/views/iot/rule/databridge/config/KafkaMQConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..91dfe382f0000e743a557c51f8002e47fec5818a --- /dev/null +++ b/src/views/iot/rule/databridge/config/KafkaMQConfigForm.vue @@ -0,0 +1,45 @@ + + diff --git a/src/views/iot/rule/databridge/config/MqttConfigForm.vue b/src/views/iot/rule/databridge/config/MqttConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..2aa1701101940fe8534e4fd2f56763fe4f00a0f8 --- /dev/null +++ b/src/views/iot/rule/databridge/config/MqttConfigForm.vue @@ -0,0 +1,45 @@ + + diff --git a/src/views/iot/rule/databridge/config/RabbitMQConfigForm.vue b/src/views/iot/rule/databridge/config/RabbitMQConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..b7ee5bb49ecb160440df16ef59ff55bc68c5ff92 --- /dev/null +++ b/src/views/iot/rule/databridge/config/RabbitMQConfigForm.vue @@ -0,0 +1,63 @@ + + diff --git a/src/views/iot/rule/databridge/config/RedisStreamMQConfigForm.vue b/src/views/iot/rule/databridge/config/RedisStreamMQConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..415ceb90f0f1f10cd64af41d3380cfd37646631b --- /dev/null +++ b/src/views/iot/rule/databridge/config/RedisStreamMQConfigForm.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/views/iot/rule/databridge/config/RocketMQConfigForm.vue b/src/views/iot/rule/databridge/config/RocketMQConfigForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..c1f4c102718ef6a859f9d69caab53aa396f3e721 --- /dev/null +++ b/src/views/iot/rule/databridge/config/RocketMQConfigForm.vue @@ -0,0 +1,57 @@ + + diff --git a/src/views/iot/rule/databridge/config/components/KeyValueEditor.vue b/src/views/iot/rule/databridge/config/components/KeyValueEditor.vue new file mode 100644 index 0000000000000000000000000000000000000000..a2e5430648e5d048e20442b7a35198b671de9bc5 --- /dev/null +++ b/src/views/iot/rule/databridge/config/components/KeyValueEditor.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/views/iot/rule/databridge/config/index.ts b/src/views/iot/rule/databridge/config/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..77f3315a4dc17ebd7267aa148c2940ea7a7388da --- /dev/null +++ b/src/views/iot/rule/databridge/config/index.ts @@ -0,0 +1,15 @@ +import HttpConfigForm from './HttpConfigForm.vue' +import MqttConfigForm from './MqttConfigForm.vue' +import RocketMQConfigForm from './RocketMQConfigForm.vue' +import KafkaMQConfigForm from './KafkaMQConfigForm.vue' +import RabbitMQConfigForm from './RabbitMQConfigForm.vue' +import RedisStreamMQConfigForm from './RedisStreamMQConfigForm.vue' + +export { + HttpConfigForm, + MqttConfigForm, + RocketMQConfigForm, + KafkaMQConfigForm, + RabbitMQConfigForm, + RedisStreamMQConfigForm +} diff --git a/src/views/iot/device/index.vue b/src/views/iot/rule/databridge/index.vue similarity index 49% rename from src/views/iot/device/index.vue rename to src/views/iot/rule/databridge/index.vue index 784b14828168eb48baa9a9dac3a6ba585316fbab..2bb7720dd92213d3afb7b3d37dc87421163e49d0 100644 --- a/src/views/iot/device/index.vue +++ b/src/views/iot/rule/databridge/index.vue @@ -2,91 +2,93 @@ - - - - - - + - - - - + - + + + + + + + + + - + 搜索 - + 重置 - + 新增 @@ -95,58 +97,47 @@ - - + + + + + - - + - + - - - - + - diff --git a/src/views/iot/thingmodel/ThingModelEvent.vue b/src/views/iot/thingmodel/ThingModelEvent.vue new file mode 100644 index 0000000000000000000000000000000000000000..592eb86c6e7b2b8ec4eeeb752540820e8ce0d994 --- /dev/null +++ b/src/views/iot/thingmodel/ThingModelEvent.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/views/iot/thingmodel/ThingModelForm.vue b/src/views/iot/thingmodel/ThingModelForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..f800257d96f2dbda91fac8d97da6449e4af369b8 --- /dev/null +++ b/src/views/iot/thingmodel/ThingModelForm.vue @@ -0,0 +1,215 @@ + + + + diff --git a/src/views/iot/thingmodel/ThingModelInputOutputParam.vue b/src/views/iot/thingmodel/ThingModelInputOutputParam.vue new file mode 100644 index 0000000000000000000000000000000000000000..2bf4bb932bc4e5b22856e1dd0ad670d1434d1bbf --- /dev/null +++ b/src/views/iot/thingmodel/ThingModelInputOutputParam.vue @@ -0,0 +1,155 @@ + + + + + + diff --git a/src/views/iot/thingmodel/ThingModelProperty.vue b/src/views/iot/thingmodel/ThingModelProperty.vue new file mode 100644 index 0000000000000000000000000000000000000000..74a6b2efaf36ae590ecdf6d81ca4839e566b9a07 --- /dev/null +++ b/src/views/iot/thingmodel/ThingModelProperty.vue @@ -0,0 +1,169 @@ + + + + + + diff --git a/src/views/iot/thingmodel/ThingModelService.vue b/src/views/iot/thingmodel/ThingModelService.vue new file mode 100644 index 0000000000000000000000000000000000000000..de5009fd64a14e43090f88937b6ce2ca8cb083af --- /dev/null +++ b/src/views/iot/thingmodel/ThingModelService.vue @@ -0,0 +1,59 @@ + + + + + + diff --git a/src/views/iot/thingmodel/components/DataDefinition.vue b/src/views/iot/thingmodel/components/DataDefinition.vue new file mode 100644 index 0000000000000000000000000000000000000000..2b94a578396bbc0815f0c65e0cd32774301a376e --- /dev/null +++ b/src/views/iot/thingmodel/components/DataDefinition.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/src/views/iot/thingmodel/components/index.ts b/src/views/iot/thingmodel/components/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..66c692bacdf09d1ae5f3bc203416c64ca5141851 --- /dev/null +++ b/src/views/iot/thingmodel/components/index.ts @@ -0,0 +1,3 @@ +import DataDefinition from './DataDefinition.vue' + +export { DataDefinition } diff --git a/src/views/iot/thingmodel/config.ts b/src/views/iot/thingmodel/config.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c9699a192c8aadac71b16d5367d980d94842763 --- /dev/null +++ b/src/views/iot/thingmodel/config.ts @@ -0,0 +1,213 @@ +import { isEmpty } from '@/utils/is' + +/** dataSpecs 数值型数据结构 */ +export interface DataSpecsNumberDataVO { + dataType: 'int' | 'float' | 'double' // 数据类型,取值为 INT、FLOAT 或 DOUBLE + max: string // 最大值,必须与 dataType 设置一致,且为 STRING 类型 + min: string // 最小值,必须与 dataType 设置一致,且为 STRING 类型 + step: string // 步长,必须与 dataType 设置一致,且为 STRING 类型 + precise?: string // 精度,当 dataType 为 FLOAT 或 DOUBLE 时可选 + defaultValue?: string // 默认值,可选 + unit: string // 单位的符号 + unitName: string // 单位的名称 +} + +/** dataSpecs 枚举型数据结构 */ +export interface DataSpecsEnumOrBoolDataVO { + dataType: 'enum' | 'bool' + defaultValue?: string // 默认值,可选 + name: string // 枚举项的名称 + value: number | undefined // 枚举值 +} + +/** 属性值的数据类型 */ +export const DataSpecsDataType = { + INT: 'int', + FLOAT: 'float', + DOUBLE: 'double', + ENUM: 'enum', + BOOL: 'bool', + TEXT: 'text', + DATE: 'date', + STRUCT: 'struct', + ARRAY: 'array' +} as const + +/** 物体模型数据类型配置项 */ +export const dataTypeOptions = [ + { value: DataSpecsDataType.INT, label: '整数型' }, + { value: DataSpecsDataType.FLOAT, label: '单精度浮点型' }, + { value: DataSpecsDataType.DOUBLE, label: '双精度浮点型' }, + { value: DataSpecsDataType.ENUM, label: '枚举型' }, + { value: DataSpecsDataType.BOOL, label: '布尔型' }, + { value: DataSpecsDataType.TEXT, label: '文本型' }, + { value: DataSpecsDataType.DATE, label: '时间型' }, + { value: DataSpecsDataType.STRUCT, label: '结构体' }, + { value: DataSpecsDataType.ARRAY, label: '数组' } +] + +/** 获得物体模型数据类型配置项名称 */ +export const getDataTypeOptionsLabel = (value: string) => { + if (isEmpty(value)) { + return value + } + const dataType = dataTypeOptions.find((option) => option.value === value) + return dataType && `${dataType.value}(${dataType.label})` +} + +// IOT 产品物模型类型枚举类 +export const ThingModelType = { + PROPERTY: 1, // 属性 + SERVICE: 2, // 服务 + EVENT: 3 // 事件 +} as const + +// IOT 产品物模型访问模式枚举类 +export const ThingModelAccessMode = { + READ_WRITE: { + label: '读写', + value: 'rw' + }, + READ_ONLY: { + label: '只读', + value: 'r' + } +} as const + +// IOT 产品物模型服务调用方式枚举 +export const ThingModelServiceCallType = { + ASYNC: { + label: '异步调用', + value: 'async' + }, + SYNC: { + label: '同步调用', + value: 'sync' + } +} as const +export const getCallTypeByValue = (value: string): string | undefined => + Object.values(ThingModelServiceCallType).find((type) => type.value === value)?.label + +// IOT 产品物模型事件类型枚举 +export const ThingModelEventType = { + INFO: { + label: '信息', + value: 'info' + }, + ALERT: { + label: '告警', + value: 'alert' + }, + ERROR: { + label: '故障', + value: 'error' + } +} as const +export const getEventTypeByValue = (value: string): string | undefined => + Object.values(ThingModelEventType).find((type) => type.value === value)?.label + +// IOT 产品物模型参数是输入参数还是输出参数 +export const ThingModelParamDirection = { + INPUT: 'input', // 输入参数 + OUTPUT: 'output' // 输出参数 +} as const + +/** 公共校验规则 */ +export const ThingModelFormRules = { + name: [ + { required: true, message: '功能名称不能为空', trigger: 'blur' }, + { + pattern: /^[\u4e00-\u9fa5a-zA-Z0-9][\u4e00-\u9fa5a-zA-Z0-9\-_/\.]{0,29}$/, + message: + '支持中文、大小写字母、日文、数字、短划线、下划线、斜杠和小数点,必须以中文、英文或数字开头,不超过 30 个字符', + trigger: 'blur' + } + ], + type: [{ required: true, message: '功能类型不能为空', trigger: 'blur' }], + identifier: [ + { required: true, message: '标识符不能为空', trigger: 'blur' }, + { + pattern: /^[a-zA-Z0-9_]{1,50}$/, + message: '支持大小写字母、数字和下划线,不超过 50 个字符', + trigger: 'blur' + }, + { + validator: (_: any, value: string, callback: any) => { + const reservedKeywords = ['set', 'get', 'post', 'property', 'event', 'time', 'value'] + if (reservedKeywords.includes(value)) { + callback( + new Error( + 'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义' + ) + ) + } else if (/^\d+$/.test(value)) { + callback(new Error('标识符不能是纯数字')) + } else { + callback() + } + }, + trigger: 'blur' + } + ], + 'property.dataSpecs.childDataType': [{ required: true, message: '元素类型不能为空' }], + 'property.dataSpecs.size': [ + { required: true, message: '元素个数不能为空' }, + { + validator: (_: any, value: any, callback: any) => { + if (isEmpty(value)) { + callback(new Error('元素个数不能为空')) + return + } + if (isNaN(Number(value))) { + callback(new Error('元素个数必须是数字')) + return + } + callback() + }, + trigger: 'blur' + } + ], + 'property.dataSpecs.length': [ + { required: true, message: '请输入文本字节长度', trigger: 'blur' }, + { + validator: (_: any, value: any, callback: any) => { + if (isEmpty(value)) { + callback(new Error('文本长度不能为空')) + return + } + if (isNaN(Number(value))) { + callback(new Error('文本长度必须是数字')) + return + } + callback() + }, + trigger: 'blur' + } + ], + 'property.accessMode': [{ required: true, message: '请选择读写类型', trigger: 'change' }] +} + +/** 校验布尔值名称 */ +export const validateBoolName = (_: any, value: string, callback: any) => { + if (isEmpty(value)) { + callback(new Error('布尔值名称不能为空')) + return + } + // 检查开头字符 + if (!/^[\u4e00-\u9fa5a-zA-Z0-9]/.test(value)) { + callback(new Error('布尔值名称必须以中文、英文字母或数字开头')) + return + } + // 检查整体格式 + if (!/^[\u4e00-\u9fa5a-zA-Z0-9][a-zA-Z0-9\u4e00-\u9fa5_-]*$/.test(value)) { + callback(new Error('布尔值名称只能包含中文、英文字母、数字、下划线和短划线')) + return + } + // 检查长度(一个中文算一个字符) + if (value.length > 20) { + callback(new Error('布尔值名称长度不能超过 20 个字符')) + return + } + + callback() +} diff --git a/src/views/iot/thingmodel/dataSpecs/ThingModelArrayDataSpecs.vue b/src/views/iot/thingmodel/dataSpecs/ThingModelArrayDataSpecs.vue new file mode 100644 index 0000000000000000000000000000000000000000..04a975a1c633a593f5733e853ee88a1d3bb6939b --- /dev/null +++ b/src/views/iot/thingmodel/dataSpecs/ThingModelArrayDataSpecs.vue @@ -0,0 +1,52 @@ + + + + + + diff --git a/src/views/iot/thingmodel/dataSpecs/ThingModelEnumDataSpecs.vue b/src/views/iot/thingmodel/dataSpecs/ThingModelEnumDataSpecs.vue new file mode 100644 index 0000000000000000000000000000000000000000..fb323ca6ac8681d80992c48d98111afe4beb6aa0 --- /dev/null +++ b/src/views/iot/thingmodel/dataSpecs/ThingModelEnumDataSpecs.vue @@ -0,0 +1,159 @@ + + + + + + diff --git a/src/views/iot/thingmodel/dataSpecs/ThingModelNumberDataSpecs.vue b/src/views/iot/thingmodel/dataSpecs/ThingModelNumberDataSpecs.vue new file mode 100644 index 0000000000000000000000000000000000000000..66c9531351210546ed9205f70e386c15f2baf3e1 --- /dev/null +++ b/src/views/iot/thingmodel/dataSpecs/ThingModelNumberDataSpecs.vue @@ -0,0 +1,139 @@ + + + + + + diff --git a/src/views/iot/thingmodel/dataSpecs/ThingModelStructDataSpecs.vue b/src/views/iot/thingmodel/dataSpecs/ThingModelStructDataSpecs.vue new file mode 100644 index 0000000000000000000000000000000000000000..fb2258c1679aa2e5d0b2f0532903f053a512e20c --- /dev/null +++ b/src/views/iot/thingmodel/dataSpecs/ThingModelStructDataSpecs.vue @@ -0,0 +1,170 @@ + + + + + + diff --git a/src/views/iot/thingmodel/dataSpecs/index.ts b/src/views/iot/thingmodel/dataSpecs/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..30151aea62ba1d0fc2d98f370418fada1d504fd7 --- /dev/null +++ b/src/views/iot/thingmodel/dataSpecs/index.ts @@ -0,0 +1,11 @@ +import ThingModelEnumDataSpecs from './ThingModelEnumDataSpecs.vue' +import ThingModelNumberDataSpecs from './ThingModelNumberDataSpecs.vue' +import ThingModelArrayDataSpecs from './ThingModelArrayDataSpecs.vue' +import ThingModelStructDataSpecs from './ThingModelStructDataSpecs.vue' + +export { + ThingModelEnumDataSpecs, + ThingModelNumberDataSpecs, + ThingModelArrayDataSpecs, + ThingModelStructDataSpecs +} diff --git a/src/views/iot/product/detail/ThinkModelFunction.vue b/src/views/iot/thingmodel/index.vue similarity index 55% rename from src/views/iot/product/detail/ThinkModelFunction.vue rename to src/views/iot/thingmodel/index.vue index 5a75d109fb8cc3f49a5c020a3276355bbf2b9a59..9ddd449afe3a6e22162608cb395b70681c74fa8b 100644 --- a/src/views/iot/product/detail/ThinkModelFunction.vue +++ b/src/views/iot/thingmodel/index.vue @@ -1,22 +1,23 @@ +