fix: iot && IconifyIcon

This commit is contained in:
xingyu4j
2025-10-13 10:41:08 +08:00
parent 4b69721a90
commit a4561b3dd8
22 changed files with 218 additions and 188 deletions

View File

@@ -3,6 +3,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { IoTOtaFirmwareApi } from '#/api/iot/ota/firmware'; import type { IoTOtaFirmwareApi } from '#/api/iot/ota/firmware';
import { Page, useVbenModal } from '@vben/common-ui'; import { Page, useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
@@ -119,7 +120,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
download download
class="text-primary cursor-pointer hover:underline" class="text-primary cursor-pointer hover:underline"
> >
<Icon icon="ant-design:download-outlined" class="mr-1" /> <IconifyIcon icon="ant-design:download-outlined" class="mr-1" />
下载固件 下载固件
</a> </a>
<span v-else class="text-gray-400">无文件</span> <span v-else class="text-gray-400">无文件</span>

View File

@@ -5,10 +5,19 @@ import type { OtaTask } from '#/api/iot/ota/task';
import { onMounted, reactive, ref } from 'vue'; import { onMounted, reactive, ref } from 'vue';
import { VbenButton } from '@vben/common-ui'; import { IconifyIcon } from '@vben/icons';
import { formatDate } from '@vben/utils'; import { formatDate } from '@vben/utils';
import { Card, Input, message, Modal, Space, Table, Tag } from 'ant-design-vue'; import {
Button,
Card,
Input,
message,
Modal,
Space,
Table,
Tag,
} from 'ant-design-vue';
import * as IoTOtaTaskApi from '#/api/iot/ota/task'; import * as IoTOtaTaskApi from '#/api/iot/ota/task';
import { IoTOtaTaskStatusEnum } from '#/views/iot/utils/constants'; import { IoTOtaTaskStatusEnum } from '#/views/iot/utils/constants';
@@ -167,10 +176,10 @@ onMounted(() => {
<Card title="升级任务管理" class="mb-5"> <Card title="升级任务管理" class="mb-5">
<!-- 搜索栏 --> <!-- 搜索栏 -->
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
<VbenButton type="primary" @click="openTaskForm"> <Button type="primary" @click="openTaskForm">
<Icon icon="ant-design:plus-outlined" class="mr-1" /> <IconifyIcon icon="ant-design:plus-outlined" class="mr-1" />
新增 新增
</VbenButton> </Button>
<Input <Input
v-model:value="queryParams.name" v-model:value="queryParams.name"
placeholder="请输入任务名称" placeholder="请输入任务名称"

View File

@@ -87,7 +87,7 @@ function updateCondition(index: number, condition: TriggerCondition) {
<!-- 空状态 --> <!-- 空状态 -->
<div v-if="!subGroup || subGroup.length === 0" class="py-24px text-center"> <div v-if="!subGroup || subGroup.length === 0" class="py-24px text-center">
<div class="gap-12px flex flex-col items-center"> <div class="gap-12px flex flex-col items-center">
<Icon <IconifyIcon
icon="ep:plus" icon="ep:plus"
class="text-32px text-[var(--el-text-color-placeholder)]" class="text-32px text-[var(--el-text-color-placeholder)]"
/> />
@@ -96,7 +96,7 @@ function updateCondition(index: number, condition: TriggerCondition) {
<p class="text-12px">点击下方按钮添加第一个条件</p> <p class="text-12px">点击下方按钮添加第一个条件</p>
</div> </div>
<Button type="primary" @click="addCondition"> <Button type="primary" @click="addCondition">
<Icon icon="ep:plus" /> <IconifyIcon icon="ep:plus" />
添加条件 添加条件
</Button> </Button>
</div> </div>

View File

@@ -1,9 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { VxeGridInstance } from '#/adapter/vxe-table'; import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { ThingModelApi } from '#/api/iot/thingmodel';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { message } from 'ant-design-vue'; import { Button, message } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteThingModel, getThingModelPage } from '#/api/iot/thingmodel'; import { deleteThingModel, getThingModelPage } from '#/api/iot/thingmodel';
@@ -20,6 +22,8 @@ const props = defineProps<{
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions: { gridOptions: {
columns: useGridColumns(), columns: useGridColumns(),
height: 'auto',
keepSource: true,
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page, form }) => { query: async ({ page, form }) => {
@@ -32,26 +36,30 @@ const [Grid, gridApi] = useVbenVxeGrid({
}, },
}, },
}, },
}, rowConfig: {
keyField: 'id',
isHover: true,
},
toolbarConfig: {
refresh: true,
search: true,
},
} as VxeTableGridOptions<ThingModelApi.ThingModel>,
formOptions: { formOptions: {
schema: useGridFormSchema(), schema: useGridFormSchema(),
}, },
}); });
const xGrid = shallowRef<VxeGridInstance>();
const formRef = ref();
const tslRef = ref();
// 新增功能 // 新增功能
const handleCreate = () => { const handleCreate = () => {
// TODO: 打开物模型表单 // TODO: 打开物模型表单
console.log('新增功能'); console.error('新增功能');
}; };
// 编辑功能 // 编辑功能
const handleEdit = (row: any) => { const handleEdit = (row: any) => {
// TODO: 打开物模型表单 // TODO: 打开物模型表单
console.log('编辑功能:', row); console.error('编辑功能:', row);
}; };
// 删除功能 // 删除功能
@@ -68,7 +76,7 @@ const handleDelete = async (row: any) => {
// 打开 TSL // 打开 TSL
const handleOpenTSL = () => { const handleOpenTSL = () => {
// TODO: 打开 TSL 弹窗 // TODO: 打开 TSL 弹窗
console.log('打开 TSL'); console.error('打开 TSL');
}; };
// 获取数据类型标签 // 获取数据类型标签
@@ -82,13 +90,13 @@ const getDataTypeLabel = (row: any) => {
description="管理产品的物模型定义,包括属性、服务和事件" description="管理产品的物模型定义,包括属性、服务和事件"
title="物模型管理" title="物模型管理"
> >
<Grid ref="xGrid"> <Grid>
<template #toolbar-tools> <template #toolbar-tools>
<VbenButton @click="handleCreate"> <Button @click="handleCreate">
<Icon icon="ant-design:plus-outlined" class="mr-1" /> <IconifyIcon icon="ant-design:plus-outlined" class="mr-1" />
添加功能 添加功能
</VbenButton> </Button>
<VbenButton type="success" @click="handleOpenTSL"> TSL </VbenButton> <Button type="primary" @click="handleOpenTSL"> TSL </Button>
</template> </template>
<!-- 数据类型列 --> <!-- 数据类型列 -->
@@ -99,17 +107,15 @@ const getDataTypeLabel = (row: any) => {
<!-- 数据定义列 --> <!-- 数据定义列 -->
<template #dataDefinition="{ row }"> <template #dataDefinition="{ row }">
<!-- TODO: 实现数据定义组件 --> <!-- TODO: 实现数据定义组件 -->
<span class="text-gray-400">-</span> <span class="text-gray-400">{{ row }}</span>
</template> </template>
<!-- 操作列 --> <!-- 操作列 -->
<template #actions="{ row }"> <template #actions="{ row }">
<VbenButton size="small" type="primary" @click="handleEdit(row)"> <Button size="small" type="primary" @click="handleEdit(row)">
编辑 编辑
</VbenButton> </Button>
<VbenButton size="small" type="danger" @click="handleDelete(row)"> <Button size="small" danger @click="handleDelete(row)"> 删除 </Button>
删除
</VbenButton>
</template> </template>
</Grid> </Grid>
</Page> </Page>

View File

@@ -2,13 +2,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { ThingModelEvent } from '#/api/iot/thingmodel';
import { watch } from 'vue'; import { watch } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Form, Radio } from 'ant-design-vue';
import { ThingModelEvent } from '#/api/iot/thingmodel';
import { import {
IoTThingModelEventTypeEnum, IoTThingModelEventTypeEnum,
IoTThingModelParamDirectionEnum, IoTThingModelParamDirectionEnum,
@@ -30,7 +32,7 @@ const thingModelEvent = useVModel(
// 默认选中INFO 信息 // 默认选中INFO 信息
watch( watch(
() => thingModelEvent.value.type, () => thingModelEvent.value.type,
(val: string) => (val: string | undefined) =>
isEmpty(val) && isEmpty(val) &&
(thingModelEvent.value.type = IoTThingModelEventTypeEnum.INFO.value), (thingModelEvent.value.type = IoTThingModelEventTypeEnum.INFO.value),
{ immediate: true }, { immediate: true },
@@ -38,32 +40,32 @@ watch(
</script> </script>
<template> <template>
<el-form-item <Form.Item
:rules="[{ required: true, message: '请选择事件类型', trigger: 'change' }]" :rules="[{ required: true, message: '请选择事件类型', trigger: 'change' }]"
label="事件类型" label="事件类型"
prop="event.type" prop="event.type"
> >
<el-radio-group v-model="thingModelEvent.type"> <Radio.Group v-model="thingModelEvent.type">
<el-radio <Radio
v-for="eventType in Object.values(IoTThingModelEventTypeEnum)" v-for="eventType in Object.values(IoTThingModelEventTypeEnum)"
:key="eventType.value" :key="eventType.value"
:value="eventType.value" :value="eventType.value"
> >
{{ eventType.label }} {{ eventType.label }}
</el-radio> </Radio>
</el-radio-group> </Radio.Group>
</el-form-item> </Form.Item>
<el-form-item label="输出参数"> <Form.Item label="输出参数">
<ThingModelInputOutputParam <ThingModelInputOutputParam
v-model="thingModelEvent.outputParams" v-model="thingModelEvent.outputData"
:direction="IoTThingModelParamDirectionEnum.OUTPUT" :direction="IoTThingModelParamDirectionEnum.OUTPUT"
/> />
</el-form-item> </Form.Item>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-form-item) { :deep(.ant-form-item) {
.el-form-item { .ant-form-item {
margin-bottom: 0; margin-bottom: 0;
} }
} }

View File

@@ -12,9 +12,13 @@ import { getDictOptions } from '@vben/hooks';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { cloneDeep } from '@vben/utils'; import { cloneDeep } from '@vben/utils';
import { message } from 'ant-design-vue'; import { Button, Form, Input, message, Radio } from 'ant-design-vue';
import { createThingModel, updateThingModel } from '#/api/iot/thingmodel'; import {
createThingModel,
getThingModel,
updateThingModel,
} from '#/api/iot/thingmodel';
import { import {
IOT_PROVIDE_KEY, IOT_PROVIDE_KEY,
IoTDataSpecsDataTypeEnum, IoTDataSpecsDataTypeEnum,
@@ -37,7 +41,7 @@ const dialogTitle = ref(''); // 弹窗的标题
const formLoading = ref(false); // 表单的加载中1修改时的数据加载2提交的按钮禁用 const formLoading = ref(false); // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref(''); // 表单的类型create - 新增update - 修改 const formType = ref(''); // 表单的类型create - 新增update - 修改
const formData = ref<ThingModelData>({ const formData = ref<ThingModelData>({
type: IoTThingModelTypeEnum.PROPERTY, type: IoTThingModelTypeEnum.PROPERTY.toString(),
dataType: IoTDataSpecsDataTypeEnum.INT, dataType: IoTDataSpecsDataTypeEnum.INT,
property: { property: {
dataType: IoTDataSpecsDataTypeEnum.INT, dataType: IoTDataSpecsDataTypeEnum.INT,
@@ -47,7 +51,7 @@ const formData = ref<ThingModelData>({
}, },
service: {}, service: {},
event: {}, event: {},
} as ThingModelData); });
const formRef = ref(); // 表单 Ref const formRef = ref(); // 表单 Ref
@@ -176,68 +180,64 @@ function resetForm() {
</script> </script>
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle"> <Modal v-model="dialogVisible" :title="dialogTitle">
<a-form <Form
ref="formRef" ref="formRef"
:loading="formLoading" :loading="formLoading"
:model="formData" :model="formData"
:rules="ThingModelFormRules"
:label-col="{ span: 6 }" :label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }" :wrapper-col="{ span: 18 }"
> >
<a-form-item label="功能类型" name="type"> <Form.Item label="功能类型" name="type">
<a-radio-group v-model:value="formData.type"> <Radio.Group v-model:value="formData.type">
<a-radio-button <Radio.Button
v-for="dict in getDictOptions( v-for="(dict, index) in getDictOptions(
DICT_TYPE.IOT_THING_MODEL_TYPE, DICT_TYPE.IOT_THING_MODEL_TYPE,
'number', 'number',
)" )"
:key="dict.value" :key="index"
:value="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</a-radio-button> </Radio.Button>
</a-radio-group> </Radio.Group>
</a-form-item> </Form.Item>
<a-form-item label="功能名称" name="name"> <Form.Item label="功能名称" name="name">
<a-input v-model:value="formData.name" placeholder="请输入功能名称" /> <Input v-model:value="formData.name" placeholder="请输入功能名称" />
</a-form-item> </Form.Item>
<a-form-item label="标识符" name="identifier"> <Form.Item label="标识符" name="identifier">
<a-input <Input v-model:value="formData.identifier" placeholder="请输入标识符" />
v-model:value="formData.identifier" </Form.Item>
placeholder="请输入标识符"
/>
</a-form-item>
<!-- 属性配置 --> <!-- 属性配置 -->
<ThingModelProperty <ThingModelProperty
v-if="formData.type === IoTThingModelTypeEnum.PROPERTY" v-if="formData.type === IoTThingModelTypeEnum.PROPERTY.toString()"
v-model="formData.property" v-model="formData.property"
/> />
<!-- 服务配置 --> <!-- 服务配置 -->
<ThingModelService <ThingModelService
v-if="formData.type === IoTThingModelTypeEnum.SERVICE" v-if="formData.type === IoTThingModelTypeEnum.SERVICE.toString()"
v-model="formData.service" v-model="formData.service"
/> />
<!-- 事件配置 --> <!-- 事件配置 -->
<ThingModelEvent <ThingModelEvent
v-if="formData.type === IoTThingModelTypeEnum.EVENT" v-if="formData.type === IoTThingModelTypeEnum.EVENT.toString()"
v-model="formData.event" v-model="formData.event"
/> />
<a-form-item label="描述" name="description"> <Form.Item label="描述" name="desc">
<a-textarea <Input.TextArea
v-model:value="formData.description" v-model:value="formData.desc"
:maxlength="200" :maxlength="200"
:rows="3" :rows="3"
placeholder="请输入属性描述" placeholder="请输入属性描述"
/> />
</a-form-item> </Form.Item>
</a-form> </Form>
<template #footer> <template #footer>
<a-button :disabled="formLoading" type="primary" @click="submitForm"> <Button :disabled="formLoading" type="primary" @click="submitForm">
确 定 确 定
</a-button> </Button>
<a-button @click="dialogVisible = false"> </a-button> <Button @click="dialogVisible = false"> </Button>
</template> </template>
</Dialog> </Modal>
</template> </template>

View File

@@ -1,10 +1,14 @@
<!-- 产品的物模型表单eventservice 项里的参数 --> <!-- 产品的物模型表单eventservice 项里的参数 -->
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue';
import { ref, unref } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Button, Divider, Form, Input, Modal } from 'ant-design-vue';
import { ThingModelFormRules } from '#/api/iot/thingmodel';
import { IoTDataSpecsDataTypeEnum } from '#/views/iot/utils/constants'; import { IoTDataSpecsDataTypeEnum } from '#/views/iot/utils/constants';
import ThingModelProperty from './ThingModelProperty.vue'; import ThingModelProperty from './ThingModelProperty.vue';
@@ -29,7 +33,7 @@ const formData = ref<any>({
}); });
/** 打开 param 表单 */ /** 打开 param 表单 */
const openParamForm = (val: any) => { function openParamForm(val: any) {
dialogVisible.value = true; dialogVisible.value = true;
resetForm(); resetForm();
if (isEmpty(val)) { if (isEmpty(val)) {
@@ -46,15 +50,15 @@ const openParamForm = (val: any) => {
dataSpecsList: val.dataSpecsList, dataSpecsList: val.dataSpecsList,
}, },
}; };
}; }
/** 删除 param 项 */ /** 删除 param 项 */
const deleteParamItem = (index: number) => { function deleteParamItem(index: number) {
thingModelParams.value.splice(index, 1); thingModelParams.value.splice(index, 1);
}; }
/** 添加参数 */ /** 添加参数 */
const submitForm = async () => { async function submitForm() {
// 初始化参数列表 // 初始化参数列表
if (isEmpty(thingModelParams.value)) { if (isEmpty(thingModelParams.value)) {
thingModelParams.value = []; thingModelParams.value = [];
@@ -93,10 +97,10 @@ const submitForm = async () => {
} finally { } finally {
dialogVisible.value = false; dialogVisible.value = false;
} }
}; }
/** 重置表单 */ /** 重置表单 */
const resetForm = () => { function resetForm() {
formData.value = { formData.value = {
dataType: IoTDataSpecsDataTypeEnum.INT, dataType: IoTDataSpecsDataTypeEnum.INT,
property: { property: {
@@ -107,59 +111,46 @@ const resetForm = () => {
}, },
}; };
paramFormRef.value?.resetFields(); paramFormRef.value?.resetFields();
}; }
</script> </script>
<template> <template>
<div <div
v-for="(item, index) in thingModelParams" v-for="(item, index) in thingModelParams"
:key="index" :key="index"
class="w-1/1 param-item px-10px mb-10px flex justify-between" class="w-1/1 px-10px mb-10px flex justify-between bg-gray-100"
> >
<span>参数名称{{ item.name }}</span> <span>参数名称{{ item.name }}</span>
<div class="btn"> <div class="btn">
<el-button link type="primary" @click="openParamForm(item)"> <Button link type="primary" @click="openParamForm(item)"> 编辑 </Button>
编辑 <Divider direction="vertical" />
</el-button> <Button link danger @click="deleteParamItem(index)"> 删除 </Button>
<el-divider direction="vertical" />
<el-button link type="danger" @click="deleteParamItem(index)">
删除
</el-button>
</div> </div>
</div> </div>
<el-button link type="primary" @click="openParamForm(null)"> <Button link type="primary" @click="openParamForm(null)"> +新增参数 </Button>
+新增参数
</el-button>
<!-- param 表单 --> <!-- param 表单 -->
<Dialog v-model="dialogVisible" title="新增参数" append-to-body> <Modal v-model="dialogVisible" title="新增参数" append-to-body>
<el-form <Form
ref="paramFormRef" ref="paramFormRef"
v-loading="formLoading" v-loading="formLoading"
:model="formData" :model="formData"
:rules="ThingModelFormRules"
label-width="100px" label-width="100px"
> >
<el-form-item label="参数名称" prop="name"> <Form.Item label="参数名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入功能名称" /> <Input v-model="formData.name" placeholder="请输入功能名称" />
</el-form-item> </Form.Item>
<el-form-item label="标识符" prop="identifier"> <Form.Item label="标识符" prop="identifier">
<el-input v-model="formData.identifier" placeholder="请输入标识符" /> <Input v-model="formData.identifier" placeholder="请输入标识符" />
</el-form-item> </Form.Item>
<!-- 属性配置 --> <!-- 属性配置 -->
<ThingModelProperty v-model="formData.property" is-params /> <ThingModelProperty v-model="formData.property" is-params />
</el-form> </Form>
<template #footer> <template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> <Button :disabled="formLoading" type="primary" @click="submitForm">
</el-button> </Button>
<el-button @click="dialogVisible = false"> </el-button> <Button @click="dialogVisible = false"> </Button>
</template> </template>
</Dialog> </Modal>
</template> </template>
<style lang="scss" scoped>
.param-item {
background-color: #e4f2fd;
}
</style>

View File

@@ -1,10 +1,17 @@
<!-- 产品的物模型表单property --> <!-- 产品的物模型表单property -->
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue';
import type { ThingModelProperty } from '#/api/iot/thingmodel';
import { computed, watch } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Form, Input, Radio, Select } from 'ant-design-vue';
import { ThingModelProperty, validateBoolName } from '#/api/iot/thingmodel'; import { validateBoolName } from '#/api/iot/thingmodel';
import { import {
getDataTypeOptions, getDataTypeOptions,
IoTDataSpecsDataTypeEnum, IoTDataSpecsDataTypeEnum,
@@ -46,7 +53,7 @@ const getDataTypeOptions2 = computed(() => {
}); // 获得数据类型列表 }); // 获得数据类型列表
/** 属性值的数据类型切换时初始化相关数据 */ /** 属性值的数据类型切换时初始化相关数据 */
const handleChange = (dataType: any) => { function handleChange(dataType: any) {
property.value.dataSpecs = {}; property.value.dataSpecs = {};
property.value.dataSpecsList = []; property.value.dataSpecsList = [];
// 不是列表型数据才设置 dataSpecs.dataType // 不是列表型数据才设置 dataSpecs.dataType
@@ -75,12 +82,12 @@ const handleChange = (dataType: any) => {
break; break;
} }
} }
}; }
/** 默认选中读写 */ /** 默认选中读写 */
watch( watch(
() => property.value.accessMode, () => property.value.accessMode,
(val: string) => { (val: string | undefined) => {
if (props.isStructDataSpecs || props.isParams) { if (props.isStructDataSpecs || props.isParams) {
return; return;
} }
@@ -93,25 +100,25 @@ watch(
</script> </script>
<template> <template>
<el-form-item <Form.Item
:rules="[{ required: true, message: '请选择数据类型', trigger: 'change' }]" :rules="[{ required: true, message: '请选择数据类型', trigger: 'change' }]"
label="数据类型" label="数据类型"
prop="property.dataType" prop="property.dataType"
> >
<el-select <Select
v-model="property.dataType" v-model="property.dataType"
placeholder="请选择数据类型" placeholder="请选择数据类型"
@change="handleChange" @change="handleChange"
> >
<!-- ARRAY STRUCT 类型数据相互嵌套时最多支持递归嵌套 2 父和子 --> <!-- ARRAY STRUCT 类型数据相互嵌套时最多支持递归嵌套 2 父和子 -->
<el-option <Select.Option
v-for="option in getDataTypeOptions2" v-for="option in getDataTypeOptions2"
:key="option.value" :key="option.value"
:label="`${option.value}(${option.label})`" :label="`${option.value}(${option.label})`"
:value="option.value" :value="option.value"
/> />
</el-select> </Select>
</el-form-item> </Form.Item>
<!-- 数值型配置 --> <!-- 数值型配置 -->
<ThingModelNumberDataSpecs <ThingModelNumberDataSpecs
v-if=" v-if="
@@ -129,7 +136,7 @@ watch(
v-model="property.dataSpecsList" v-model="property.dataSpecsList"
/> />
<!-- 布尔型配置 --> <!-- 布尔型配置 -->
<el-form-item <Form.Item
v-if="property.dataType === IoTDataSpecsDataTypeEnum.BOOL" v-if="property.dataType === IoTDataSpecsDataTypeEnum.BOOL"
label="布尔值" label="布尔值"
> >
@@ -137,7 +144,7 @@ watch(
<div class="w-1/1 mb-5px flex items-center justify-start"> <div class="w-1/1 mb-5px flex items-center justify-start">
<span>{{ item.value }}</span> <span>{{ item.value }}</span>
<span class="mx-2">-</span> <span class="mx-2">-</span>
<el-form-item <Form.Item
:prop="`property.dataSpecsList[${index}].name`" :prop="`property.dataSpecsList[${index}].name`"
:rules="[ :rules="[
{ required: true, message: '枚举描述不能为空' }, { required: true, message: '枚举描述不能为空' },
@@ -145,41 +152,41 @@ watch(
]" ]"
class="mb-0 flex-1" class="mb-0 flex-1"
> >
<el-input <Input
v-model="item.name" v-model="item.name"
:placeholder="`如:${item.value === 0 ? '关' : '开'}`" :placeholder="`如:${item.value === 0 ? '关' : '开'}`"
class="w-255px!" class="w-255px!"
/> />
</el-form-item> </Form.Item>
</div> </div>
</template> </template>
</el-form-item> </Form.Item>
<!-- 文本型配置 --> <!-- 文本型配置 -->
<el-form-item <Form.Item
v-if="property.dataType === IoTDataSpecsDataTypeEnum.TEXT" v-if="property.dataType === IoTDataSpecsDataTypeEnum.TEXT"
label="数据长度" label="数据长度"
prop="property.dataSpecs.length" prop="property.dataSpecs.length"
> >
<el-input <Input
v-model="property.dataSpecs.length" v-model="property.dataSpecs.length"
class="w-255px!" class="w-255px!"
placeholder="请输入文本字节长度" placeholder="请输入文本字节长度"
> >
<template #append>字节</template> <template #append>字节</template>
</el-input> </Input>
</el-form-item> </Form.Item>
<!-- 时间型配置 --> <!-- 时间型配置 -->
<el-form-item <Form.Item
v-if="property.dataType === IoTDataSpecsDataTypeEnum.DATE" v-if="property.dataType === IoTDataSpecsDataTypeEnum.DATE"
label="时间格式" label="时间格式"
prop="date" prop="date"
> >
<el-input <Input
class="w-255px!" class="w-255px!"
disabled disabled
placeholder="String 类型的 UTC 时间戳(毫秒)" placeholder="String 类型的 UTC 时间戳(毫秒)"
/> />
</el-form-item> </Form.Item>
<!-- 数组型配置--> <!-- 数组型配置-->
<ThingModelArrayDataSpecs <ThingModelArrayDataSpecs
v-if="property.dataType === IoTDataSpecsDataTypeEnum.ARRAY" v-if="property.dataType === IoTDataSpecsDataTypeEnum.ARRAY"
@@ -190,26 +197,26 @@ watch(
v-if="property.dataType === IoTDataSpecsDataTypeEnum.STRUCT" v-if="property.dataType === IoTDataSpecsDataTypeEnum.STRUCT"
v-model="property.dataSpecsList" v-model="property.dataSpecsList"
/> />
<el-form-item <Form.Item
v-if="!isStructDataSpecs && !isParams" v-if="!isStructDataSpecs && !isParams"
label="读写类型" label="读写类型"
prop="property.accessMode" prop="property.accessMode"
> >
<el-radio-group v-model="property.accessMode"> <Radio.Group v-model="property.accessMode">
<el-radio <Radio
v-for="accessMode in Object.values(IoTThingModelAccessModeEnum)" v-for="accessMode in Object.values(IoTThingModelAccessModeEnum)"
:key="accessMode.value" :key="accessMode.value"
:label="accessMode.value" :label="accessMode.value"
> >
{{ accessMode.label }} {{ accessMode.label }}
</el-radio> </Radio>
</el-radio-group> </Radio.Group>
</el-form-item> </Form.Item>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-form-item) { :deep(.ant-form-item) {
.el-form-item { .ant-form-item {
margin-bottom: 0; margin-bottom: 0;
} }
} }

View File

@@ -1,10 +1,16 @@
<!-- 产品的物模型表单service --> <!-- 产品的物模型表单service -->
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from 'vue';
import type { ThingModelService } from '#/api/iot/thingmodel';
import { watch } from 'vue';
import { isEmpty } from '@vben/utils'; import { isEmpty } from '@vben/utils';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { Form, Radio } from 'ant-design-vue';
import { ThingModelService } from '#/api/iot/thingmodel';
import { import {
IoTThingModelParamDirectionEnum, IoTThingModelParamDirectionEnum,
IoTThingModelServiceCallTypeEnum, IoTThingModelServiceCallTypeEnum,
@@ -22,7 +28,7 @@ const service = useVModel(props, 'modelValue', emits) as Ref<ThingModelService>;
/** 默认选中ASYNC 异步 */ /** 默认选中ASYNC 异步 */
watch( watch(
() => service.value.callType, () => service.value.callType,
(val: string) => (val: string | undefined) =>
isEmpty(val) && isEmpty(val) &&
(service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value), (service.value.callType = IoTThingModelServiceCallTypeEnum.ASYNC.value),
{ immediate: true }, { immediate: true },
@@ -30,38 +36,38 @@ watch(
</script> </script>
<template> <template>
<el-form-item <Form.Item
:rules="[{ required: true, message: '请选择调用方式', trigger: 'change' }]" :rules="[{ required: true, message: '请选择调用方式', trigger: 'change' }]"
label="调用方式" label="调用方式"
prop="service.callType" prop="service.callType"
> >
<el-radio-group v-model="service.callType"> <Radio.Group v-model="service.callType">
<el-radio <Radio
v-for="callType in Object.values(IoTThingModelServiceCallTypeEnum)" v-for="callType in Object.values(IoTThingModelServiceCallTypeEnum)"
:key="callType.value" :key="callType.value"
:value="callType.value" :value="callType.value"
> >
{{ callType.label }} {{ callType.label }}
</el-radio> </Radio>
</el-radio-group> </Radio.Group>
</el-form-item> </Form.Item>
<el-form-item label="输入参数"> <Form.Item label="输入参数">
<ThingModelInputOutputParam <ThingModelInputOutputParam
v-model="service.inputParams" v-model="service.inputData"
:direction="IoTThingModelParamDirectionEnum.INPUT" :direction="IoTThingModelParamDirectionEnum.INPUT"
/> />
</el-form-item> </Form.Item>
<el-form-item label="输出参数"> <Form.Item label="输出参数">
<ThingModelInputOutputParam <ThingModelInputOutputParam
v-model="service.outputParams" v-model="service.outputData"
:direction="IoTThingModelParamDirectionEnum.OUTPUT" :direction="IoTThingModelParamDirectionEnum.OUTPUT"
/> />
</el-form-item> </Form.Item>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
:deep(.el-form-item) { :deep(.ant-form-item) {
.el-form-item { .ant-form-item {
margin-bottom: 0; margin-bottom: 0;
} }
} }

View File

@@ -1,10 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { ProductVO } from '#/api/iot/product/product'; import type { IotProductApi } from '#/api/iot/product/product';
import { inject, onMounted, ref } from 'vue'; import { inject, onMounted, ref } from 'vue';
import { Modal, Radio } from 'ant-design-vue';
import hljs from 'highlight.js'; // 导入代码高亮文件 import hljs from 'highlight.js'; // 导入代码高亮文件
import json from 'highlight.js/lib/languages/json'; import json from 'highlight.js/lib/languages/json';
@@ -17,7 +18,7 @@ defineOptions({ name: 'ThingModelTSL' });
const dialogVisible = ref(false); // 弹窗的是否展示 const dialogVisible = ref(false); // 弹窗的是否展示
const dialogTitle = ref('物模型 TSL'); // 弹窗的标题 const dialogTitle = ref('物模型 TSL'); // 弹窗的标题
const product = inject<Ref<ProductVO>>(IOT_PROVIDE_KEY.PRODUCT); // 注入产品信息 const product = inject<Ref<IotProductApi.Product>>(IOT_PROVIDE_KEY.PRODUCT); // 注入产品信息
const viewMode = ref('code'); // 查看模式code-代码视图editor-编辑器视图 const viewMode = ref('code'); // 查看模式code-代码视图editor-编辑器视图
/** 打开弹窗 */ /** 打开弹窗 */
@@ -43,17 +44,17 @@ onMounted(async () => {
</script> </script>
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle"> <Modal v-model="dialogVisible" :title="dialogTitle">
<JsonEditor <JsonEditor
v-model="thingModelTSL" v-model="thingModelTSL"
:mode="viewMode === 'editor' ? 'code' : 'view'" :mode="viewMode === 'editor' ? 'code' : 'view'"
height="600px" height="600px"
/> />
<template #footer> <template #footer>
<el-radio-group v-model="viewMode" size="small"> <Radio.Group v-model="viewMode" size="small">
<el-radio-button label="code">代码视图</el-radio-button> <Radio.Button label="code">代码视图</Radio.Button>
<el-radio-button label="editor">编辑器视图</el-radio-button> <Radio.Button label="editor">编辑器视图</Radio.Button>
</el-radio-group> </Radio.Group>
</template> </template>
</Dialog> </Modal>
</template> </template>

View File

@@ -155,7 +155,6 @@ onMounted(async () => {
ref="structFormRef" ref="structFormRef"
v-loading="formLoading" v-loading="formLoading"
:model="formData" :model="formData"
:rules="ThingModelFormRules"
label-width="100px" label-width="100px"
> >
<Form.Item label="参数名称" prop="name"> <Form.Item label="参数名称" prop="name">

View File

@@ -68,7 +68,7 @@ onMounted(async () => {
</ElTooltip> </ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom"> <!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <IconifyIcon icon="fluent:text-column-three-24-filled" />
</ElRadioButton> </ElRadioButton>
</ElTooltip>--> </ElTooltip>-->
</ElRadioGroup> </ElRadioGroup>

View File

@@ -53,7 +53,7 @@ const formData = useVModel(props, 'modelValue', emit);
</ElTooltip> </ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom"> <!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <IconifyIcon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip>--> </el-tooltip>-->
</ElRadioGroup> </ElRadioGroup>

View File

@@ -70,7 +70,7 @@ onMounted(async () => {
</ElTooltip> </ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom"> <!--<el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <IconifyIcon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</ElTooltip>--> </ElTooltip>-->
</ElRadioGroup> </ElRadioGroup>

View File

@@ -218,7 +218,7 @@ const eachCube = (callback: (x: number, y: number, cube: Cube) => void) => {
@click="handleCubeClick(row, col)" @click="handleCubeClick(row, col)"
@mouseenter="handleCellHover(row, col)" @mouseenter="handleCellHover(row, col)"
> >
<Icon icon="ep-plus" /> <IconifyIcon icon="ep-plus" />
</td> </td>
</tr> </tr>
</tbody> </tbody>

View File

@@ -3,6 +3,8 @@ import type { MallCombinationActivityApi } from '#/api/mall/promotion/combinatio
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons';
import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity'; import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity';
import CombinationTableSelect from '#/views/mall/promotion/combination/components/combination-table-select.vue'; import CombinationTableSelect from '#/views/mall/promotion/combination/components/combination-table-select.vue';
@@ -125,7 +127,7 @@ const emitActivityChange = () => {
<el-tooltip :content="combinationActivity.name"> <el-tooltip :content="combinationActivity.name">
<div class="relative h-full w-full"> <div class="relative h-full w-full">
<el-image :src="combinationActivity.picUrl" class="h-full w-full" /> <el-image :src="combinationActivity.picUrl" class="h-full w-full" />
<Icon <IconifyIcon
v-show="!disabled" v-show="!disabled"
class="del-icon" class="del-icon"
icon="ep:circle-close-filled" icon="ep:circle-close-filled"
@@ -136,7 +138,7 @@ const emitActivityChange = () => {
</div> </div>
<el-tooltip content="选择活动" v-if="canAdd"> <el-tooltip content="选择活动" v-if="canAdd">
<div class="select-box" @click="openCombinationActivityTableSelect"> <div class="select-box" @click="openCombinationActivityTableSelect">
<Icon icon="ep:plus" /> <IconifyIcon icon="ep:plus" />
</div> </div>
</el-tooltip> </el-tooltip>
</div> </div>

View File

@@ -6,6 +6,7 @@ import { onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui'; import { ContentWrap } 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 { import {
dateFormatter, dateFormatter,
fenToYuan, fenToYuan,
@@ -281,11 +282,11 @@ onMounted(async () => {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <IconifyIcon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" /> <IconifyIcon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@@ -6,6 +6,7 @@ import { reactive, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui'; import { ContentWrap } from '@vben/common-ui';
import { CouponTemplateTakeTypeEnum, DICT_TYPE } from '@vben/constants'; import { CouponTemplateTakeTypeEnum, DICT_TYPE } from '@vben/constants';
import { getDictOptions } from '@vben/hooks'; import { getDictOptions } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate'; import * as CouponTemplateApi from '#/api/mall/promotion/coupon/couponTemplate';
import { import {
@@ -130,11 +131,11 @@ const submitForm = () => {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <IconifyIcon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" /> <IconifyIcon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@@ -6,6 +6,7 @@ import { computed, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui'; import { ContentWrap } 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 { dateFormatter, fenToYuanFormat } from '@vben/utils'; import { dateFormatter, fenToYuanFormat } from '@vben/utils';
import { CHANGE_EVENT } from 'element-plus'; import { CHANGE_EVENT } from 'element-plus';
@@ -236,11 +237,11 @@ const calculateIsCheckAll = () => {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <IconifyIcon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" /> <IconifyIcon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@@ -6,6 +6,7 @@ import { onMounted, ref } from 'vue';
import { ContentWrap } from '@vben/common-ui'; import { ContentWrap } 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 { import {
dateFormatter, dateFormatter,
fenToYuan, fenToYuan,
@@ -274,11 +275,11 @@ onMounted(async () => {
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <IconifyIcon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery"> <el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" /> <IconifyIcon class="mr-5px" icon="ep:refresh" />
重置 重置
</el-button> </el-button>
</el-form-item> </el-form-item>

View File

@@ -8,6 +8,7 @@ import type { AnalysisOverviewIconItem } from '#/views/mall/home/components/data
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { confirm } from '@vben/common-ui'; import { confirm } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { import {
calculateRelativeRate, calculateRelativeRate,
@@ -312,7 +313,7 @@ const loadOverview = () => {
:loading="exportLoading" :loading="exportLoading"
v-access:code="['statistics:product:export']" v-access:code="['statistics:product:export']"
> >
<Icon icon="ep:download" class="mr-1" />导出 <IconifyIcon icon="ep:download" class="mr-1" />导出
</el-button> </el-button>
</ShortcutDateRangePicker> </ShortcutDateRangePicker>
</template> </template>

View File

@@ -8,6 +8,7 @@ import type { AnalysisOverviewIconItem } from '#/views/mall/home/components/data
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { confirm } from '@vben/common-ui'; import { confirm } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts'; import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { import {
calculateRelativeRate, calculateRelativeRate,
@@ -270,7 +271,7 @@ const lineChartOptions = reactive({
:loading="exportLoading" :loading="exportLoading"
v-access:code="['statistics:trade:export']" v-access:code="['statistics:trade:export']"
> >
<Icon icon="ep:download" class="mr-1" />导出 <IconifyIcon icon="ep:download" class="mr-1" />导出
</el-button> </el-button>
</ShortcutDateRangePicker> </ShortcutDateRangePicker>
</template> </template>