Showing
85 changed files
with
2490 additions
and
1644 deletions
Too many changes to show.
To preserve performance only 85 of 151 files are displayed.
| 1 | import { DeviceProfileModel } from '../../device/model/deviceModel'; | 1 | import { DeviceProfileModel } from '../../device/model/deviceModel'; |
| 2 | -import { HistoryData } from './model'; | 2 | +import { DeviceAttributeItemType, HistoryData } from './model'; |
| 3 | import { defHttp } from '/@/utils/http/axios'; | 3 | import { defHttp } from '/@/utils/http/axios'; |
| 4 | import { isString } from '/@/utils/is'; | 4 | import { isString } from '/@/utils/is'; |
| 5 | import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; | 5 | import { OrderByEnum } from '/@/views/device/localtion/cpns/TimePeriodForm/config'; |
| @@ -17,7 +17,7 @@ export const getDeviceHistoryInfo = (params: Recordable, orderBy = OrderByEnum.D | @@ -17,7 +17,7 @@ export const getDeviceHistoryInfo = (params: Recordable, orderBy = OrderByEnum.D | ||
| 17 | return defHttp.get<HistoryData>( | 17 | return defHttp.get<HistoryData>( |
| 18 | { | 18 | { |
| 19 | url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, | 19 | url: `/plugins/telemetry/DEVICE/${params.entityId}/values/timeseries`, |
| 20 | - params: { orderBy, ...params, entityId: null }, | 20 | + params: { ...params, entityId: null, orderBy }, |
| 21 | }, | 21 | }, |
| 22 | { | 22 | { |
| 23 | joinPrefix: false, | 23 | joinPrefix: false, |
| @@ -38,7 +38,7 @@ export const getDeviceDataKeys = (id: string) => { | @@ -38,7 +38,7 @@ export const getDeviceDataKeys = (id: string) => { | ||
| 38 | }; | 38 | }; |
| 39 | // 获取设备状态,在线 or 离线时间 | 39 | // 获取设备状态,在线 or 离线时间 |
| 40 | export const getDeviceActiveTime = (entityId: string) => { | 40 | export const getDeviceActiveTime = (entityId: string) => { |
| 41 | - return defHttp.get( | 41 | + return defHttp.get<DeviceAttributeItemType[]>( |
| 42 | { | 42 | { |
| 43 | url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`, | 43 | url: `/plugins/telemetry/DEVICE/${entityId}/values/attributes?keys=active`, |
| 44 | }, | 44 | }, |
| @@ -13,7 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel'; | @@ -13,7 +13,7 @@ import { ChildDeviceParams } from './model/deviceModel'; | ||
| 13 | import { PaginationResult } from '/#/axios'; | 13 | import { PaginationResult } from '/#/axios'; |
| 14 | import { AlarmLogItem } from './model/deviceConfigModel'; | 14 | import { AlarmLogItem } from './model/deviceConfigModel'; |
| 15 | import { omit } from 'lodash-es'; | 15 | import { omit } from 'lodash-es'; |
| 16 | -import { CommandDeliveryWayEnum } from '/@/enums/toolEnum'; | 16 | +import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum'; |
| 17 | enum DeviceManagerApi { | 17 | enum DeviceManagerApi { |
| 18 | /** | 18 | /** |
| 19 | * 设备URL | 19 | * 设备URL |
| @@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => { | @@ -137,7 +137,7 @@ export const createOrEditDevice = (data) => { | ||
| 137 | 137 | ||
| 138 | // 查询设备详情 | 138 | // 查询设备详情 |
| 139 | export const getDeviceDetail = (id: string) => { | 139 | export const getDeviceDetail = (id: string) => { |
| 140 | - return defHttp.get({ | 140 | + return defHttp.get<DeviceRecord>({ |
| 141 | url: DeviceManagerApi.DEVICE_URL + `/${id}`, | 141 | url: DeviceManagerApi.DEVICE_URL + `/${id}`, |
| 142 | }); | 142 | }); |
| 143 | }; | 143 | }; |
| 1 | import { BasicPageParams } from '/@/api/model/baseModel'; | 1 | import { BasicPageParams } from '/@/api/model/baseModel'; |
| 2 | +import { CommandTypeEnum, RPCCommandMethodEnum } from '/@/enums/deviceEnum'; | ||
| 2 | 3 | ||
| 3 | export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams; | 4 | export type TDeviceConfigPageQueryParam = BasicPageParams & TDeviceConfigParams; |
| 4 | 5 | ||
| @@ -184,3 +185,10 @@ export interface Configuration { | @@ -184,3 +185,10 @@ export interface Configuration { | ||
| 184 | export interface TransportConfiguration { | 185 | export interface TransportConfiguration { |
| 185 | type: string; | 186 | type: string; |
| 186 | } | 187 | } |
| 188 | + | ||
| 189 | +export interface RpcCommandType { | ||
| 190 | + additionalInfo: { cmdType: CommandTypeEnum }; | ||
| 191 | + method: RPCCommandMethodEnum; | ||
| 192 | + params: Recordable | string | number; | ||
| 193 | + persistent: boolean; | ||
| 194 | +} |
| 1 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 1 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 2 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | 2 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; |
| 3 | 3 | ||
| 4 | export interface Specs { | 4 | export interface Specs { |
| 5 | - min: string; | ||
| 6 | - max: string; | 5 | + min: number; |
| 6 | + max: number; | ||
| 7 | unit: string; | 7 | unit: string; |
| 8 | unitName: string; | 8 | unitName: string; |
| 9 | 9 | ||
| 10 | dataType?: string; | 10 | dataType?: string; |
| 11 | name?: string; | 11 | name?: string; |
| 12 | - value?: string; | ||
| 13 | - step: string; | ||
| 14 | - length: string; | 12 | + value?: string | number; |
| 13 | + step: number; | ||
| 14 | + length: number; | ||
| 15 | boolOpen: string; | 15 | boolOpen: string; |
| 16 | boolClose: string; | 16 | boolClose: string; |
| 17 | valueRange?: { | 17 | valueRange?: { |
| 18 | - min: string; | ||
| 19 | - max: string; | 18 | + min: number; |
| 19 | + max: number; | ||
| 20 | }; | 20 | }; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| @@ -26,17 +26,23 @@ export interface DataType { | @@ -26,17 +26,23 @@ export interface DataType { | ||
| 26 | specsList?: Specs[]; | 26 | specsList?: Specs[]; |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | +export interface ExtensionDesc { | ||
| 30 | + zoomFactor?: number; | ||
| 31 | + actionType?: string; | ||
| 32 | + dataType: string; | ||
| 33 | + registerAddress: number; | ||
| 34 | +} | ||
| 35 | + | ||
| 29 | export interface StructJSON { | 36 | export interface StructJSON { |
| 30 | - functionName?: string; | 37 | + functionName: string; |
| 31 | identifier: string; | 38 | identifier: string; |
| 32 | - remark?: string; | ||
| 33 | dataType?: DataType; | 39 | dataType?: DataType; |
| 40 | + remark?: string; | ||
| 34 | serviceCommand?: string; | 41 | serviceCommand?: string; |
| 35 | - accessMode?: string; | ||
| 36 | } | 42 | } |
| 37 | 43 | ||
| 38 | export interface FunctionJson { | 44 | export interface FunctionJson { |
| 39 | - dataType?: DataType | DataType[]; | 45 | + dataType?: DataType; |
| 40 | inputData?: StructJSON[]; | 46 | inputData?: StructJSON[]; |
| 41 | outputData?: StructJSON[]; | 47 | outputData?: StructJSON[]; |
| 42 | serviceCommand?: string; | 48 | serviceCommand?: string; |
| @@ -46,19 +52,19 @@ export interface ModelOfMatterParams { | @@ -46,19 +52,19 @@ export interface ModelOfMatterParams { | ||
| 46 | deviceProfileId?: string; | 52 | deviceProfileId?: string; |
| 47 | functionJson: FunctionJson; | 53 | functionJson: FunctionJson; |
| 48 | functionName: string; | 54 | functionName: string; |
| 49 | - functionType: FunctionType; | 55 | + functionType: FunctionTypeEnum; |
| 50 | identifier: string; | 56 | identifier: string; |
| 51 | - remark: string; | 57 | + remark?: string; |
| 52 | id?: string; | 58 | id?: string; |
| 53 | categoryId?: string; | 59 | categoryId?: string; |
| 54 | callType?: string; | 60 | callType?: string; |
| 55 | eventType?: string; | 61 | eventType?: string; |
| 56 | accessMode?: string; | 62 | accessMode?: string; |
| 57 | - extensionDesc?: Recordable; | 63 | + extensionDesc?: ExtensionDesc; |
| 58 | } | 64 | } |
| 59 | 65 | ||
| 60 | export interface GetModelTslParams { | 66 | export interface GetModelTslParams { |
| 61 | - functionType: FunctionType; | 67 | + functionType: FunctionTypeEnum; |
| 62 | deviceProfileId: string; | 68 | deviceProfileId: string; |
| 63 | ifShowClass?: string | Boolean; | 69 | ifShowClass?: string | Boolean; |
| 64 | } | 70 | } |
| @@ -85,3 +91,26 @@ export interface ModelOfMatterItemRecordType { | @@ -85,3 +91,26 @@ export interface ModelOfMatterItemRecordType { | ||
| 85 | status: number; | 91 | status: number; |
| 86 | deviceProfileId: string; | 92 | deviceProfileId: string; |
| 87 | } | 93 | } |
| 94 | + | ||
| 95 | +export interface BatchGetObjectModelItemType { | ||
| 96 | + id: string; | ||
| 97 | + name: string; | ||
| 98 | + transportType: string; | ||
| 99 | + deviceType: string; | ||
| 100 | + tsl: Tsl[]; | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +export interface Tsl { | ||
| 104 | + functionName: string; | ||
| 105 | + identifier: string; | ||
| 106 | + functionType: string; | ||
| 107 | + accessMode?: string; | ||
| 108 | + specs?: { | ||
| 109 | + dataType: DataType; | ||
| 110 | + }; | ||
| 111 | + extensionDesc: ExtensionDesc; | ||
| 112 | + eventType?: string; | ||
| 113 | + outputData?: StructJSON[]; | ||
| 114 | + callType?: string; | ||
| 115 | + inputData?: StructJSON[]; | ||
| 116 | +} |
| 1 | import { BasicPageParams } from '../model/baseModel'; | 1 | import { BasicPageParams } from '../model/baseModel'; |
| 2 | import { | 2 | import { |
| 3 | + BatchGetObjectModelItemType, | ||
| 3 | GetModelTslParams, | 4 | GetModelTslParams, |
| 4 | ImportModelOfMatterType, | 5 | ImportModelOfMatterType, |
| 5 | ModelOfMatterItemRecordType, | 6 | ModelOfMatterItemRecordType, |
| 6 | ModelOfMatterParams, | 7 | ModelOfMatterParams, |
| 7 | } from './model/modelOfMatterModel'; | 8 | } from './model/modelOfMatterModel'; |
| 9 | +import { FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 8 | import { defHttp } from '/@/utils/http/axios'; | 10 | import { defHttp } from '/@/utils/http/axios'; |
| 9 | -import { FunctionType } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
| 10 | 11 | ||
| 11 | enum ModelOfMatter { | 12 | enum ModelOfMatter { |
| 12 | CREATE = '/things_model', | 13 | CREATE = '/things_model', |
| @@ -26,12 +27,14 @@ enum ModelOfMatter { | @@ -26,12 +27,14 @@ enum ModelOfMatter { | ||
| 26 | 27 | ||
| 27 | IMPORT_CSV = '/things_model/csvImport', | 28 | IMPORT_CSV = '/things_model/csvImport', |
| 28 | EXCEL_EXPORT = '/things_model/downloadTemplate', | 29 | EXCEL_EXPORT = '/things_model/downloadTemplate', |
| 30 | + | ||
| 31 | + BATCH_GET_TSL_BY_DEVICE_PROFILES = '/things_model/batch/get_tsl', | ||
| 29 | } | 32 | } |
| 30 | 33 | ||
| 31 | export const getModelList = ( | 34 | export const getModelList = ( |
| 32 | params: BasicPageParams & { | 35 | params: BasicPageParams & { |
| 33 | deviceProfileId?: string; | 36 | deviceProfileId?: string; |
| 34 | - functionTyp?: FunctionType; | 37 | + functionTyp?: FunctionTypeEnum; |
| 35 | nameOrIdentifier?: string; | 38 | nameOrIdentifier?: string; |
| 36 | selectType?: string | undefined; | 39 | selectType?: string | undefined; |
| 37 | id?: string; | 40 | id?: string; |
| @@ -181,3 +184,16 @@ export const excelExport = () => { | @@ -181,3 +184,16 @@ export const excelExport = () => { | ||
| 181 | responseType: 'blob', | 184 | responseType: 'blob', |
| 182 | }); | 185 | }); |
| 183 | }; | 186 | }; |
| 187 | + | ||
| 188 | +export const batchGetObjectModel = ({ | ||
| 189 | + deviceProfileIds, | ||
| 190 | + functionTypeEnum = 'all', | ||
| 191 | +}: { | ||
| 192 | + deviceProfileIds: string[]; | ||
| 193 | + functionTypeEnum?: FunctionTypeEnum | 'all'; | ||
| 194 | +}) => { | ||
| 195 | + return defHttp.post<BatchGetObjectModelItemType[]>({ | ||
| 196 | + url: ModelOfMatter.BATCH_GET_TSL_BY_DEVICE_PROFILES, | ||
| 197 | + data: { deviceProfileIds, functionTypeEnum }, | ||
| 198 | + }); | ||
| 199 | +}; |
| @@ -59,7 +59,7 @@ export interface GenModbusCommandType { | @@ -59,7 +59,7 @@ export interface GenModbusCommandType { | ||
| 59 | crc: string; | 59 | crc: string; |
| 60 | deviceCode: string; | 60 | deviceCode: string; |
| 61 | method: string; | 61 | method: string; |
| 62 | - registerAddress: string; | 62 | + registerAddress: number; |
| 63 | registerNumber?: number; | 63 | registerNumber?: number; |
| 64 | registerValues?: number[]; | 64 | registerValues?: number[]; |
| 65 | } | 65 | } |
| @@ -2,7 +2,7 @@ import { withInstall } from '/@/utils'; | @@ -2,7 +2,7 @@ import { withInstall } from '/@/utils'; | ||
| 2 | // @ts-ignore | 2 | // @ts-ignore |
| 3 | import codeEditor from './src/CodeEditor.vue'; | 3 | import codeEditor from './src/CodeEditor.vue'; |
| 4 | import jsonPreview from './src/json-preview/JsonPreview.vue'; | 4 | import jsonPreview from './src/json-preview/JsonPreview.vue'; |
| 5 | -export { JSONEditor } from './src/JSONEditor'; | 5 | +export { JSONEditor, JSONEditorValidator, parseStringToJSON } from './src/JSONEditor'; |
| 6 | 6 | ||
| 7 | export const CodeEditor = withInstall(codeEditor); | 7 | export const CodeEditor = withInstall(codeEditor); |
| 8 | export const JsonPreview = withInstall(jsonPreview); | 8 | export const JsonPreview = withInstall(jsonPreview); |
| @@ -11,18 +11,18 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v | @@ -11,18 +11,18 @@ export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.v | ||
| 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
| 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; | 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
| 13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; | 13 | export { default as ApiUpload } from './src/components/ApiUpload.vue'; |
| 14 | +export { default as ApiCascader } from './src/components/ApiCascader.vue'; | ||
| 14 | 15 | ||
| 15 | -export { default as StructForm } from './src/externalCompns/components/StructForm/StructForm.vue'; | ||
| 16 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; | 16 | export { default as JavaScriptFunctionEditor } from './src/components/JavaScriptFunctionEditor.vue'; |
| 17 | 17 | ||
| 18 | -export { ThingsModelForm } from './src/externalCompns/components/ThingsModelForm'; | 18 | +export { ThingsModelForm } from './src/components/ThingsModelForm'; |
| 19 | 19 | ||
| 20 | //注册自定义组件 | 20 | //注册自定义组件 |
| 21 | export { | 21 | export { |
| 22 | JEasyCron, | 22 | JEasyCron, |
| 23 | JEasyCronInner, | 23 | JEasyCronInner, |
| 24 | JEasyCronModal, | 24 | JEasyCronModal, |
| 25 | -} from '/@/components/Form/src/externalCompns/components/JEasyCron'; | 25 | +} from '/@/components/Form/src/components/JEasyCron'; |
| 26 | // Jeecg自定义校验 | 26 | // Jeecg自定义校验 |
| 27 | -export { JCronValidator } from '/@/components/Form/src/externalCompns/components/JEasyCron'; | 27 | +export { JCronValidator } from '/@/components/Form/src/components/JEasyCron'; |
| 28 | // export { BasicForm }; | 28 | // export { BasicForm }; |
| @@ -29,20 +29,18 @@ import { IconPicker } from '/@/components/Icon'; | @@ -29,20 +29,18 @@ import { IconPicker } from '/@/components/Icon'; | ||
| 29 | import { CountdownInput } from '/@/components/CountDown'; | 29 | import { CountdownInput } from '/@/components/CountDown'; |
| 30 | import ApiRadioGroup from './components/ApiRadioGroup.vue'; | 30 | import ApiRadioGroup from './components/ApiRadioGroup.vue'; |
| 31 | //自定义组件 | 31 | //自定义组件 |
| 32 | -import JAddInput from './externalCompns/components/JAddInput.vue'; | ||
| 33 | -import { JEasyCron } from './externalCompns/components/JEasyCron'; | 32 | +import JAddInput from './components/JAddInput.vue'; |
| 33 | +import { JEasyCron } from './components/JEasyCron'; | ||
| 34 | import ColorPicker from './components/ColorPicker.vue'; | 34 | import ColorPicker from './components/ColorPicker.vue'; |
| 35 | import IconDrawer from './components/IconDrawer.vue'; | 35 | import IconDrawer from './components/IconDrawer.vue'; |
| 36 | import ApiUpload from './components/ApiUpload.vue'; | 36 | import ApiUpload from './components/ApiUpload.vue'; |
| 37 | import ApiSearchSelect from './components/ApiSearchSelect.vue'; | 37 | import ApiSearchSelect from './components/ApiSearchSelect.vue'; |
| 38 | -import CustomMinMaxInput from './externalCompns/components/CustomMinMaxInput.vue'; | ||
| 39 | -import StructForm from './externalCompns/components/StructForm/StructForm.vue'; | 38 | +import CustomMinMaxInput from './components/CustomMinMaxInput.vue'; |
| 40 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; | 39 | import ApiSelectScrollLoad from './components/ApiSelectScrollLoad.vue'; |
| 41 | import InputGroup from './components/InputGroup.vue'; | 40 | import InputGroup from './components/InputGroup.vue'; |
| 42 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; | 41 | import RegisterAddressInput from '/@/views/task/center/components/PollCommandInput/RegisterAddressInput.vue'; |
| 43 | -import ExtendDesc from '/@/components/Form/src/externalCompns/components/ExtendDesc/index.vue'; | ||
| 44 | -import DeviceProfileForm from '/@/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue'; | ||
| 45 | -import EnumList from './externalCompns/components/StructForm/EnumList.vue'; | 42 | +import DeviceProfileForm from '/@/components/Form/src/components/DeviceProfileForm/index.vue'; |
| 43 | +import { Segmented } from './components/Segmented'; | ||
| 46 | 44 | ||
| 47 | const componentMap = new Map<ComponentType, Component>(); | 45 | const componentMap = new Map<ComponentType, Component>(); |
| 48 | 46 | ||
| @@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); | @@ -76,6 +74,7 @@ componentMap.set('TimePicker', TimePicker); | ||
| 76 | componentMap.set('StrengthMeter', StrengthMeter); | 74 | componentMap.set('StrengthMeter', StrengthMeter); |
| 77 | componentMap.set('IconPicker', IconPicker); | 75 | componentMap.set('IconPicker', IconPicker); |
| 78 | componentMap.set('InputCountDown', CountdownInput); | 76 | componentMap.set('InputCountDown', CountdownInput); |
| 77 | +componentMap.set('Segmented', Segmented); | ||
| 79 | 78 | ||
| 80 | componentMap.set('Upload', BasicUpload); | 79 | componentMap.set('Upload', BasicUpload); |
| 81 | //注册自定义组件 | 80 | //注册自定义组件 |
| @@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); | @@ -86,12 +85,9 @@ componentMap.set('IconDrawer', IconDrawer); | ||
| 86 | componentMap.set('ApiUpload', ApiUpload); | 85 | componentMap.set('ApiUpload', ApiUpload); |
| 87 | componentMap.set('ApiSearchSelect', ApiSearchSelect); | 86 | componentMap.set('ApiSearchSelect', ApiSearchSelect); |
| 88 | componentMap.set('CustomMinMaxInput', CustomMinMaxInput); | 87 | componentMap.set('CustomMinMaxInput', CustomMinMaxInput); |
| 89 | -componentMap.set('StructForm', StructForm); | ||
| 90 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); | 88 | componentMap.set('ApiSelectScrollLoad', ApiSelectScrollLoad); |
| 91 | componentMap.set('InputGroup', InputGroup); | 89 | componentMap.set('InputGroup', InputGroup); |
| 92 | componentMap.set('RegisterAddressInput', RegisterAddressInput); | 90 | componentMap.set('RegisterAddressInput', RegisterAddressInput); |
| 93 | -componentMap.set('ExtendDesc', ExtendDesc); | ||
| 94 | -componentMap.set('EnumList', EnumList); | ||
| 95 | componentMap.set('DeviceProfileForm', DeviceProfileForm); | 91 | componentMap.set('DeviceProfileForm', DeviceProfileForm); |
| 96 | 92 | ||
| 97 | export function add(compName: ComponentType, component: Component) { | 93 | export function add(compName: ComponentType, component: Component) { |
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { Cascader } from 'ant-design-vue'; | ||
| 3 | + import { LoadingOutlined } from '@ant-design/icons-vue'; | ||
| 4 | + import { CascaderOptionType } from 'ant-design-vue/lib/cascader'; | ||
| 5 | + import { get } from 'lodash'; | ||
| 6 | + import { ref, unref, watch, watchEffect } from 'vue'; | ||
| 7 | + import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | ||
| 8 | + import { useI18n } from '/@/hooks/web/useI18n'; | ||
| 9 | + import { isFunction } from '/@/utils/is'; | ||
| 10 | + | ||
| 11 | + const { t } = useI18n(); | ||
| 12 | + | ||
| 13 | + const props = withDefaults( | ||
| 14 | + defineProps<{ | ||
| 15 | + value?: (string | number)[]; | ||
| 16 | + immediate?: boolean; | ||
| 17 | + params?: any; | ||
| 18 | + api?: Fn<any, Promise<any>>; | ||
| 19 | + options?: CascaderOptionType[]; | ||
| 20 | + resultField?: string; | ||
| 21 | + fieldNames?: Record<'label' | 'value' | 'children', string>; | ||
| 22 | + }>(), | ||
| 23 | + { | ||
| 24 | + value: () => [], | ||
| 25 | + immediate: true, | ||
| 26 | + fieldNames: () => ({ label: 'label', value: 'value', children: 'children' }), | ||
| 27 | + } | ||
| 28 | + ); | ||
| 29 | + | ||
| 30 | + const emit = defineEmits(['change', 'options-change', 'update:value']); | ||
| 31 | + | ||
| 32 | + const options = ref<CascaderOptionType[]>([]); | ||
| 33 | + const loading = ref(false); | ||
| 34 | + const isFirstLoad = ref(true); | ||
| 35 | + const emitData = ref<any[]>([]); | ||
| 36 | + | ||
| 37 | + // Embedded in the form, just use the hook binding to perform form verification | ||
| 38 | + const [state] = useRuleFormItem(props, 'value', 'update:value', emitData); | ||
| 39 | + | ||
| 40 | + watchEffect(() => { | ||
| 41 | + props.immediate && fetch(); | ||
| 42 | + }); | ||
| 43 | + | ||
| 44 | + watch( | ||
| 45 | + () => props.params, | ||
| 46 | + () => { | ||
| 47 | + !unref(isFirstLoad) && fetch(); | ||
| 48 | + }, | ||
| 49 | + { deep: true } | ||
| 50 | + ); | ||
| 51 | + | ||
| 52 | + async function fetch() { | ||
| 53 | + const api = props.api; | ||
| 54 | + if (!api || !isFunction(api)) return; | ||
| 55 | + options.value = []; | ||
| 56 | + try { | ||
| 57 | + loading.value = true; | ||
| 58 | + const res = await api(props.params); | ||
| 59 | + if (Array.isArray(res)) { | ||
| 60 | + options.value = res; | ||
| 61 | + emitChange(); | ||
| 62 | + return; | ||
| 63 | + } | ||
| 64 | + if (props.resultField) { | ||
| 65 | + options.value = get(res, props.resultField) || []; | ||
| 66 | + } | ||
| 67 | + emitChange(); | ||
| 68 | + } catch (error) { | ||
| 69 | + console.warn(error); | ||
| 70 | + } finally { | ||
| 71 | + loading.value = false; | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + async function handleFetch(open) { | ||
| 76 | + if (open) { | ||
| 77 | + await fetch(); | ||
| 78 | + } | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + function emitChange() { | ||
| 82 | + emit('options-change', options); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + function handleChange(_, ...args) { | ||
| 86 | + emitData.value = args; | ||
| 87 | + } | ||
| 88 | +</script> | ||
| 89 | + | ||
| 90 | +<template> | ||
| 91 | + <Cascader | ||
| 92 | + v-bind="$attrs" | ||
| 93 | + @change="handleChange" | ||
| 94 | + :fieldNames="fieldNames" | ||
| 95 | + :options="options" | ||
| 96 | + v-model:value="state" | ||
| 97 | + @popupVisibleChange="handleFetch" | ||
| 98 | + > | ||
| 99 | + <template #notFoundContent v-if="loading"> | ||
| 100 | + <span> | ||
| 101 | + <LoadingOutlined spin class="mr-1" /> | ||
| 102 | + {{ t('component.form.apiSelectNotFound') }} | ||
| 103 | + </span> | ||
| 104 | + </template> | ||
| 105 | + </Cascader> | ||
| 106 | +</template> |
src/components/Form/src/components/CustomMinMaxInput.vue
renamed from
src/components/Form/src/externalCompns/components/CustomMinMaxInput.vue
| @@ -4,17 +4,13 @@ | @@ -4,17 +4,13 @@ | ||
| 4 | placeholder="最小值" | 4 | placeholder="最小值" |
| 5 | :disabled="$props.disabled" | 5 | :disabled="$props.disabled" |
| 6 | :value="getValue.min!" | 6 | :value="getValue.min!" |
| 7 | - style="width: 38%" | ||
| 8 | @change="(value) => emitChange(value, 'min')" | 7 | @change="(value) => emitChange(value, 'min')" |
| 9 | /> | 8 | /> |
| 10 | - <span style="width: 8px"></span> | ||
| 11 | - <span>~</span> | ||
| 12 | - <span style="width: 8px"></span> | 9 | + <div class="text-center flex-shrink-0 w-6">~</div> |
| 13 | <InputNumber | 10 | <InputNumber |
| 14 | placeholder="最大值" | 11 | placeholder="最大值" |
| 15 | :disabled="$props.disabled" | 12 | :disabled="$props.disabled" |
| 16 | :value="getValue.max!" | 13 | :value="getValue.max!" |
| 17 | - style="width: 38%" | ||
| 18 | @change="(value) => emitChange(value, 'max')" | 14 | @change="(value) => emitChange(value, 'max')" |
| 19 | /> | 15 | /> |
| 20 | </div> | 16 | </div> |
src/components/Form/src/components/DeviceProfileForm/index.vue
renamed from
src/components/Form/src/externalCompns/components/DeviceProfileForm/index.vue
src/components/Form/src/components/JAddInput.vue
renamed from
src/components/Form/src/externalCompns/components/JAddInput.vue
src/components/Form/src/components/JEasyCron/EasyCronInner.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInner.vue
src/components/Form/src/components/JEasyCron/EasyCronInput.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronInput.vue
src/components/Form/src/components/JEasyCron/EasyCronModal.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/EasyCronModal.vue
src/components/Form/src/components/JEasyCron/LICENSE
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/LICENSE
src/components/Form/src/components/JEasyCron/easy.cron.data.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.data.ts
src/components/Form/src/components/JEasyCron/easy.cron.inner.less
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.inner.less
src/components/Form/src/components/JEasyCron/easy.cron.input.less
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/easy.cron.input.less
src/components/Form/src/components/JEasyCron/index.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/index.ts
src/components/Form/src/components/JEasyCron/tabs/DayUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/DayUI.vue
src/components/Form/src/components/JEasyCron/tabs/HourUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/HourUI.vue
src/components/Form/src/components/JEasyCron/tabs/MinuteUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/MinuteUI.vue
src/components/Form/src/components/JEasyCron/tabs/MonthUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/MonthUI.vue
src/components/Form/src/components/JEasyCron/tabs/SecondUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/SecondUI.vue
src/components/Form/src/components/JEasyCron/tabs/WeekUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/WeekUI.vue
src/components/Form/src/components/JEasyCron/tabs/YearUI.vue
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/YearUI.vue
src/components/Form/src/components/JEasyCron/tabs/useTabMixin.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/tabs/useTabMixin.ts
src/components/Form/src/components/JEasyCron/validator.ts
renamed from
src/components/Form/src/externalCompns/components/JEasyCron/validator.ts
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { | ||
| 3 | + computed, | ||
| 4 | + CSSProperties, | ||
| 5 | + nextTick, | ||
| 6 | + onBeforeUnmount, | ||
| 7 | + ref, | ||
| 8 | + TransitionProps, | ||
| 9 | + unref, | ||
| 10 | + watch, | ||
| 11 | + } from 'vue'; | ||
| 12 | + import { addClass } from './styleClass'; | ||
| 13 | + import { ThumbReact } from './type'; | ||
| 14 | + import { removeClass } from '/@/utils/domUtils'; | ||
| 15 | + | ||
| 16 | + const calcThumbStyle = (targetElement: HTMLElement | null | undefined): ThumbReact => | ||
| 17 | + targetElement | ||
| 18 | + ? { | ||
| 19 | + left: targetElement.offsetLeft, | ||
| 20 | + right: | ||
| 21 | + (targetElement.parentElement!.clientWidth as number) - | ||
| 22 | + targetElement.clientWidth - | ||
| 23 | + targetElement.offsetLeft, | ||
| 24 | + width: targetElement.clientWidth, | ||
| 25 | + } | ||
| 26 | + : null; | ||
| 27 | + | ||
| 28 | + const toPX = (value?: number) => (value !== undefined ? `${value}px` : undefined); | ||
| 29 | + | ||
| 30 | + const props = defineProps<{ | ||
| 31 | + value?: string | number; | ||
| 32 | + getValueIndex: (value: string | number) => number; | ||
| 33 | + containerRef?: HTMLDivElement; | ||
| 34 | + prefixCls: string; | ||
| 35 | + direction?: 'rtl' | 'ltr'; | ||
| 36 | + motionName: string; | ||
| 37 | + }>(); | ||
| 38 | + | ||
| 39 | + const emits = defineEmits(['motionStart', 'motionEnd']); | ||
| 40 | + | ||
| 41 | + const thumbRef = ref<HTMLDivElement>(); | ||
| 42 | + | ||
| 43 | + const findValueElement = (val: string | number) => { | ||
| 44 | + const { getValueIndex, prefixCls } = props; | ||
| 45 | + const index = getValueIndex(val); | ||
| 46 | + const ele = unref(props.containerRef)?.querySelectorAll<HTMLDivElement>(`.${prefixCls}-item`)?.[ | ||
| 47 | + index | ||
| 48 | + ]; | ||
| 49 | + | ||
| 50 | + return ele?.offsetParent && ele; | ||
| 51 | + }; | ||
| 52 | + | ||
| 53 | + const prevStyle = ref<ThumbReact>(null); | ||
| 54 | + const nextStyle = ref<ThumbReact>(null); | ||
| 55 | + | ||
| 56 | + watch( | ||
| 57 | + () => props.value, | ||
| 58 | + (value, prevValue) => { | ||
| 59 | + const prev = findValueElement(prevValue as number); | ||
| 60 | + const next = findValueElement(value as number); | ||
| 61 | + | ||
| 62 | + const calcPrevStyle = calcThumbStyle(prev); | ||
| 63 | + const calcNextStyle = calcThumbStyle(next); | ||
| 64 | + | ||
| 65 | + prevStyle.value = calcPrevStyle; | ||
| 66 | + nextStyle.value = calcNextStyle; | ||
| 67 | + | ||
| 68 | + prev && next ? emits('motionStart') : emits('motionEnd'); | ||
| 69 | + }, | ||
| 70 | + { flush: 'post' } | ||
| 71 | + ); | ||
| 72 | + | ||
| 73 | + const thumbStart = computed(() => { | ||
| 74 | + const { direction } = props; | ||
| 75 | + | ||
| 76 | + return direction === 'rtl' | ||
| 77 | + ? toPX(-(prevStyle.value?.right as number)) | ||
| 78 | + : toPX(prevStyle.value?.left as number); | ||
| 79 | + }); | ||
| 80 | + | ||
| 81 | + const thumbActive = computed(() => { | ||
| 82 | + const { direction } = props; | ||
| 83 | + | ||
| 84 | + return direction === 'rtl' | ||
| 85 | + ? toPX(-(nextStyle.value?.right as number)) | ||
| 86 | + : toPX(nextStyle.value?.left as number); | ||
| 87 | + }); | ||
| 88 | + | ||
| 89 | + let timeId: any; | ||
| 90 | + const onAppearStart: TransitionProps['onBeforeEnter'] = (el: HTMLDivElement) => { | ||
| 91 | + clearTimeout(timeId); | ||
| 92 | + nextTick(() => { | ||
| 93 | + if (el) { | ||
| 94 | + el.style.transform = `translateX(var(--thumb-start-left))`; | ||
| 95 | + el.style.width = `var(--thumb-start-width)`; | ||
| 96 | + } | ||
| 97 | + }); | ||
| 98 | + }; | ||
| 99 | + | ||
| 100 | + const onAppearActive: TransitionProps['onEnter'] = (el: HTMLDivElement) => { | ||
| 101 | + timeId = setTimeout(() => { | ||
| 102 | + if (el) { | ||
| 103 | + addClass(el, `${props.motionName}-appear-active`); | ||
| 104 | + el.style.transform = `translateX(var(--thumb-active-left))`; | ||
| 105 | + el.style.width = `var(--thumb-active-width)`; | ||
| 106 | + } | ||
| 107 | + }); | ||
| 108 | + }; | ||
| 109 | + | ||
| 110 | + const onAppearEnd: TransitionProps['onAfterEnter'] = (el: HTMLDivElement) => { | ||
| 111 | + prevStyle.value = null; | ||
| 112 | + nextStyle.value = null; | ||
| 113 | + if (el) { | ||
| 114 | + el.style.transform = ''; | ||
| 115 | + el.style.width = ''; | ||
| 116 | + removeClass(el, `${props.motionName}-appear-active`); | ||
| 117 | + } | ||
| 118 | + emits('motionEnd'); | ||
| 119 | + }; | ||
| 120 | + | ||
| 121 | + const mergedStyle = computed( | ||
| 122 | + () => | ||
| 123 | + ({ | ||
| 124 | + '--thumb-start-left': thumbStart.value, | ||
| 125 | + '--thumb-start-width': toPX(prevStyle.value?.width), | ||
| 126 | + '--thumb-active-left': thumbActive.value, | ||
| 127 | + '--thumb-active-width': toPX(nextStyle.value?.width), | ||
| 128 | + } as CSSProperties) | ||
| 129 | + ); | ||
| 130 | + | ||
| 131 | + onBeforeUnmount(() => { | ||
| 132 | + clearTimeout(timeId); | ||
| 133 | + }); | ||
| 134 | + | ||
| 135 | + const motionProps = computed(() => { | ||
| 136 | + return { | ||
| 137 | + ref: thumbRef, | ||
| 138 | + style: mergedStyle.value, | ||
| 139 | + class: `${props.prefixCls}-thumb`, | ||
| 140 | + }; | ||
| 141 | + }); | ||
| 142 | +</script> | ||
| 143 | + | ||
| 144 | +<template> | ||
| 145 | + <Transition | ||
| 146 | + appear | ||
| 147 | + @before-enter="onAppearStart" | ||
| 148 | + @enter="onAppearActive" | ||
| 149 | + @after-enter="onAppearEnd" | ||
| 150 | + > | ||
| 151 | + <div v-if="prevStyle && nextStyle" v-bind="motionProps"></div> | ||
| 152 | + </Transition> | ||
| 153 | +</template> |
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { computed, ref } from 'vue'; | ||
| 3 | + import MotionThumb from './MotionThumb.vue'; | ||
| 4 | + import { SegmentedBaseOption, SegmentedOption } from './type'; | ||
| 5 | + | ||
| 6 | + const props = withDefaults( | ||
| 7 | + defineProps<{ | ||
| 8 | + block?: boolean; | ||
| 9 | + disabled?: boolean; | ||
| 10 | + options?: string[] | number[] | SegmentedOption[]; | ||
| 11 | + value?: string | number; | ||
| 12 | + motionName?: string; | ||
| 13 | + }>(), | ||
| 14 | + { | ||
| 15 | + options: () => [], | ||
| 16 | + motionName: 'thumb-motion', | ||
| 17 | + } | ||
| 18 | + ); | ||
| 19 | + | ||
| 20 | + const slots = defineSlots<{ label?(props: SegmentedBaseOption): any }>(); | ||
| 21 | + | ||
| 22 | + const prefixCls = 'segmented'; | ||
| 23 | + | ||
| 24 | + const thumbShow = ref(false); | ||
| 25 | + | ||
| 26 | + const rootRef = ref<HTMLDivElement>(); | ||
| 27 | + | ||
| 28 | + const getOptions = computed<SegmentedBaseOption[]>(() => { | ||
| 29 | + const { options } = props; | ||
| 30 | + return (options as SegmentedOption[]).map((item) => { | ||
| 31 | + return { | ||
| 32 | + value: item?.value || item, | ||
| 33 | + disabled: item?.disabled || false, | ||
| 34 | + payload: item?.payload || {}, | ||
| 35 | + title: item?.title || item, | ||
| 36 | + className: item?.className || '', | ||
| 37 | + } as SegmentedBaseOption; | ||
| 38 | + }); | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + const emits = defineEmits(['change', 'update:value']); | ||
| 42 | + | ||
| 43 | + const handleChange = (value: string | number) => { | ||
| 44 | + emits('update:value', value); | ||
| 45 | + emits('change', value); | ||
| 46 | + }; | ||
| 47 | +</script> | ||
| 48 | + | ||
| 49 | +<template> | ||
| 50 | + <div :class="prefixCls" ref="rootRef"> | ||
| 51 | + <div :class="`${prefixCls}-group`"> | ||
| 52 | + <MotionThumb | ||
| 53 | + :containerRef="rootRef" | ||
| 54 | + :prefix-cls="prefixCls" | ||
| 55 | + :value="value" | ||
| 56 | + :motion-name="`${prefixCls}-${motionName}`" | ||
| 57 | + direction="ltr" | ||
| 58 | + :get-value-index="(val) => getOptions.findIndex((item) => item.value === val)" | ||
| 59 | + @motion-start="thumbShow = true" | ||
| 60 | + @motion-end="thumbShow = false" | ||
| 61 | + /> | ||
| 62 | + <label | ||
| 63 | + v-for="item in getOptions" | ||
| 64 | + :key="item.value" | ||
| 65 | + :class="[ | ||
| 66 | + `${prefixCls}-item`, | ||
| 67 | + value === item.value && !thumbShow && `${prefixCls}-item-selected`, | ||
| 68 | + (disabled || item.disabled) && `${prefixCls}-item-disabled`, | ||
| 69 | + ]" | ||
| 70 | + @click="!disabled && !item.disabled && handleChange(item.value)" | ||
| 71 | + > | ||
| 72 | + <input type="radio" class="absolute pointer-events-none w-0 h-0 opacity-0" /> | ||
| 73 | + <slot v-if="slots.label" name="label" v-bind="item"></slot> | ||
| 74 | + <template v-else> | ||
| 75 | + <div :class="`${prefixCls}-item-label`"> {{ item.title }}</div> | ||
| 76 | + </template> | ||
| 77 | + </label> | ||
| 78 | + </div> | ||
| 79 | + </div> | ||
| 80 | +</template> | ||
| 81 | + | ||
| 82 | +<style lang="less"> | ||
| 83 | + .segmented { | ||
| 84 | + box-sizing: border-box; | ||
| 85 | + padding: 2px; | ||
| 86 | + color: #000000a6; | ||
| 87 | + font-size: 14px; | ||
| 88 | + display: inline-block; | ||
| 89 | + border-radius: 4px; | ||
| 90 | + background-color: #f5f5f5; | ||
| 91 | + transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
| 92 | + | ||
| 93 | + &-block { | ||
| 94 | + display: flex; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + &-block.segmented-item { | ||
| 98 | + flex: 1; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + &-group { | ||
| 102 | + position: relative; | ||
| 103 | + display: flex; | ||
| 104 | + align-items: stretch; | ||
| 105 | + justify-content: flex-start; | ||
| 106 | + width: 100%; | ||
| 107 | + border-radius: inherit; | ||
| 108 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + &-item { | ||
| 112 | + position: relative; | ||
| 113 | + cursor: pointer; | ||
| 114 | + border-radius: inherit; | ||
| 115 | + text-align: center; | ||
| 116 | + transition: color 0.2s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
| 117 | + | ||
| 118 | + &-label { | ||
| 119 | + min-height: 28px; | ||
| 120 | + line-height: 28px; | ||
| 121 | + padding: 0 11px; | ||
| 122 | + overflow: hidden; | ||
| 123 | + white-space: nowrap; | ||
| 124 | + text-overflow: ellipsis; | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + &::after { | ||
| 128 | + content: ''; | ||
| 129 | + position: absolute; | ||
| 130 | + width: 100%; | ||
| 131 | + height: 100%; | ||
| 132 | + top: 0; | ||
| 133 | + inset-inline-start: 0; | ||
| 134 | + border-radius: inherit; | ||
| 135 | + transition: background-color 0.2s; | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + &:hover:not(.segmented-item-selected):not(.segmented-item-disabled) { | ||
| 139 | + color: #000000e0; | ||
| 140 | + | ||
| 141 | + &::after { | ||
| 142 | + background-color: rgba(0, 0, 0, 0.06); | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + &-disabled { | ||
| 147 | + cursor: not-allowed; | ||
| 148 | + color: rgba(0, 0, 0, 0.25); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + &-selected { | ||
| 152 | + background-color: #fff; | ||
| 153 | + | ||
| 154 | + ::after { | ||
| 155 | + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), | ||
| 156 | + 0 2px 4px 0 rgba(0, 0, 0, 0.02); | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + &-selected:not(.segmented-item-disabled) { | ||
| 161 | + color: rgba(0, 0, 0, 0.88); | ||
| 162 | + } | ||
| 163 | + } | ||
| 164 | + | ||
| 165 | + &-thumb { | ||
| 166 | + position: absolute; | ||
| 167 | + inset-block-start: 0; | ||
| 168 | + inset-inline-start: 0; | ||
| 169 | + width: 0; | ||
| 170 | + height: 100%; | ||
| 171 | + background-color: #fff; | ||
| 172 | + | ||
| 173 | + &-motion-appear-active { | ||
| 174 | + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), | ||
| 175 | + width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); | ||
| 176 | + will-change: transform, width; | ||
| 177 | + } | ||
| 178 | + | ||
| 179 | + & ~ .segmented-item:not(.segmented-item-selected):not(.segmented-item-disabled)::after { | ||
| 180 | + background-color: transparent; | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | +</style> |
| 1 | +export { default as Segmented } from './Segmented.vue'; |
| 1 | +export function hasClass(node: HTMLElement, className: string) { | ||
| 2 | + if (node.classList) { | ||
| 3 | + return node.classList.contains(className); | ||
| 4 | + } | ||
| 5 | + const originClass = node.className; | ||
| 6 | + return ` ${originClass} `.indexOf(` ${className} `) > -1; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export function addClass(node: HTMLElement, className: string) { | ||
| 10 | + if (node.classList) { | ||
| 11 | + node.classList.add(className); | ||
| 12 | + } else { | ||
| 13 | + if (!hasClass(node, className)) { | ||
| 14 | + node.className = `${node.className} ${className}`; | ||
| 15 | + } | ||
| 16 | + } | ||
| 17 | +} |
| 1 | +export interface SegmentedBaseOption { | ||
| 2 | + value: string | number; | ||
| 3 | + disabled?: boolean; | ||
| 4 | + payload?: any; | ||
| 5 | + title?: string; | ||
| 6 | + className?: string; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export interface SegmentedOption extends SegmentedBaseOption { | ||
| 10 | + label?: VueNode | ((option: SegmentedBaseOption) => VueNode); | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +export type ThumbReact = { | ||
| 14 | + left: number; | ||
| 15 | + right: number; | ||
| 16 | + width: number; | ||
| 17 | +} | null; |
src/components/Form/src/components/ThingsModelForm/StructPreview.vue
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/StructPreview.vue
src/components/Form/src/components/ThingsModelForm/config.ts
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/config.ts
| 1 | -import { FormSchema } from '../../../types/form'; | 1 | +import { FormSchema } from '/@/components/Form'; |
| 2 | import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 2 | import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 3 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 3 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 4 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | 5 | +import { validateTCPCustomCommand } from '.'; |
| 5 | 6 | ||
| 6 | export const getFormSchemas = ({ | 7 | export const getFormSchemas = ({ |
| 7 | structJSON: structJson, | 8 | structJSON: structJson, |
| @@ -127,6 +128,7 @@ export const getFormSchemas = ({ | @@ -127,6 +128,7 @@ export const getFormSchemas = ({ | ||
| 127 | label: functionName!, | 128 | label: functionName!, |
| 128 | component: 'Input', | 129 | component: 'Input', |
| 129 | changeEvent: 'update:value', | 130 | changeEvent: 'update:value', |
| 131 | + rules: [{ required: required, validator: () => Promise.resolve() }], | ||
| 130 | componentProps: () => { | 132 | componentProps: () => { |
| 131 | return { | 133 | return { |
| 132 | inputData: dataType?.specs || [], | 134 | inputData: dataType?.specs || [], |
| @@ -143,6 +145,7 @@ export const getFormSchemas = ({ | @@ -143,6 +145,7 @@ export const getFormSchemas = ({ | ||
| 143 | component: 'Input', | 145 | component: 'Input', |
| 144 | required, | 146 | required, |
| 145 | defaultValue: serviceCommand, | 147 | defaultValue: serviceCommand, |
| 148 | + rules: [{ validator: validateTCPCustomCommand }], | ||
| 146 | componentProps: { | 149 | componentProps: { |
| 147 | placeholder: `请输入服务命令`, | 150 | placeholder: `请输入服务命令`, |
| 148 | }, | 151 | }, |
src/components/Form/src/components/ThingsModelForm/index.ts
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/index.ts
src/components/Form/src/components/ThingsModelForm/index.vue
renamed from
src/components/Form/src/externalCompns/components/ThingsModelForm/index.vue
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | + import { | ||
| 3 | + ComponentPublicInstance, | ||
| 4 | + computed, | ||
| 5 | + nextTick, | ||
| 6 | + reactive, | ||
| 7 | + ref, | ||
| 8 | + toRaw, | ||
| 9 | + unref, | ||
| 10 | + watch, | ||
| 11 | + } from 'vue'; | ||
| 2 | import { Card } from 'ant-design-vue'; | 12 | import { Card } from 'ant-design-vue'; |
| 3 | - import { ComponentPublicInstance, computed, nextTick, reactive, unref, watch } from 'vue'; | ||
| 4 | import { getFormSchemas } from './config'; | 13 | import { getFormSchemas } from './config'; |
| 5 | import { ThingsModelForm } from '.'; | 14 | import { ThingsModelForm } from '.'; |
| 6 | - import { DefineComponentsBasicExpose } from '/#/utils'; | ||
| 7 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 15 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 8 | - import { useForm } from '../../../hooks/useForm'; | 16 | + import { useForm } from '../../hooks/useForm'; |
| 9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 17 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 10 | - import { BasicForm } from '/@/components/Form'; | ||
| 11 | - | ||
| 12 | - const props = withDefaults( | ||
| 13 | - defineProps<{ | ||
| 14 | - value?: Recordable; | ||
| 15 | - inputData?: StructJSON[]; | ||
| 16 | - required?: boolean; | ||
| 17 | - title?: string; | ||
| 18 | - transportType?: string; | ||
| 19 | - disabled?: boolean; | ||
| 20 | - identifier?: string; | ||
| 21 | - }>(), | ||
| 22 | - { | ||
| 23 | - inputData: () => [], | ||
| 24 | - required: true, | ||
| 25 | - } | ||
| 26 | - ); | 18 | + import { BasicForm, FormProps } from '/@/components/Form'; |
| 19 | + import { deepMerge } from '/@/utils'; | ||
| 20 | + | ||
| 21 | + interface ThingsModelFormPropsType { | ||
| 22 | + value?: Recordable; | ||
| 23 | + inputData?: StructJSON[]; | ||
| 24 | + required?: boolean; | ||
| 25 | + title?: string; | ||
| 26 | + transportType?: string; | ||
| 27 | + disabled?: boolean; | ||
| 28 | + identifier?: string; | ||
| 29 | + formProps?: FormProps; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + const props = withDefaults(defineProps<ThingsModelFormPropsType>(), { | ||
| 33 | + inputData: () => [], | ||
| 34 | + required: true, | ||
| 35 | + formProps: () => ({}), | ||
| 36 | + }); | ||
| 27 | 37 | ||
| 28 | const thingsModelFormListElMap = reactive< | 38 | const thingsModelFormListElMap = reactive< |
| 29 | Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }> | 39 | Record<string, { el: InstanceType<typeof ThingsModelForm>; structJSON: StructJSON }> |
| 30 | >({}); | 40 | >({}); |
| 31 | 41 | ||
| 42 | + const propsRef = ref<Partial<ThingsModelFormPropsType>>({}); | ||
| 43 | + | ||
| 44 | + const getProps = computed<ThingsModelFormPropsType>( | ||
| 45 | + () => ({ ...props, ...unref(propsRef) } as ThingsModelFormPropsType) | ||
| 46 | + ); | ||
| 47 | + | ||
| 32 | const [register, formActionType] = useForm({ | 48 | const [register, formActionType] = useForm({ |
| 33 | - schemas: getFormSchemas({ | ||
| 34 | - structJSON: props.inputData || [], | ||
| 35 | - required: props.required, | ||
| 36 | - transportType: props.transportType, | ||
| 37 | - }), | ||
| 38 | - showActionButtonGroup: false, | ||
| 39 | layout: 'inline', | 49 | layout: 'inline', |
| 40 | labelWidth: 100, | 50 | labelWidth: 100, |
| 51 | + ...toRaw(unref(getProps)), | ||
| 52 | + schemas: getFormSchemasByProps(), | ||
| 53 | + showActionButtonGroup: false, | ||
| 41 | }); | 54 | }); |
| 42 | 55 | ||
| 43 | const getStructFormItem = computed(() => { | 56 | const getStructFormItem = computed(() => { |
| 44 | - const { inputData } = props; | ||
| 45 | - return inputData.filter((item) => item.dataType?.type === DataTypeEnum.STRUCT); | 57 | + const { inputData } = unref(getProps); |
| 58 | + return (inputData || []).filter((item) => item.dataType?.type === DataTypeEnum.STRUCT); | ||
| 46 | }); | 59 | }); |
| 47 | 60 | ||
| 48 | const setFormElRef = ( | 61 | const setFormElRef = ( |
| @@ -87,7 +100,7 @@ | @@ -87,7 +100,7 @@ | ||
| 87 | }; | 100 | }; |
| 88 | 101 | ||
| 89 | watch( | 102 | watch( |
| 90 | - () => props.value, | 103 | + () => unref(getProps).value, |
| 91 | async (value) => { | 104 | async (value) => { |
| 92 | await nextTick(); | 105 | await nextTick(); |
| 93 | formActionType.resetFields(); | 106 | formActionType.resetFields(); |
| @@ -97,30 +110,36 @@ | @@ -97,30 +110,36 @@ | ||
| 97 | ); | 110 | ); |
| 98 | 111 | ||
| 99 | watch( | 112 | watch( |
| 100 | - () => [props.inputData, props.identifier], | 113 | + () => [unref(getProps).inputData, unref(getProps).identifier], |
| 101 | (value) => { | 114 | (value) => { |
| 102 | - if (value && value.length) { | ||
| 103 | - const schemas = getFormSchemas({ | ||
| 104 | - structJSON: props.inputData || [], | ||
| 105 | - required: props.required, | ||
| 106 | - transportType: props.transportType, | ||
| 107 | - }); | ||
| 108 | - formActionType.setProps({ schemas }); | ||
| 109 | - } | 115 | + if (value && value.length) formActionType.setProps({ schemas: getFormSchemasByProps() }); |
| 110 | } | 116 | } |
| 111 | ); | 117 | ); |
| 112 | 118 | ||
| 113 | watch( | 119 | watch( |
| 114 | - () => props.disabled, | 120 | + () => unref(getProps).disabled, |
| 115 | (value) => { | 121 | (value) => { |
| 116 | formActionType.setProps({ disabled: value }); | 122 | formActionType.setProps({ disabled: value }); |
| 117 | } | 123 | } |
| 118 | ); | 124 | ); |
| 119 | 125 | ||
| 120 | - defineExpose<DefineComponentsBasicExpose>({ | 126 | + function getFormSchemasByProps() { |
| 127 | + return getFormSchemas({ | ||
| 128 | + structJSON: unref(getProps).inputData || [], | ||
| 129 | + required: unref(getProps).required, | ||
| 130 | + transportType: unref(getProps).transportType, | ||
| 131 | + }); | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + function setProps(props: Partial<ThingsModelFormPropsType>) { | ||
| 135 | + propsRef.value = deepMerge(unref(propsRef) || {}, props); | ||
| 136 | + } | ||
| 137 | + | ||
| 138 | + defineExpose({ | ||
| 121 | getFieldsValue, | 139 | getFieldsValue, |
| 122 | setFieldsValue, | 140 | setFieldsValue, |
| 123 | validate, | 141 | validate, |
| 142 | + setProps, | ||
| 124 | }); | 143 | }); |
| 125 | </script> | 144 | </script> |
| 126 | 145 | ||
| @@ -155,6 +174,10 @@ | @@ -155,6 +174,10 @@ | ||
| 155 | width: 100%; | 174 | width: 100%; |
| 156 | } | 175 | } |
| 157 | 176 | ||
| 177 | + :deep(.ant-col.ant-form-item-control) { | ||
| 178 | + min-height: auto; | ||
| 179 | + } | ||
| 180 | + | ||
| 158 | :deep(.ant-input-number) { | 181 | :deep(.ant-input-number) { |
| 159 | width: 100%; | 182 | width: 100%; |
| 160 | } | 183 | } |
src/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue
deleted
100644 → 0
| 1 | -<script lang="ts" setup> | ||
| 2 | - import { Card } from 'ant-design-vue'; | ||
| 3 | - import { computed, nextTick, onMounted, onUpdated, unref, watch } from 'vue'; | ||
| 4 | - import { BasicCreateFormParams } from './type'; | ||
| 5 | - import { DynamicProps } from '/#/utils'; | ||
| 6 | - import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 7 | - import { BasicForm, FormProps, FormSchema, useForm } from '/@/components/Form'; | ||
| 8 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 9 | - import { ReadAndWriteEnum } from '/@/enums/toolEnum'; | ||
| 10 | - | ||
| 11 | - const props = withDefaults( | ||
| 12 | - defineProps<{ | ||
| 13 | - inputData?: StructJSON[]; | ||
| 14 | - formProps?: FormProps; | ||
| 15 | - value?: Recordable; | ||
| 16 | - required?: boolean; | ||
| 17 | - }>(), | ||
| 18 | - { | ||
| 19 | - inputData: () => [] as StructJSON[], | ||
| 20 | - formProps: () => ({}), | ||
| 21 | - value: () => ({}), | ||
| 22 | - } | ||
| 23 | - ); | ||
| 24 | - | ||
| 25 | - const emit = defineEmits(['update:value']); | ||
| 26 | - | ||
| 27 | - const getCurrentKeys = computed(() => { | ||
| 28 | - const { inputData } = props; | ||
| 29 | - return (inputData || []).map((item) => item.identifier); | ||
| 30 | - }); | ||
| 31 | - | ||
| 32 | - const [register, { setProps, getFieldsValue, setFieldsValue }] = useForm({ | ||
| 33 | - schemas: [], | ||
| 34 | - showActionButtonGroup: false, | ||
| 35 | - layout: 'inline', | ||
| 36 | - labelWidth: 80, | ||
| 37 | - wrapperCol: { span: 12 }, | ||
| 38 | - ...(props.formProps || ({} as unknown as Partial<DynamicProps<FormProps>>)), | ||
| 39 | - }); | ||
| 40 | - | ||
| 41 | - const syncValue = (key: string, value: any) => { | ||
| 42 | - let record = getFieldsValue(); | ||
| 43 | - record = { ...record, [key]: value }; | ||
| 44 | - const setValues = {}; | ||
| 45 | - Object.keys(record).forEach((key) => { | ||
| 46 | - const hasKey = unref(getCurrentKeys).includes(key); | ||
| 47 | - if (hasKey) { | ||
| 48 | - setValues[key] = record[key]; | ||
| 49 | - } | ||
| 50 | - }); | ||
| 51 | - emit('update:value', setValues); | ||
| 52 | - }; | ||
| 53 | - | ||
| 54 | - const createInputNumber = ({ | ||
| 55 | - identifier, | ||
| 56 | - functionName, | ||
| 57 | - specs, | ||
| 58 | - }: BasicCreateFormParams): FormSchema => { | ||
| 59 | - const { valueRange } = specs! as Partial<Specs>; | ||
| 60 | - const { max = 2147483647, min = -2147483648 } = valueRange || {}; | ||
| 61 | - return { | ||
| 62 | - field: identifier, | ||
| 63 | - label: functionName, | ||
| 64 | - component: 'InputNumber', | ||
| 65 | - rules: [ | ||
| 66 | - { | ||
| 67 | - required: props.required, | ||
| 68 | - message: `${functionName}是必填项`, | ||
| 69 | - }, | ||
| 70 | - { | ||
| 71 | - type: 'number', | ||
| 72 | - trigger: 'change', | ||
| 73 | - validator: (_rule, value) => { | ||
| 74 | - const reg = /^[0-9]*$/; | ||
| 75 | - if (!reg.test(value)) return Promise.reject(`${functionName}不是一个有效的数字`); | ||
| 76 | - if (value < min || value > max) { | ||
| 77 | - return Promise.reject(`${functionName}取值范围在${min}~${max}之间`); | ||
| 78 | - } | ||
| 79 | - return Promise.resolve(value); | ||
| 80 | - }, | ||
| 81 | - }, | ||
| 82 | - ], | ||
| 83 | - componentProps: { | ||
| 84 | - max, | ||
| 85 | - min, | ||
| 86 | - // step: step, | ||
| 87 | - // formatter: (value: string) => value, | ||
| 88 | - // parser: (string: string) => { | ||
| 89 | - // if (dataType === DataTypeEnum.NUMBER_INT) { | ||
| 90 | - // return Number(Number(string).toFixed()); | ||
| 91 | - // } | ||
| 92 | - // return Number(string); | ||
| 93 | - // }, | ||
| 94 | - onChange: (value: string) => { | ||
| 95 | - syncValue(identifier, value); | ||
| 96 | - }, | ||
| 97 | - }, | ||
| 98 | - } as FormSchema; | ||
| 99 | - }; | ||
| 100 | - | ||
| 101 | - const createInput = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | ||
| 102 | - const { length = 10240 } = specs! as Partial<Specs>; | ||
| 103 | - return { | ||
| 104 | - field: identifier, | ||
| 105 | - label: functionName, | ||
| 106 | - component: 'Input', | ||
| 107 | - rules: [ | ||
| 108 | - { | ||
| 109 | - required: props.required, | ||
| 110 | - message: `${functionName}是必填项`, | ||
| 111 | - }, | ||
| 112 | - { | ||
| 113 | - type: 'string', | ||
| 114 | - trigger: 'change', | ||
| 115 | - validator: (_rule, value) => { | ||
| 116 | - if (value.length > length) { | ||
| 117 | - return Promise.reject(`${functionName}数据长度应该小于${length}`); | ||
| 118 | - } | ||
| 119 | - return Promise.resolve(value); | ||
| 120 | - }, | ||
| 121 | - }, | ||
| 122 | - ], | ||
| 123 | - componentProps: { | ||
| 124 | - maxLength: length, | ||
| 125 | - onChange: (value: InputEvent) => { | ||
| 126 | - syncValue(identifier, (value.target as HTMLInputElement).value); | ||
| 127 | - }, | ||
| 128 | - }, | ||
| 129 | - } as FormSchema; | ||
| 130 | - }; | ||
| 131 | - | ||
| 132 | - const createSelect = ({ identifier, functionName, specs }: BasicCreateFormParams): FormSchema => { | ||
| 133 | - const { boolClose, boolOpen } = specs! as Partial<Specs>; | ||
| 134 | - return { | ||
| 135 | - field: identifier, | ||
| 136 | - label: functionName, | ||
| 137 | - component: 'Select', | ||
| 138 | - rules: [ | ||
| 139 | - { | ||
| 140 | - required: props.required, | ||
| 141 | - message: `${functionName}是必填项`, | ||
| 142 | - type: 'number', | ||
| 143 | - }, | ||
| 144 | - ], | ||
| 145 | - componentProps: { | ||
| 146 | - options: [ | ||
| 147 | - { label: `${boolClose}-0`, value: 0 }, | ||
| 148 | - { label: `${boolOpen}-1`, value: 1 }, | ||
| 149 | - ], | ||
| 150 | - onChange: (value: string) => { | ||
| 151 | - syncValue(identifier, value); | ||
| 152 | - }, | ||
| 153 | - }, | ||
| 154 | - }; | ||
| 155 | - }; | ||
| 156 | - | ||
| 157 | - const createInputJson = ({ identifier, functionName }: BasicCreateFormParams): FormSchema => { | ||
| 158 | - return { | ||
| 159 | - field: identifier, | ||
| 160 | - label: functionName, | ||
| 161 | - component: 'InputTextArea', | ||
| 162 | - rules: [ | ||
| 163 | - { | ||
| 164 | - required: props.required, | ||
| 165 | - message: `${functionName}是必填项`, | ||
| 166 | - }, | ||
| 167 | - ], | ||
| 168 | - componentProps: { | ||
| 169 | - onChange: (value: InputEvent) => { | ||
| 170 | - syncValue(identifier, (value.target as HTMLInputElement).value); | ||
| 171 | - }, | ||
| 172 | - }, | ||
| 173 | - }; | ||
| 174 | - }; | ||
| 175 | - | ||
| 176 | - const transformToFormSchema = (inputData: StructJSON[]) => { | ||
| 177 | - const schemas: FormSchema[] = []; | ||
| 178 | - for (const item of inputData) { | ||
| 179 | - const { dataType, identifier, functionName, accessMode } = item; | ||
| 180 | - if (accessMode === ReadAndWriteEnum.READ) { | ||
| 181 | - continue; | ||
| 182 | - } | ||
| 183 | - const { type, specs } = dataType! || {}; | ||
| 184 | - | ||
| 185 | - const params: BasicCreateFormParams = { | ||
| 186 | - identifier: identifier!, | ||
| 187 | - functionName: functionName!, | ||
| 188 | - dataType: dataType! as unknown as DataTypeEnum, | ||
| 189 | - specs: specs as Partial<Specs>, | ||
| 190 | - }; | ||
| 191 | - if (type === DataTypeEnum.NUMBER_INT || type === DataTypeEnum.NUMBER_DOUBLE) { | ||
| 192 | - schemas.push(createInputNumber(params)); | ||
| 193 | - } | ||
| 194 | - | ||
| 195 | - if (type === DataTypeEnum.BOOL) { | ||
| 196 | - schemas.push(createSelect(params)); | ||
| 197 | - } | ||
| 198 | - | ||
| 199 | - if (type === DataTypeEnum.STRING) { | ||
| 200 | - schemas.push(createInput(params)); | ||
| 201 | - } | ||
| 202 | - | ||
| 203 | - if (type === DataTypeEnum.STRUCT) { | ||
| 204 | - schemas.push(createInputJson(params)); | ||
| 205 | - } | ||
| 206 | - } | ||
| 207 | - | ||
| 208 | - return schemas; | ||
| 209 | - }; | ||
| 210 | - | ||
| 211 | - const generateSchemas = (value: StructJSON[]) => { | ||
| 212 | - if (value && value.length) { | ||
| 213 | - const schemas = transformToFormSchema(value); | ||
| 214 | - setProps({ schemas }); | ||
| 215 | - } | ||
| 216 | - }; | ||
| 217 | - | ||
| 218 | - onMounted(() => { | ||
| 219 | - generateSchemas(props.inputData); | ||
| 220 | - }); | ||
| 221 | - | ||
| 222 | - onUpdated(async () => { | ||
| 223 | - if (props.inputData && props.inputData.length) { | ||
| 224 | - await nextTick(); | ||
| 225 | - setFieldsValue(props.value || {}); | ||
| 226 | - } | ||
| 227 | - }); | ||
| 228 | - | ||
| 229 | - watch( | ||
| 230 | - () => props.inputData, | ||
| 231 | - (value) => { | ||
| 232 | - generateSchemas(value); | ||
| 233 | - } | ||
| 234 | - ); | ||
| 235 | - | ||
| 236 | - watch( | ||
| 237 | - () => props.formProps, | ||
| 238 | - (value) => { | ||
| 239 | - setProps(value); | ||
| 240 | - } | ||
| 241 | - ); | ||
| 242 | -</script> | ||
| 243 | - | ||
| 244 | -<template> | ||
| 245 | - <Card bordered class="!border-dashed !rounded-lg !border-2px"> | ||
| 246 | - <!-- <Alert class="!mb-4 w-32" message="服务参数配置" type="info" /> --> | ||
| 247 | - <BasicForm class="object-model-validate-form" @register="register" /> | ||
| 248 | - </Card> | ||
| 249 | -</template> | ||
| 250 | - | ||
| 251 | -<style lang="less"> | ||
| 252 | - .object-model-validate-form { | ||
| 253 | - .ant-input-number { | ||
| 254 | - width: 100% !important; | ||
| 255 | - } | ||
| 256 | - } | ||
| 257 | -</style> |
src/components/Form/src/externalCompns/components/StructForm/StructForm.vue
deleted
100644 → 0
| 1 | -<script lang="ts"> | ||
| 2 | - export default { | ||
| 3 | - inheritAttrs: false, | ||
| 4 | - }; | ||
| 5 | -</script> | ||
| 6 | -<script lang="ts" setup> | ||
| 7 | - import StructFormModel from './StructFormModel.vue'; | ||
| 8 | - import { useModal } from '/@/components/Modal'; | ||
| 9 | - import { PlusOutlined } from '@ant-design/icons-vue'; | ||
| 10 | - import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
| 11 | - import { computed, unref } from 'vue'; | ||
| 12 | - import { buildUUID } from '/@/utils/uuid'; | ||
| 13 | - import { Divider, Button } from 'ant-design-vue'; | ||
| 14 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | ||
| 15 | - import { cloneDeep } from 'lodash-es'; | ||
| 16 | - import { isArray } from '/@/utils/is'; | ||
| 17 | - import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
| 18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 19 | - | ||
| 20 | - const emit = defineEmits(['update:value']); | ||
| 21 | - | ||
| 22 | - const props = withDefaults( | ||
| 23 | - defineProps<{ | ||
| 24 | - value: ModelOfMatterParams[]; | ||
| 25 | - disabled: boolean; | ||
| 26 | - hasStructForm?: boolean; | ||
| 27 | - hiddenAccessMode?: boolean; | ||
| 28 | - }>(), | ||
| 29 | - { | ||
| 30 | - value: () => [], | ||
| 31 | - hiddenAccessMode: false, | ||
| 32 | - hasStructForm: false, | ||
| 33 | - } | ||
| 34 | - ); | ||
| 35 | - | ||
| 36 | - const getValue = computed<StructRecord[]>(() => { | ||
| 37 | - const { value } = props; | ||
| 38 | - | ||
| 39 | - return (isArray(value) ? value : []).map((item) => { | ||
| 40 | - return { | ||
| 41 | - ...(item as StructRecord), | ||
| 42 | - ...((item as StructRecord).id ? {} : { id: buildUUID() }), | ||
| 43 | - }; | ||
| 44 | - }); | ||
| 45 | - }); | ||
| 46 | - | ||
| 47 | - const [registerModal, { openModal }] = useModal(); | ||
| 48 | - | ||
| 49 | - const handleCreateParams = () => { | ||
| 50 | - openModal(true, { | ||
| 51 | - mode: OpenModalMode.CREATE, | ||
| 52 | - } as OpenModalParams); | ||
| 53 | - }; | ||
| 54 | - | ||
| 55 | - const handleUpdate = (value: StructRecord) => { | ||
| 56 | - openModal(true, { | ||
| 57 | - mode: OpenModalMode.UPDATE, | ||
| 58 | - record: { | ||
| 59 | - ...value, | ||
| 60 | - [FormField.HAS_STRUCT_FROM]: value?.dataType?.type === DataTypeEnum.STRUCT, | ||
| 61 | - }, | ||
| 62 | - } as OpenModalParams); | ||
| 63 | - }; | ||
| 64 | - | ||
| 65 | - const handleDelete = (value: StructRecord) => { | ||
| 66 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | ||
| 67 | - const _value = cloneDeep(unref(getValue)); | ||
| 68 | - _value.splice(index, 1); | ||
| 69 | - emit('update:value', _value); | ||
| 70 | - }; | ||
| 71 | - | ||
| 72 | - const handleSaveStruct = (mode: OpenModalMode, value: StructRecord) => { | ||
| 73 | - const _value = cloneDeep(unref(getValue)); | ||
| 74 | - | ||
| 75 | - if (mode === OpenModalMode.UPDATE) { | ||
| 76 | - const index = unref(getValue).findIndex((item) => item.id === value.id); | ||
| 77 | - ~index && _value.splice(index, 1, value); | ||
| 78 | - } else { | ||
| 79 | - _value.push(value); | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - emit('update:value', _value); | ||
| 83 | - }; | ||
| 84 | -</script> | ||
| 85 | - | ||
| 86 | -<template> | ||
| 87 | - <section> | ||
| 88 | - <div class="text-blue-500 cursor-pointer"> | ||
| 89 | - <section> | ||
| 90 | - <div | ||
| 91 | - class="flex bg-blue-50 mb-2 p-2 text-gray-500 justify-between items-center" | ||
| 92 | - v-for="item in getValue" | ||
| 93 | - :key="item.id" | ||
| 94 | - > | ||
| 95 | - <div>参数名称: {{ item.functionName }}</div> | ||
| 96 | - <div class="flex"> | ||
| 97 | - <Button class="!p-0" type="link" @click="handleUpdate(item)"> | ||
| 98 | - <span>{{ disabled ? '查看' : '编辑' }}</span> | ||
| 99 | - </Button> | ||
| 100 | - <Divider type="vertical" /> | ||
| 101 | - <Button :disabled="disabled" class="!p-0" type="link" @click="handleDelete(item)"> | ||
| 102 | - <span>删除</span> | ||
| 103 | - </Button> | ||
| 104 | - </div> | ||
| 105 | - </div> | ||
| 106 | - </section> | ||
| 107 | - <div :class="$props.disabled && 'text-gray-400'"> | ||
| 108 | - <span class="mr-2"> | ||
| 109 | - <PlusOutlined /> | ||
| 110 | - </span> | ||
| 111 | - <span @click="!disabled && handleCreateParams()">增加参数</span> | ||
| 112 | - </div> | ||
| 113 | - </div> | ||
| 114 | - <StructFormModel | ||
| 115 | - :has-struct-form="hasStructForm!" | ||
| 116 | - :hidden-access-mode="hiddenAccessMode" | ||
| 117 | - :disabled="disabled" | ||
| 118 | - :value-list="getValue" | ||
| 119 | - @register="registerModal" | ||
| 120 | - @submit="handleSaveStruct" | ||
| 121 | - /> | ||
| 122 | - </section> | ||
| 123 | -</template> | ||
| 124 | - | ||
| 125 | -<style lang="less" scoped></style> |
| 1 | -<script lang="ts"> | ||
| 2 | - export default { | ||
| 3 | - inheritAttrs: false, | ||
| 4 | - }; | ||
| 5 | -</script> | ||
| 6 | -<script lang="ts" setup> | ||
| 7 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
| 8 | - import { formSchemas } from './config'; | ||
| 9 | - import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
| 10 | - import { OpenModalMode, OpenModalParams, StructRecord } from './type'; | ||
| 11 | - import { computed, ref, unref } from 'vue'; | ||
| 12 | - import { transfromToStructJSON } from './util'; | ||
| 13 | - import { cloneDeep } from 'lodash-es'; | ||
| 14 | - import { DataType, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 15 | - import { isArray } from '/@/utils/is'; | ||
| 16 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 17 | - import EnumList from './EnumList.vue'; | ||
| 18 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 19 | - | ||
| 20 | - const modalReceiveRecord = ref<OpenModalParams>({ | ||
| 21 | - mode: OpenModalMode.CREATE, | ||
| 22 | - }); | ||
| 23 | - | ||
| 24 | - const props = defineProps<{ | ||
| 25 | - disabled: boolean; | ||
| 26 | - hasStructForm: boolean; | ||
| 27 | - valueList: StructRecord[]; | ||
| 28 | - hiddenAccessMode: boolean; | ||
| 29 | - }>(); | ||
| 30 | - | ||
| 31 | - const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
| 32 | - | ||
| 33 | - const emit = defineEmits(['register', 'submit']); | ||
| 34 | - | ||
| 35 | - const { createMessage } = useMessage(); | ||
| 36 | - | ||
| 37 | - const [register, { validate, setFieldsValue, setProps }] = useForm({ | ||
| 38 | - labelWidth: 100, | ||
| 39 | - actionColOptions: { | ||
| 40 | - span: 14, | ||
| 41 | - }, | ||
| 42 | - disabled: props.disabled, | ||
| 43 | - showResetButton: false, | ||
| 44 | - submitOnReset: false, | ||
| 45 | - showActionButtonGroup: false, | ||
| 46 | - }); | ||
| 47 | - | ||
| 48 | - const getFormSchemas = computed(() => { | ||
| 49 | - const { hasStructForm, hiddenAccessMode } = props; | ||
| 50 | - return formSchemas({ | ||
| 51 | - hasStructForm, | ||
| 52 | - hiddenAccessMode, | ||
| 53 | - }); | ||
| 54 | - }); | ||
| 55 | - | ||
| 56 | - const [registerModal, { closeModal }] = useModalInner((record: OpenModalParams) => { | ||
| 57 | - modalReceiveRecord.value = record; | ||
| 58 | - const data = record.record || {}; | ||
| 59 | - const { dataType = {} } = data! as StructJSON; | ||
| 60 | - const { specs = {}, type, specsList } = dataType as DataType; | ||
| 61 | - | ||
| 62 | - if (record.record) { | ||
| 63 | - const value = { | ||
| 64 | - type, | ||
| 65 | - ...data, | ||
| 66 | - ...(isArray(specs) ? { specs } : { ...specs }), | ||
| 67 | - enumList: type === DataTypeEnum.ENUM ? specsList : [], | ||
| 68 | - }; | ||
| 69 | - | ||
| 70 | - setFieldsValue(value); | ||
| 71 | - } | ||
| 72 | - setProps({ disabled: props.disabled }); | ||
| 73 | - }); | ||
| 74 | - | ||
| 75 | - const validateRepeat = (value: StructRecord, valueList: StructRecord[]) => { | ||
| 76 | - return valueList.filter((item) => item.identifier === value.identifier).length >= 1; | ||
| 77 | - }; | ||
| 78 | - | ||
| 79 | - const handleSubmit = async () => { | ||
| 80 | - try { | ||
| 81 | - const _value = await validate(); | ||
| 82 | - await unref(enumListRef)?.validate?.(); | ||
| 83 | - let structJSON = transfromToStructJSON(_value, unref(enumListRef)?.getFieldsValue?.() || []); | ||
| 84 | - const value = { | ||
| 85 | - ...structJSON, | ||
| 86 | - ...(unref(modalReceiveRecord)?.record?.id | ||
| 87 | - ? { id: unref(modalReceiveRecord)?.record?.id } | ||
| 88 | - : {}), | ||
| 89 | - }; | ||
| 90 | - | ||
| 91 | - if ( | ||
| 92 | - unref(modalReceiveRecord).mode === OpenModalMode.CREATE && | ||
| 93 | - validateRepeat(_value, props.valueList) | ||
| 94 | - ) { | ||
| 95 | - createMessage.error('存在一致的标识符'); | ||
| 96 | - return; | ||
| 97 | - } | ||
| 98 | - emit('submit', unref(modalReceiveRecord).mode, cloneDeep(value)); | ||
| 99 | - closeModal(); | ||
| 100 | - } catch (error) {} | ||
| 101 | - }; | ||
| 102 | -</script> | ||
| 103 | - | ||
| 104 | -<template> | ||
| 105 | - <BasicModal | ||
| 106 | - @register="registerModal" | ||
| 107 | - :title="modalReceiveRecord.mode === OpenModalMode.CREATE ? '创建参数' : '编辑参数'" | ||
| 108 | - :width="800" | ||
| 109 | - @ok="handleSubmit" | ||
| 110 | - destroy-on-close | ||
| 111 | - :show-ok-btn="!$props.disabled" | ||
| 112 | - > | ||
| 113 | - <BasicForm @register="register" :schemas="getFormSchemas"> | ||
| 114 | - <template #EnumList="{ field, model }"> | ||
| 115 | - <EnumList ref="enumListRef" :value="model[field]" :disabled="disabled" /> | ||
| 116 | - </template> | ||
| 117 | - </BasicForm> | ||
| 118 | - </BasicModal> | ||
| 119 | -</template> | ||
| 120 | - | ||
| 121 | -<style lang="less" scoped></style> |
src/components/Form/src/externalCompns/components/StructForm/config.ts
deleted
100644 → 0
| 1 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
| 2 | -import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 3 | -import { Rule } from '/@/components/Form'; | ||
| 4 | -import { FormSchema } from '/@/components/Table'; | ||
| 5 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 6 | -import { isNullOrUnDef } from '/@/utils/is'; | ||
| 7 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
| 8 | - | ||
| 9 | -export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | ||
| 10 | - value = value || {}; | ||
| 11 | - const { min, max } = value; | ||
| 12 | - if (min > max) { | ||
| 13 | - return Promise.reject('最大值小于最小值'); | ||
| 14 | - } | ||
| 15 | - return Promise.resolve(); | ||
| 16 | -}; | ||
| 17 | - | ||
| 18 | -export const validateJSON = (_rule, value = [] as ModelOfMatterParams[], _callback) => { | ||
| 19 | - if (value.length) { | ||
| 20 | - return Promise.resolve(); | ||
| 21 | - } | ||
| 22 | - return Promise.reject('JSON对象不能为空'); | ||
| 23 | -}; | ||
| 24 | - | ||
| 25 | -interface StructFormSchemasParmasType { | ||
| 26 | - hasStructForm: boolean; | ||
| 27 | - hiddenAccessMode: boolean; | ||
| 28 | - isTcp?: boolean; | ||
| 29 | -} | ||
| 30 | - | ||
| 31 | -//功能名称和标识符 输入框不能包含逗号 | ||
| 32 | -const validateExcludeComma = (field: string, errorName: string): Rule[] => { | ||
| 33 | - return [ | ||
| 34 | - { | ||
| 35 | - required: true, | ||
| 36 | - trigger: 'blur', | ||
| 37 | - validator: () => { | ||
| 38 | - const reg = /[,,]+/; | ||
| 39 | - if (reg.test(field)) { | ||
| 40 | - return Promise.reject(`${errorName}不能包含逗号`); | ||
| 41 | - } | ||
| 42 | - return Promise.resolve(); | ||
| 43 | - }, | ||
| 44 | - }, | ||
| 45 | - ]; | ||
| 46 | -}; | ||
| 47 | - | ||
| 48 | -export const formSchemas = ({ | ||
| 49 | - hasStructForm, | ||
| 50 | - hiddenAccessMode, | ||
| 51 | - isTcp = false, | ||
| 52 | -}: StructFormSchemasParmasType): FormSchema[] => { | ||
| 53 | - return [ | ||
| 54 | - { | ||
| 55 | - field: FormField.FUNCTION_NAME, | ||
| 56 | - label: '功能名称', | ||
| 57 | - required: true, | ||
| 58 | - component: 'Input', | ||
| 59 | - colProps: { | ||
| 60 | - span: 18, | ||
| 61 | - }, | ||
| 62 | - componentProps: { | ||
| 63 | - maxLength: 32, | ||
| 64 | - placeholder: '请输入功能名称', | ||
| 65 | - }, | ||
| 66 | - dynamicRules: ({ values }) => { | ||
| 67 | - return [ | ||
| 68 | - { required: true, message: '请输入功能名称' }, | ||
| 69 | - ...validateExcludeComma(values[FormField.FUNCTION_NAME], '功能名称'), | ||
| 70 | - ]; | ||
| 71 | - }, | ||
| 72 | - }, | ||
| 73 | - { | ||
| 74 | - field: FormField.IDENTIFIER, | ||
| 75 | - label: '标识符', | ||
| 76 | - required: true, | ||
| 77 | - component: 'Input', | ||
| 78 | - colProps: { | ||
| 79 | - span: 18, | ||
| 80 | - }, | ||
| 81 | - componentProps: { | ||
| 82 | - maxLength: 128, | ||
| 83 | - placeholder: '请输入标识符', | ||
| 84 | - }, | ||
| 85 | - dynamicRules: ({ values }) => { | ||
| 86 | - return [ | ||
| 87 | - { required: true, message: '请输入标识符' }, | ||
| 88 | - ...validateExcludeComma(values[FormField.IDENTIFIER], '标识符'), | ||
| 89 | - ]; | ||
| 90 | - }, | ||
| 91 | - }, | ||
| 92 | - { | ||
| 93 | - field: FormField.HAS_STRUCT_FROM, | ||
| 94 | - label: '是否已存在结构体', | ||
| 95 | - component: 'Input', | ||
| 96 | - ifShow: false, | ||
| 97 | - }, | ||
| 98 | - { | ||
| 99 | - field: FormField.TYPE, | ||
| 100 | - label: '数据类型', | ||
| 101 | - required: true, | ||
| 102 | - component: 'ApiSelect', | ||
| 103 | - colProps: { | ||
| 104 | - span: 9, | ||
| 105 | - }, | ||
| 106 | - defaultValue: 'INT', | ||
| 107 | - componentProps: ({ formActionType }) => { | ||
| 108 | - const { setFieldsValue } = formActionType; | ||
| 109 | - return { | ||
| 110 | - placeholder: '请选择数据类型', | ||
| 111 | - api: async (params: Recordable) => { | ||
| 112 | - try { | ||
| 113 | - const record = await findDictItemByCode(params); | ||
| 114 | - | ||
| 115 | - if (isTcp) { | ||
| 116 | - // TCP 产品 属性可创建范围 | ||
| 117 | - return record.filter((item) => | ||
| 118 | - [ | ||
| 119 | - DataTypeEnum.BOOL, | ||
| 120 | - DataTypeEnum.NUMBER_DOUBLE, | ||
| 121 | - DataTypeEnum.NUMBER_INT, | ||
| 122 | - DataTypeEnum.STRING, | ||
| 123 | - ].includes(item.itemValue as DataTypeEnum) | ||
| 124 | - ); | ||
| 125 | - } | ||
| 126 | - | ||
| 127 | - return hasStructForm | ||
| 128 | - ? record.filter((item) => item.itemValue !== DataTypeEnum.STRUCT) | ||
| 129 | - : record; | ||
| 130 | - } catch (error) { | ||
| 131 | - return []; | ||
| 132 | - } | ||
| 133 | - }, | ||
| 134 | - params: { | ||
| 135 | - dictCode: 'data_type', | ||
| 136 | - }, | ||
| 137 | - labelField: 'itemText', | ||
| 138 | - valueField: 'itemValue', | ||
| 139 | - getPopupContainer: () => document.body, | ||
| 140 | - onChange: (value: string) => { | ||
| 141 | - if (value == DataTypeEnum.STRUCT) { | ||
| 142 | - setFieldsValue({ [FormField.SPECS_LIST]: [], [FormField.HAS_STRUCT_FROM]: true }); | ||
| 143 | - } | ||
| 144 | - }, | ||
| 145 | - }; | ||
| 146 | - }, | ||
| 147 | - }, | ||
| 148 | - { | ||
| 149 | - field: FormField.ENUM_LIST, | ||
| 150 | - component: 'Input', | ||
| 151 | - label: '枚举', | ||
| 152 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.ENUM, | ||
| 153 | - slot: 'EnumList', | ||
| 154 | - colProps: { | ||
| 155 | - span: 24, | ||
| 156 | - }, | ||
| 157 | - }, | ||
| 158 | - { | ||
| 159 | - field: FormField.VALUE_RANGE, | ||
| 160 | - label: '取值范围', | ||
| 161 | - component: 'CustomMinMaxInput', | ||
| 162 | - valueField: 'value', | ||
| 163 | - changeEvent: 'update:value', | ||
| 164 | - colProps: { | ||
| 165 | - span: 18, | ||
| 166 | - }, | ||
| 167 | - ifShow: ({ values }) => | ||
| 168 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 169 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
| 170 | - rules: [{ validator: validateValueRange }], | ||
| 171 | - }, | ||
| 172 | - { | ||
| 173 | - field: FormField.STEP, | ||
| 174 | - label: '步长', | ||
| 175 | - component: 'InputNumber', | ||
| 176 | - colProps: { | ||
| 177 | - span: 18, | ||
| 178 | - }, | ||
| 179 | - componentProps: { | ||
| 180 | - maxLength: 255, | ||
| 181 | - placeholder: '请输入步长', | ||
| 182 | - min: 1, | ||
| 183 | - formatter: (value: number | string) => { | ||
| 184 | - return value ? Math.floor(Number(value)) : value; | ||
| 185 | - }, | ||
| 186 | - }, | ||
| 187 | - ifShow: ({ values }) => | ||
| 188 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 189 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
| 190 | - dynamicRules: ({ model }) => { | ||
| 191 | - const valueRange = model[FormField.VALUE_RANGE] || {}; | ||
| 192 | - const { min, max } = valueRange; | ||
| 193 | - const step = model[FormField.STEP]; | ||
| 194 | - return [ | ||
| 195 | - { | ||
| 196 | - validator: () => { | ||
| 197 | - if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
| 198 | - if (step > max - min) { | ||
| 199 | - return Promise.reject('步长不能大于取值范围的差值'); | ||
| 200 | - } | ||
| 201 | - return Promise.resolve(); | ||
| 202 | - }, | ||
| 203 | - }, | ||
| 204 | - ]; | ||
| 205 | - }, | ||
| 206 | - }, | ||
| 207 | - { | ||
| 208 | - field: FormField.UNIT_NAME, | ||
| 209 | - label: '单位名称', | ||
| 210 | - component: 'Input', | ||
| 211 | - show: false, | ||
| 212 | - }, | ||
| 213 | - { | ||
| 214 | - field: FormField.UNIT, | ||
| 215 | - label: '单位', | ||
| 216 | - component: 'ApiSelect', | ||
| 217 | - colProps: { | ||
| 218 | - span: 9, | ||
| 219 | - }, | ||
| 220 | - componentProps: ({ formActionType }) => { | ||
| 221 | - const { setFieldsValue } = formActionType; | ||
| 222 | - return { | ||
| 223 | - placeholder: '请选择单位', | ||
| 224 | - api: async (params) => { | ||
| 225 | - const list = await findDictItemByCode(params); | ||
| 226 | - list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
| 227 | - return list; | ||
| 228 | - }, | ||
| 229 | - params: { | ||
| 230 | - dictCode: 'attribute_unit', | ||
| 231 | - }, | ||
| 232 | - labelInValue: true, | ||
| 233 | - labelField: 'itemText', | ||
| 234 | - valueField: 'itemValue', | ||
| 235 | - onChange(_, record: Record<'label' | 'value', string>) { | ||
| 236 | - if (record) { | ||
| 237 | - const { label } = record; | ||
| 238 | - setFieldsValue({ [FormField.UNIT_NAME]: label }); | ||
| 239 | - } | ||
| 240 | - }, | ||
| 241 | - getPopupContainer: () => document.body, | ||
| 242 | - showSearch: true, | ||
| 243 | - filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
| 244 | - let { label, value } = option; | ||
| 245 | - label = label.toLowerCase(); | ||
| 246 | - value = value.toLowerCase(); | ||
| 247 | - inputValue = inputValue.toLowerCase(); | ||
| 248 | - return label.includes(inputValue) || value.includes(inputValue); | ||
| 249 | - }, | ||
| 250 | - }; | ||
| 251 | - }, | ||
| 252 | - ifShow: ({ values }) => | ||
| 253 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 254 | - values[FormField.TYPE] === DataTypeEnum.NUMBER_DOUBLE, | ||
| 255 | - }, | ||
| 256 | - { | ||
| 257 | - field: FormField.BOOL_CLOSE, | ||
| 258 | - component: 'Input', | ||
| 259 | - required: true, | ||
| 260 | - label: '0 -', | ||
| 261 | - colProps: { | ||
| 262 | - span: 18, | ||
| 263 | - }, | ||
| 264 | - componentProps: { | ||
| 265 | - placeholder: '如:关', | ||
| 266 | - }, | ||
| 267 | - defaultValue: '关', | ||
| 268 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | ||
| 269 | - dynamicRules: ({ model }) => { | ||
| 270 | - const close = model[FormField.BOOL_CLOSE]; | ||
| 271 | - const open = model[FormField.BOOL_OPEN]; | ||
| 272 | - return [ | ||
| 273 | - { | ||
| 274 | - required: true, | ||
| 275 | - }, | ||
| 276 | - { | ||
| 277 | - validator() { | ||
| 278 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 279 | - return Promise.resolve(); | ||
| 280 | - }, | ||
| 281 | - }, | ||
| 282 | - ]; | ||
| 283 | - }, | ||
| 284 | - }, | ||
| 285 | - { | ||
| 286 | - field: FormField.BOOL_OPEN, | ||
| 287 | - component: 'Input', | ||
| 288 | - required: true, | ||
| 289 | - label: '1 -', | ||
| 290 | - colProps: { | ||
| 291 | - span: 18, | ||
| 292 | - }, | ||
| 293 | - componentProps: { | ||
| 294 | - placeholder: '如:开', | ||
| 295 | - }, | ||
| 296 | - defaultValue: '开', | ||
| 297 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.BOOL, | ||
| 298 | - dynamicRules: ({ model }) => { | ||
| 299 | - const close = model[FormField.BOOL_CLOSE]; | ||
| 300 | - const open = model[FormField.BOOL_OPEN]; | ||
| 301 | - return [ | ||
| 302 | - { | ||
| 303 | - required: true, | ||
| 304 | - }, | ||
| 305 | - { | ||
| 306 | - validator() { | ||
| 307 | - if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 308 | - return Promise.resolve(); | ||
| 309 | - }, | ||
| 310 | - }, | ||
| 311 | - ]; | ||
| 312 | - }, | ||
| 313 | - }, | ||
| 314 | - { | ||
| 315 | - field: FormField.LENGTH, | ||
| 316 | - component: 'Input', | ||
| 317 | - required: true, | ||
| 318 | - label: '数据长度', | ||
| 319 | - defaultValue: '10240', | ||
| 320 | - colProps: { | ||
| 321 | - span: 8, | ||
| 322 | - }, | ||
| 323 | - componentProps: { | ||
| 324 | - placeholder: '请输入数据长度', | ||
| 325 | - }, | ||
| 326 | - renderComponentContent: () => { | ||
| 327 | - return { | ||
| 328 | - suffix: () => '字节', | ||
| 329 | - }; | ||
| 330 | - }, | ||
| 331 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRING, | ||
| 332 | - }, | ||
| 333 | - { | ||
| 334 | - field: FormField.EXTENSION_DESC, | ||
| 335 | - component: 'ExtendDesc', | ||
| 336 | - label: '扩展描述', | ||
| 337 | - valueField: 'value', | ||
| 338 | - changeEvent: 'update:value', | ||
| 339 | - ifShow: isTcp, | ||
| 340 | - colProps: { | ||
| 341 | - span: 16, | ||
| 342 | - }, | ||
| 343 | - componentProps: ({ formModel }) => { | ||
| 344 | - return { | ||
| 345 | - dataType: formModel[FormField.TYPE], | ||
| 346 | - }; | ||
| 347 | - }, | ||
| 348 | - }, | ||
| 349 | - { | ||
| 350 | - field: FormField.ACCESS_MODE, | ||
| 351 | - component: 'ApiRadioGroup', | ||
| 352 | - label: '读写类型', | ||
| 353 | - required: true, | ||
| 354 | - colProps: { | ||
| 355 | - span: 24, | ||
| 356 | - }, | ||
| 357 | - ifShow: () => !hiddenAccessMode && !hasStructForm, | ||
| 358 | - defaultValue: 'r', | ||
| 359 | - componentProps: { | ||
| 360 | - placeholder: '请选择读写类型', | ||
| 361 | - api: findDictItemByCode, | ||
| 362 | - params: { | ||
| 363 | - dictCode: 'read_write_type', | ||
| 364 | - }, | ||
| 365 | - labelField: 'itemText', | ||
| 366 | - valueField: 'itemValue', | ||
| 367 | - }, | ||
| 368 | - }, | ||
| 369 | - { | ||
| 370 | - field: FormField.SPECS_LIST, | ||
| 371 | - label: 'JSON对象', | ||
| 372 | - component: 'StructForm', | ||
| 373 | - valueField: 'value', | ||
| 374 | - changeEvent: 'update:value', | ||
| 375 | - colProps: { span: 24 }, | ||
| 376 | - ifShow: ({ values }) => values[FormField.TYPE] === DataTypeEnum.STRUCT, | ||
| 377 | - rules: [{ required: true, validator: validateJSON }], | ||
| 378 | - componentProps: ({ formModel }) => { | ||
| 379 | - return { | ||
| 380 | - hasStructForm: formModel[FormField.HAS_STRUCT_FROM], | ||
| 381 | - }; | ||
| 382 | - }, | ||
| 383 | - }, | ||
| 384 | - { | ||
| 385 | - field: FormField.REFARK, | ||
| 386 | - label: '备注', | ||
| 387 | - component: 'InputTextArea', | ||
| 388 | - componentProps: { | ||
| 389 | - rows: 4, | ||
| 390 | - maxLength: 100, | ||
| 391 | - placeholder: '请输入描述', | ||
| 392 | - }, | ||
| 393 | - }, | ||
| 394 | - ]; | ||
| 395 | -}; |
src/components/Form/src/externalCompns/components/StructForm/type.ts
deleted
100644 → 0
| 1 | -import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 2 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 3 | -import { FormField } from '/@/views/device/profiles/step/cpns/physical/cpns/config'; | ||
| 4 | - | ||
| 5 | -export enum OpenModalMode { | ||
| 6 | - CREATE = 'create', | ||
| 7 | - UPDATE = 'update', | ||
| 8 | -} | ||
| 9 | - | ||
| 10 | -export interface OpenModalParams { | ||
| 11 | - mode: OpenModalMode; | ||
| 12 | - record?: StructRecord; | ||
| 13 | -} | ||
| 14 | - | ||
| 15 | -export interface StructFormValue | ||
| 16 | - extends Partial<Record<Exclude<FormField, FormField.VALUE_RANGE | FormField.STRUCT>, string>> { | ||
| 17 | - [FormField.TYPE]: DataTypeEnum; | ||
| 18 | - [FormField.VALUE_RANGE]?: { | ||
| 19 | - [FormField.MIN]: string; | ||
| 20 | - [FormField.MAX]: string; | ||
| 21 | - }; | ||
| 22 | - [FormField.STRUCT]: StructRecord[]; | ||
| 23 | -} | ||
| 24 | - | ||
| 25 | -export interface StructRecord extends StructJSON { | ||
| 26 | - id: string; | ||
| 27 | -} |
src/components/Form/src/externalCompns/components/StructForm/util.ts
deleted
100644 → 0
| 1 | -import { cloneDeep } from 'lodash-es'; | ||
| 2 | -import { StructFormValue } from './type'; | ||
| 3 | -import { | ||
| 4 | - DataType, | ||
| 5 | - ModelOfMatterParams, | ||
| 6 | - Specs, | ||
| 7 | - StructJSON, | ||
| 8 | -} from '/@/api/device/model/modelOfMatterModel'; | ||
| 9 | -import { isArray } from '/@/utils/is'; | ||
| 10 | -import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 11 | - | ||
| 12 | -export function transfromToStructJSON(value: StructFormValue, enumList: Specs[] = []): StructJSON { | ||
| 13 | - const { | ||
| 14 | - type, | ||
| 15 | - valueRange, | ||
| 16 | - step, | ||
| 17 | - length = '', | ||
| 18 | - boolClose, | ||
| 19 | - boolOpen, | ||
| 20 | - unit, | ||
| 21 | - unitName, | ||
| 22 | - functionName, | ||
| 23 | - identifier, | ||
| 24 | - remark, | ||
| 25 | - specs, | ||
| 26 | - accessMode, | ||
| 27 | - } = value; | ||
| 28 | - const basic = { functionName, identifier, remark, accessMode }; | ||
| 29 | - let dataType = {} as unknown as DataType; | ||
| 30 | - | ||
| 31 | - switch (type) { | ||
| 32 | - case DataTypeEnum.NUMBER_INT: | ||
| 33 | - dataType = { | ||
| 34 | - type, | ||
| 35 | - specs: { valueRange, step, unit, unitName }, | ||
| 36 | - }; | ||
| 37 | - break; | ||
| 38 | - | ||
| 39 | - case DataTypeEnum.NUMBER_DOUBLE: | ||
| 40 | - dataType = { | ||
| 41 | - type, | ||
| 42 | - specs: { valueRange, step, unit, unitName }, | ||
| 43 | - }; | ||
| 44 | - break; | ||
| 45 | - | ||
| 46 | - case DataTypeEnum.BOOL: | ||
| 47 | - dataType = { | ||
| 48 | - type, | ||
| 49 | - specs: { | ||
| 50 | - boolOpen: boolOpen, | ||
| 51 | - boolClose: boolClose, | ||
| 52 | - }, | ||
| 53 | - }; | ||
| 54 | - break; | ||
| 55 | - | ||
| 56 | - case DataTypeEnum.STRING: | ||
| 57 | - dataType = { | ||
| 58 | - type, | ||
| 59 | - specs: { length }, | ||
| 60 | - }; | ||
| 61 | - break; | ||
| 62 | - | ||
| 63 | - case DataTypeEnum.ENUM: | ||
| 64 | - dataType = { | ||
| 65 | - type, | ||
| 66 | - specsList: enumList, | ||
| 67 | - }; | ||
| 68 | - break; | ||
| 69 | - | ||
| 70 | - case DataTypeEnum.STRUCT: | ||
| 71 | - dataType = { | ||
| 72 | - type, | ||
| 73 | - specs: specs! as unknown as ModelOfMatterParams[], | ||
| 74 | - }; | ||
| 75 | - break; | ||
| 76 | - } | ||
| 77 | - return { ...basic, dataType } as StructJSON; | ||
| 78 | -} | ||
| 79 | - | ||
| 80 | -export const excludeIdInStructJSON = (struct: DataType) => { | ||
| 81 | - const _value = cloneDeep(struct); | ||
| 82 | - const { specs } = _value; | ||
| 83 | - if (!specs) return _value; | ||
| 84 | - const list = [specs]; | ||
| 85 | - | ||
| 86 | - while (list.length) { | ||
| 87 | - for (const item of list) { | ||
| 88 | - if (isArray(item)) { | ||
| 89 | - (item as StructJSON[]).forEach((temp) => { | ||
| 90 | - if (temp.dataType?.specs) { | ||
| 91 | - list.push(temp.dataType.specs); | ||
| 92 | - } | ||
| 93 | - Reflect.has(temp, 'id') && Reflect.deleteProperty(temp, 'id'); | ||
| 94 | - }); | ||
| 95 | - } else { | ||
| 96 | - Reflect.has(item as Recordable, 'id') && Reflect.deleteProperty(item as Recordable, 'id'); | ||
| 97 | - } | ||
| 98 | - list.shift(); | ||
| 99 | - } | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - return _value; | ||
| 103 | -}; |
| @@ -117,12 +117,12 @@ export type ComponentType = | @@ -117,12 +117,12 @@ export type ComponentType = | ||
| 117 | | 'ColorPicker' | 117 | | 'ColorPicker' |
| 118 | | 'IconDrawer' | 118 | | 'IconDrawer' |
| 119 | | 'ApiUpload' | 119 | | 'ApiUpload' |
| 120 | + | 'ApiCascader' | ||
| 120 | | 'ApiSearchSelect' | 121 | | 'ApiSearchSelect' |
| 121 | | 'StructForm' | 122 | | 'StructForm' |
| 122 | | 'ApiSelectScrollLoad' | 123 | | 'ApiSelectScrollLoad' |
| 123 | | 'TransferModal' | 124 | | 'TransferModal' |
| 124 | | 'TransferTableModal' | 125 | | 'TransferTableModal' |
| 125 | - | 'ObjectModelValidateForm' | ||
| 126 | | 'ThingsModelForm' | 126 | | 'ThingsModelForm' |
| 127 | | 'DevicePicker' | 127 | | 'DevicePicker' |
| 128 | | 'ProductPicker' | 128 | | 'ProductPicker' |
| @@ -145,4 +145,6 @@ export type ComponentType = | @@ -145,4 +145,6 @@ export type ComponentType = | ||
| 145 | | 'TriggerDurationInput' | 145 | | 'TriggerDurationInput' |
| 146 | | 'AlarmProfileSelect' | 146 | | 'AlarmProfileSelect' |
| 147 | | 'LockControlGroup' | 147 | | 'LockControlGroup' |
| 148 | - | 'EnumList'; | 148 | + | 'EnumList' |
| 149 | + | 'Segmented' | ||
| 150 | + | 'StructFormItem'; |
src/enums/deviceEnum.ts
0 → 100644
| 1 | +export enum TransportTypeEnum { | ||
| 2 | + DEFAULT = 'DEFAULT', | ||
| 3 | + MQTT = 'MQTT', | ||
| 4 | + COAP = 'COAP', | ||
| 5 | + LWM2M = 'LWM2M', | ||
| 6 | + SNMP = 'SNMP', | ||
| 7 | + TCP = 'TCP', | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +export enum ReadAndWriteEnum { | ||
| 11 | + READ = 'r', | ||
| 12 | + READ_AND_WRITE = 'rw', | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +export enum ServiceCallTypeEnum { | ||
| 16 | + ASYNC = 'ASYNC', | ||
| 17 | + SYNC = 'SYNC', | ||
| 18 | +} | ||
| 19 | + | ||
| 20 | +export enum ServiceCallTypeNameEnum { | ||
| 21 | + ASYNC = '异步', | ||
| 22 | + SYNC = '同步', | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +export enum CommandDeliveryWayEnum { | ||
| 26 | + ONE_WAY = 'oneway', | ||
| 27 | + TWO_WAY = 'twoway', | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +export enum CommandDeliveryWayNameEnum { | ||
| 31 | + ONE_WAY = '单向', | ||
| 32 | + TWO_WAY = '双向', | ||
| 33 | +} | ||
| 34 | + | ||
| 35 | +export enum CommandTypeEnum { | ||
| 36 | + CUSTOM = 0, | ||
| 37 | + SERVICE = 1, | ||
| 38 | + ATTRIBUTE = 2, | ||
| 39 | + API = 'api', | ||
| 40 | +} | ||
| 41 | + | ||
| 42 | +export enum CommandTypeNameEnum { | ||
| 43 | + CUSTOM = '自定义', | ||
| 44 | + SERVICE = '服务', | ||
| 45 | + ATTRIBUTE = '属性', | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +export enum RPCCommandMethodEnum { | ||
| 49 | + THINGSKIT = 'methodThingskit', | ||
| 50 | +} |
| 1 | export enum DictEnum { | 1 | export enum DictEnum { |
| 2 | + // 物模型数据类型 | ||
| 3 | + DATA_TYPE = 'data_type', | ||
| 4 | + | ||
| 5 | + // 事件类型 | ||
| 6 | + EVENT_TYPE = 'event_type', | ||
| 7 | + // 物模型单位 | ||
| 8 | + ATTRIBUTE_UNIT = 'attribute_unit', | ||
| 9 | + // 物模型读写类型 | ||
| 10 | + READ_WRITE_TYP = 'read_write_type', | ||
| 2 | // 从机地址 | 11 | // 从机地址 |
| 3 | SLAVE_ADDRESS = 'slave_address', | 12 | SLAVE_ADDRESS = 'slave_address', |
| 4 | // 功能码 | 13 | // 功能码 |
| @@ -154,16 +154,3 @@ export enum ExecutionActionNameEnum { | @@ -154,16 +154,3 @@ export enum ExecutionActionNameEnum { | ||
| 154 | DEVICE_OUT = '设备输出', | 154 | DEVICE_OUT = '设备输出', |
| 155 | MSG_NOTIFY = '告警输出', | 155 | MSG_NOTIFY = '告警输出', |
| 156 | } | 156 | } |
| 157 | - | ||
| 158 | -export enum CommandTypeEnum { | ||
| 159 | - CUSTOM = 0, | ||
| 160 | - SERVICE = 1, | ||
| 161 | - ATTRIBUTE = 2, | ||
| 162 | - API = 'api', | ||
| 163 | -} | ||
| 164 | - | ||
| 165 | -export enum CommandTypeNameEnum { | ||
| 166 | - CUSTOM = '自定义', | ||
| 167 | - SERVICE = '服务', | ||
| 168 | - ATTRIBUTE = '属性', | ||
| 169 | -} |
| @@ -7,6 +7,30 @@ export enum DataTypeEnum { | @@ -7,6 +7,30 @@ export enum DataTypeEnum { | ||
| 7 | ENUM = 'ENUM', | 7 | ENUM = 'ENUM', |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | +export enum FunctionTypeEnum { | ||
| 11 | + PROPERTIES = 'properties', | ||
| 12 | + SERVICE = 'services', | ||
| 13 | + EVENTS = 'events', | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +export enum FunctionTypeNameEnum { | ||
| 17 | + PROPERTIES = '属性', | ||
| 18 | + SERVICE = '服务', | ||
| 19 | + EVENTS = '事件', | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +export enum ObjectEventTypeEnum { | ||
| 23 | + INFO = 'INFO', | ||
| 24 | + ALERT = 'ALERT', | ||
| 25 | + ERROR = 'ERROR', | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +export enum ObjectEventTypeNameEnum { | ||
| 29 | + INFO = '信息', | ||
| 30 | + ALERT = '告警', | ||
| 31 | + ERROR = '故障', | ||
| 32 | +} | ||
| 33 | + | ||
| 10 | export enum RegisterDataTypeEnum { | 34 | export enum RegisterDataTypeEnum { |
| 11 | UN_SHORT = 'unshort', | 35 | UN_SHORT = 'unshort', |
| 12 | } | 36 | } |
| @@ -26,3 +50,12 @@ export enum RegisterActionTypeNameEnum { | @@ -26,3 +50,12 @@ export enum RegisterActionTypeNameEnum { | ||
| 26 | INT = '06写入单个保持寄存器', | 50 | INT = '06写入单个保持寄存器', |
| 27 | DOUBLE = '16写入多个保持寄存器', | 51 | DOUBLE = '16写入多个保持寄存器', |
| 28 | } | 52 | } |
| 53 | + | ||
| 54 | +export enum ObjectModelAccessModeEnum { | ||
| 55 | + READ = 'r', | ||
| 56 | + READ_AND_WRITE = 'rw', | ||
| 57 | +} | ||
| 58 | + | ||
| 59 | +export enum ModbusCRCEnum { | ||
| 60 | + CRC_16_LOWER = 'CRC_16_LOWER', | ||
| 61 | +} |
| @@ -29,23 +29,3 @@ export enum BooleanStringEnum { | @@ -29,23 +29,3 @@ export enum BooleanStringEnum { | ||
| 29 | TRUE = 'true', | 29 | TRUE = 'true', |
| 30 | FALSE = 'false', | 30 | FALSE = 'false', |
| 31 | } | 31 | } |
| 32 | - | ||
| 33 | -export enum ReadAndWriteEnum { | ||
| 34 | - READ = 'r', | ||
| 35 | - READ_AND_WRITE = 'rw', | ||
| 36 | -} | ||
| 37 | - | ||
| 38 | -export enum ServiceCallTypeEnum { | ||
| 39 | - ASYNC = 'ASYNC', | ||
| 40 | - SYNC = 'SYNC', | ||
| 41 | -} | ||
| 42 | - | ||
| 43 | -export enum ServiceCallTypeNameEnum { | ||
| 44 | - ASYNC = '异步', | ||
| 45 | - SYNC = '同步', | ||
| 46 | -} | ||
| 47 | - | ||
| 48 | -export enum CommandDeliveryWayEnum { | ||
| 49 | - ONE_WAY = 'oneway', | ||
| 50 | - TWO_WAY = 'twoway', | ||
| 51 | -} |
| @@ -6,6 +6,7 @@ | @@ -6,6 +6,7 @@ | ||
| 6 | :title="getTitle" | 6 | :title="getTitle" |
| 7 | width="30%" | 7 | width="30%" |
| 8 | @ok="handleSubmit" | 8 | @ok="handleSubmit" |
| 9 | + wrapClassName="camera-configration-drawer" | ||
| 9 | > | 10 | > |
| 10 | <BasicForm @register="registerForm"> | 11 | <BasicForm @register="registerForm"> |
| 11 | <template #videoPlatformIdSlot="{ model, field }"> | 12 | <template #videoPlatformIdSlot="{ model, field }"> |
| @@ -125,7 +126,11 @@ | @@ -125,7 +126,11 @@ | ||
| 125 | }); | 126 | }); |
| 126 | } | 127 | } |
| 127 | 128 | ||
| 128 | - setFieldsValue({ ...record, videoType: record.videoPlatformDTO?.type }); | 129 | + setFieldsValue({ |
| 130 | + ...record, | ||
| 131 | + videoType: record.videoPlatformDTO?.type, | ||
| 132 | + ...(record?.params || {}), | ||
| 133 | + }); | ||
| 129 | } else { | 134 | } else { |
| 130 | editId.value = ''; | 135 | editId.value = ''; |
| 131 | } | 136 | } |
| @@ -157,6 +162,7 @@ | @@ -157,6 +162,7 @@ | ||
| 157 | ) { | 162 | ) { |
| 158 | values.streamType = values.articulation; | 163 | values.streamType = values.articulation; |
| 159 | values.playProtocol = values.videoFormat; | 164 | values.playProtocol = values.videoFormat; |
| 165 | + values.params = values.channelNo ? { channelNo: values.channelNo } : null; | ||
| 160 | } | 166 | } |
| 161 | 167 | ||
| 162 | await createOrEditCameraManage(values); | 168 | await createOrEditCameraManage(values); |
| @@ -190,3 +196,11 @@ | @@ -190,3 +196,11 @@ | ||
| 190 | }, | 196 | }, |
| 191 | }); | 197 | }); |
| 192 | </script> | 198 | </script> |
| 199 | + | ||
| 200 | +<style lang="less"> | ||
| 201 | + .camera-configration-drawer { | ||
| 202 | + .ant-input-number { | ||
| 203 | + width: 100% !important; | ||
| 204 | + } | ||
| 205 | + } | ||
| 206 | +</style> |
| @@ -21,11 +21,6 @@ export enum CameraPermission { | @@ -21,11 +21,6 @@ export enum CameraPermission { | ||
| 21 | DELETE = 'api:yt:video:delete', | 21 | DELETE = 'api:yt:video:delete', |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | -export enum VideoPlatformEnum { | ||
| 25 | - HAIKANG = 0, | ||
| 26 | - FLUORITE = 1, | ||
| 27 | -} | ||
| 28 | - | ||
| 29 | export enum AccessMode { | 24 | export enum AccessMode { |
| 30 | ManuallyEnter = 0, | 25 | ManuallyEnter = 0, |
| 31 | Streaming = 1, | 26 | Streaming = 1, |
| @@ -250,16 +245,28 @@ export const formSchema: QFormSchema[] = [ | @@ -250,16 +245,28 @@ export const formSchema: QFormSchema[] = [ | ||
| 250 | required: true, | 245 | required: true, |
| 251 | defaultValue: VideoPlatformEnum.SCI, | 246 | defaultValue: VideoPlatformEnum.SCI, |
| 252 | ifShow: ({ values }) => values.accessMode === AccessMode.Streaming, | 247 | ifShow: ({ values }) => values.accessMode === AccessMode.Streaming, |
| 253 | - componentProps: { | ||
| 254 | - api: async (params) => { | ||
| 255 | - const values = await findDictItemByCode(params); | ||
| 256 | - return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | ||
| 257 | - }, | ||
| 258 | - params: { | ||
| 259 | - dictCode: DictEnum.STREAMING_MEDIA_TYPE, | ||
| 260 | - }, | ||
| 261 | - getPopupContainer: () => document.body, | ||
| 262 | - placeholder: `请选择平台类型`, | 248 | + componentProps: ({ formActionType }) => { |
| 249 | + return { | ||
| 250 | + api: async (params) => { | ||
| 251 | + const values = await findDictItemByCode(params); | ||
| 252 | + return values.map((item) => ({ label: item.itemText, value: Number(item.itemValue) })); | ||
| 253 | + }, | ||
| 254 | + params: { | ||
| 255 | + dictCode: DictEnum.STREAMING_MEDIA_TYPE, | ||
| 256 | + }, | ||
| 257 | + getPopupContainer: () => document.body, | ||
| 258 | + placeholder: `请选择平台类型`, | ||
| 259 | + onChange() { | ||
| 260 | + const { setFieldsValue } = formActionType; | ||
| 261 | + setFieldsValue({ | ||
| 262 | + articulation: ArticulationEnumType.HIGH_DEFINITION, | ||
| 263 | + playProtocol: PlayProtocol.HTTP, | ||
| 264 | + sn: null, | ||
| 265 | + channelNo: null, | ||
| 266 | + videoPlatformId: null, | ||
| 267 | + }); | ||
| 268 | + }, | ||
| 269 | + }; | ||
| 263 | }, | 270 | }, |
| 264 | }, | 271 | }, |
| 265 | { | 272 | { |
| @@ -384,4 +391,22 @@ export const formSchema: QFormSchema[] = [ | @@ -384,4 +391,22 @@ export const formSchema: QFormSchema[] = [ | ||
| 384 | placeholder: '请输入监控点编号', | 391 | placeholder: '请输入监控点编号', |
| 385 | }, | 392 | }, |
| 386 | }, | 393 | }, |
| 394 | + { | ||
| 395 | + field: 'channelNo', | ||
| 396 | + label: '通道号', | ||
| 397 | + component: 'InputNumber', | ||
| 398 | + ifShow({ values }) { | ||
| 399 | + return ( | ||
| 400 | + values.accessMode === AccessMode.Streaming && | ||
| 401 | + values.videoType === VideoPlatformEnum.FLUORITE | ||
| 402 | + ); | ||
| 403 | + }, | ||
| 404 | + componentProps: { | ||
| 405 | + min: 1, | ||
| 406 | + max: 10000, | ||
| 407 | + step: 1, | ||
| 408 | + placeholder: '请输入通道号', | ||
| 409 | + precision: 0, | ||
| 410 | + }, | ||
| 411 | + }, | ||
| 387 | ]; | 412 | ]; |
| 1 | -import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form'; | 1 | +import { FormSchema, useComponentRegister } from '/@/components/Form'; |
| 2 | import { findDictItemByCode } from '/@/api/system/dict'; | 2 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 3 | import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager'; | 3 | import { getGatewayDevice, queryDeviceProfileBy } from '/@/api/device/deviceManager'; |
| 4 | -import { TransportTypeEnum } from '../../profiles/components/TransportDescript/const'; | ||
| 5 | -import { JSONEditorValidator } from '/@/components/CodeEditor/src/JSONEditor'; | ||
| 6 | import { JSONEditor } from '/@/components/CodeEditor'; | 4 | import { JSONEditor } from '/@/components/CodeEditor'; |
| 7 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | 5 | import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; |
| 8 | -import { getModelServices } from '/@/api/device/modelOfMatter'; | ||
| 9 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
| 10 | -import { h, toRaw, unref } from 'vue'; | ||
| 11 | -import ObjectModelValidateForm from '/@/components/Form/src/externalCompns/components/ObjectModelValidateForm/ObjectModelValidateForm.vue'; | ||
| 12 | -import { CommandDeliveryWayEnum, ServiceCallTypeEnum } from '/@/enums/toolEnum'; | 6 | +import { h } from 'vue'; |
| 13 | import { TaskTypeEnum } from '/@/views/task/center/config'; | 7 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 14 | import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; | 8 | import { AddressTypeEnum } from '/@/views/task/center/components/PollCommandInput'; |
| 15 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 9 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
| @@ -17,9 +11,9 @@ import { createImgPreview } from '/@/components/Preview'; | @@ -17,9 +11,9 @@ import { createImgPreview } from '/@/components/Preview'; | ||
| 17 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | 11 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
| 18 | import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; | 12 | import LockControlGroup from '/@/components/Form/src/components/LockControlGroup.vue'; |
| 19 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | 13 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 14 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 20 | 15 | ||
| 21 | useComponentRegister('JSONEditor', JSONEditor); | 16 | useComponentRegister('JSONEditor', JSONEditor); |
| 22 | -useComponentRegister('ObjectModelValidateForm', ObjectModelValidateForm); | ||
| 23 | useComponentRegister('LockControlGroup', LockControlGroup); | 17 | useComponentRegister('LockControlGroup', LockControlGroup); |
| 24 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 18 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 25 | 19 | ||
| @@ -910,214 +904,3 @@ export const TokenSchemas: FormSchema[] = [ | @@ -910,214 +904,3 @@ export const TokenSchemas: FormSchema[] = [ | ||
| 910 | }, | 904 | }, |
| 911 | }, | 905 | }, |
| 912 | ]; | 906 | ]; |
| 913 | - | ||
| 914 | -export enum ValueType { | ||
| 915 | - JSON = 'json', | ||
| 916 | - STRING = 'string', | ||
| 917 | -} | ||
| 918 | - | ||
| 919 | -export enum CommandFieldsEnum { | ||
| 920 | - COMMAND_TYPE = 'commandType', | ||
| 921 | - VALUE_TYPE = 'valueType', | ||
| 922 | - COMMAND_TEXT = 'commandText', | ||
| 923 | - COMAND_VALUE = 'commandValue', | ||
| 924 | - SERVICE = 'service', | ||
| 925 | - SERVICE_TYPE = 'service_type', | ||
| 926 | - TCP_SERVICE = 'tcpService', | ||
| 927 | - MODEL_INPUT = 'modelInput', | ||
| 928 | - CUSTOM_TYPE = 'customType', | ||
| 929 | -} | ||
| 930 | - | ||
| 931 | -export enum CommandType { | ||
| 932 | - CUSTOM = 'custom', | ||
| 933 | - SERVICE = 'service', | ||
| 934 | -} | ||
| 935 | - | ||
| 936 | -export const CommandSchemas = ( | ||
| 937 | - transportType: TransportTypeEnum, | ||
| 938 | - deviceProfileId: string | ||
| 939 | -): FormSchema[] => { | ||
| 940 | - return [ | ||
| 941 | - { | ||
| 942 | - field: CommandFieldsEnum.COMMAND_TYPE, | ||
| 943 | - component: 'RadioGroup', | ||
| 944 | - label: '下发类型', | ||
| 945 | - defaultValue: CommandType.CUSTOM, | ||
| 946 | - componentProps: ({ formActionType }) => { | ||
| 947 | - const { setFieldsValue } = formActionType; | ||
| 948 | - return { | ||
| 949 | - options: [ | ||
| 950 | - { label: '自定义', value: CommandType.CUSTOM }, | ||
| 951 | - { label: '服务', value: CommandType.SERVICE }, | ||
| 952 | - ], | ||
| 953 | - onChange() { | ||
| 954 | - setFieldsValue({ | ||
| 955 | - [CommandFieldsEnum.SERVICE]: null, | ||
| 956 | - [CommandFieldsEnum.MODEL_INPUT]: null, | ||
| 957 | - [CommandFieldsEnum.COMAND_VALUE]: null, | ||
| 958 | - [CommandFieldsEnum.COMMAND_TEXT]: null, | ||
| 959 | - }); | ||
| 960 | - }, | ||
| 961 | - }; | ||
| 962 | - }, | ||
| 963 | - }, | ||
| 964 | - { | ||
| 965 | - field: CommandFieldsEnum.CUSTOM_TYPE, | ||
| 966 | - component: 'RadioGroup', | ||
| 967 | - label: '单向/双向', | ||
| 968 | - defaultValue: CommandDeliveryWayEnum.ONE_WAY, | ||
| 969 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | ||
| 970 | - componentProps: { | ||
| 971 | - options: [ | ||
| 972 | - { | ||
| 973 | - label: '单向', | ||
| 974 | - value: CommandDeliveryWayEnum.ONE_WAY, | ||
| 975 | - }, | ||
| 976 | - { | ||
| 977 | - label: '双向', | ||
| 978 | - value: CommandDeliveryWayEnum.TWO_WAY, | ||
| 979 | - }, | ||
| 980 | - ], | ||
| 981 | - }, | ||
| 982 | - }, | ||
| 983 | - { | ||
| 984 | - field: CommandFieldsEnum.VALUE_TYPE, | ||
| 985 | - label: '命令类型', | ||
| 986 | - component: 'RadioGroup', | ||
| 987 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | ||
| 988 | - defaultValue: transportType === TransportTypeEnum.TCP ? ValueType.STRING : ValueType.JSON, | ||
| 989 | - componentProps: () => { | ||
| 990 | - const options: Record<'label' | 'value', string>[] = []; | ||
| 991 | - if (transportType === TransportTypeEnum.TCP) { | ||
| 992 | - options.push({ label: '字符串', value: ValueType.STRING }); | ||
| 993 | - } else { | ||
| 994 | - options.push({ label: 'JSON', value: ValueType.JSON }); | ||
| 995 | - } | ||
| 996 | - return { | ||
| 997 | - options, | ||
| 998 | - }; | ||
| 999 | - }, | ||
| 1000 | - }, | ||
| 1001 | - { | ||
| 1002 | - field: CommandFieldsEnum.COMMAND_TEXT, | ||
| 1003 | - label: '命令', | ||
| 1004 | - ifShow: ({ model }) => | ||
| 1005 | - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.STRING && | ||
| 1006 | - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | ||
| 1007 | - component: 'InputTextArea', | ||
| 1008 | - componentProps: { | ||
| 1009 | - autoSize: { | ||
| 1010 | - minRows: 3, | ||
| 1011 | - }, | ||
| 1012 | - placeholder: '请输入命令内容', | ||
| 1013 | - }, | ||
| 1014 | - dynamicRules: () => { | ||
| 1015 | - return [ | ||
| 1016 | - { | ||
| 1017 | - required: false, | ||
| 1018 | - validator: (_, value) => { | ||
| 1019 | - const zg = /^[0-9a-zA-Z]*$/; | ||
| 1020 | - if (!zg.test(value)) { | ||
| 1021 | - return Promise.reject('输入的内容只能是字母和数字的组合'); | ||
| 1022 | - } else { | ||
| 1023 | - return Promise.resolve(); | ||
| 1024 | - } | ||
| 1025 | - }, | ||
| 1026 | - }, | ||
| 1027 | - ]; | ||
| 1028 | - }, | ||
| 1029 | - }, | ||
| 1030 | - { | ||
| 1031 | - field: CommandFieldsEnum.COMAND_VALUE, | ||
| 1032 | - label: '命令', | ||
| 1033 | - component: 'JSONEditor', | ||
| 1034 | - colProps: { span: 20 }, | ||
| 1035 | - changeEvent: 'update:value', | ||
| 1036 | - valueField: 'value', | ||
| 1037 | - rules: [...JSONEditorValidator()], | ||
| 1038 | - ifShow: ({ model }) => | ||
| 1039 | - model[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON && | ||
| 1040 | - model[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM, | ||
| 1041 | - | ||
| 1042 | - componentProps: { | ||
| 1043 | - height: 250, | ||
| 1044 | - }, | ||
| 1045 | - }, | ||
| 1046 | - { | ||
| 1047 | - field: CommandFieldsEnum.SERVICE, | ||
| 1048 | - label: '服务', | ||
| 1049 | - component: 'ApiSelect', | ||
| 1050 | - ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | ||
| 1051 | - rules: [{ required: true, message: '请选择服务' }], | ||
| 1052 | - componentProps: ({ formActionType }) => { | ||
| 1053 | - const { setFieldsValue, updateSchema } = formActionType; | ||
| 1054 | - return { | ||
| 1055 | - api: async () => { | ||
| 1056 | - try { | ||
| 1057 | - const result = await getModelServices({ deviceProfileId }); | ||
| 1058 | - return result || []; | ||
| 1059 | - } catch (error) { | ||
| 1060 | - return []; | ||
| 1061 | - } | ||
| 1062 | - }, | ||
| 1063 | - valueField: 'identifier', | ||
| 1064 | - labelField: 'functionName', | ||
| 1065 | - getPopupContainer: () => document.body, | ||
| 1066 | - onChange(value: string, options: ModelOfMatterParams) { | ||
| 1067 | - if (!value) return; | ||
| 1068 | - | ||
| 1069 | - const setValues = { | ||
| 1070 | - [CommandFieldsEnum.CUSTOM_TYPE]: | ||
| 1071 | - options.callType === ServiceCallTypeEnum.ASYNC | ||
| 1072 | - ? CommandDeliveryWayEnum.ONE_WAY | ||
| 1073 | - : CommandDeliveryWayEnum.TWO_WAY, | ||
| 1074 | - [CommandFieldsEnum.MODEL_INPUT]: null, | ||
| 1075 | - }; | ||
| 1076 | - | ||
| 1077 | - if (transportType !== TransportTypeEnum.TCP) { | ||
| 1078 | - updateSchema({ | ||
| 1079 | - field: CommandFieldsEnum.MODEL_INPUT, | ||
| 1080 | - componentProps: { | ||
| 1081 | - inputData: toRaw(unref(options.functionJson.inputData)), | ||
| 1082 | - }, | ||
| 1083 | - }); | ||
| 1084 | - } else { | ||
| 1085 | - Object.assign(setValues, { | ||
| 1086 | - [CommandFieldsEnum.TCP_SERVICE]: | ||
| 1087 | - options.functionJson?.inputData?.[0]?.serviceCommand, | ||
| 1088 | - }); | ||
| 1089 | - } | ||
| 1090 | - | ||
| 1091 | - setFieldsValue(setValues); | ||
| 1092 | - }, | ||
| 1093 | - }; | ||
| 1094 | - }, | ||
| 1095 | - }, | ||
| 1096 | - { | ||
| 1097 | - field: CommandFieldsEnum.TCP_SERVICE, | ||
| 1098 | - component: 'Input', | ||
| 1099 | - label: '命令', | ||
| 1100 | - dynamicDisabled: true, | ||
| 1101 | - ifShow: ({ model }) => | ||
| 1102 | - model[CommandFieldsEnum.SERVICE] && | ||
| 1103 | - transportType === TransportTypeEnum.TCP && | ||
| 1104 | - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | ||
| 1105 | - }, | ||
| 1106 | - { | ||
| 1107 | - field: CommandFieldsEnum.MODEL_INPUT, | ||
| 1108 | - component: 'ObjectModelValidateForm', | ||
| 1109 | - label: '输入参数', | ||
| 1110 | - changeEvent: 'update:value', | ||
| 1111 | - valueField: 'value', | ||
| 1112 | - ifShow: ({ model }) => | ||
| 1113 | - model[CommandFieldsEnum.SERVICE] && | ||
| 1114 | - transportType !== TransportTypeEnum.TCP && | ||
| 1115 | - model[CommandFieldsEnum.COMMAND_TYPE] !== CommandType.CUSTOM, | ||
| 1116 | - componentProps: { | ||
| 1117 | - formProps: { | ||
| 1118 | - wrapperCol: { span: 24 }, | ||
| 1119 | - } as FormProps, | ||
| 1120 | - }, | ||
| 1121 | - }, | ||
| 1122 | - ]; | ||
| 1123 | -}; |
| @@ -5,8 +5,8 @@ import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | @@ -5,8 +5,8 @@ import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
| 5 | import { findDictItemByCode } from '/@/api/system/dict'; | 5 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 6 | import { FormSchema, useComponentRegister } from '/@/components/Form'; | 6 | import { FormSchema, useComponentRegister } from '/@/components/Form'; |
| 7 | import { BasicColumn } from '/@/components/Table'; | 7 | import { BasicColumn } from '/@/components/Table'; |
| 8 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 8 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | 9 | import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; |
| 9 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
| 10 | import XLSX, { CellObject } from 'xlsx'; | 10 | import XLSX, { CellObject } from 'xlsx'; |
| 11 | 11 | ||
| 12 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 12 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 1 | +import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
| 2 | +import { getModelServices } from '/@/api/device/modelOfMatter'; | ||
| 3 | +import { FormProps, FormSchema, useComponentRegister } from '/@/components/Form'; | ||
| 4 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; | ||
| 5 | +import { JSONEditor, JSONEditorValidator } from '/@/components/CodeEditor'; | ||
| 6 | +import { | ||
| 7 | + TransportTypeEnum, | ||
| 8 | + CommandTypeNameEnum, | ||
| 9 | + CommandTypeEnum, | ||
| 10 | + ServiceCallTypeEnum, | ||
| 11 | + CommandDeliveryWayEnum, | ||
| 12 | + CommandDeliveryWayNameEnum, | ||
| 13 | +} from '/@/enums/deviceEnum'; | ||
| 14 | + | ||
| 15 | +export interface CommandDeliveryFormFieldType { | ||
| 16 | + [CommandFieldsEnum.COMMAND_TYPE]: CommandTypeEnum; | ||
| 17 | + [CommandFieldsEnum.TCP_COMMAND_VALUE]?: string; | ||
| 18 | + [CommandFieldsEnum.COMAND_VALUE]?: string; | ||
| 19 | + [CommandFieldsEnum.SERVICE]?: string; | ||
| 20 | + [CommandFieldsEnum.MODEL_INPUT]?: ModelOfMatterParams; | ||
| 21 | + [CommandFieldsEnum.CALL_TYPE]: CommandDeliveryWayEnum; | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +export enum CommandFieldsEnum { | ||
| 25 | + COMMAND_TYPE = 'commandType', | ||
| 26 | + TCP_COMMAND_VALUE = 'tcpCommandValue', | ||
| 27 | + COMAND_VALUE = 'commandValue', | ||
| 28 | + SERVICE = 'service', | ||
| 29 | + MODEL_INPUT = 'modelInput', | ||
| 30 | + CALL_TYPE = 'callType', | ||
| 31 | + | ||
| 32 | + SERVICE_COMMAND = 'serviceCommand', | ||
| 33 | + | ||
| 34 | + SERVICE_OBJECT_MODEL = 'serviceObjectModel', | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +useComponentRegister('JSONEditor', JSONEditor); | ||
| 38 | + | ||
| 39 | +export const CommandSchemas = ( | ||
| 40 | + transportType: TransportTypeEnum, | ||
| 41 | + deviceProfileId: string | ||
| 42 | +): FormSchema[] => { | ||
| 43 | + return [ | ||
| 44 | + { | ||
| 45 | + field: CommandFieldsEnum.COMMAND_TYPE, | ||
| 46 | + component: 'RadioGroup', | ||
| 47 | + label: '下发类型', | ||
| 48 | + defaultValue: CommandTypeEnum.CUSTOM, | ||
| 49 | + required: true, | ||
| 50 | + componentProps: ({ formActionType }) => { | ||
| 51 | + const { setFieldsValue } = formActionType; | ||
| 52 | + return { | ||
| 53 | + options: [ | ||
| 54 | + { label: CommandTypeNameEnum.CUSTOM, value: CommandTypeEnum.CUSTOM }, | ||
| 55 | + { label: CommandTypeNameEnum.SERVICE, value: CommandTypeEnum.SERVICE }, | ||
| 56 | + ], | ||
| 57 | + onChange() { | ||
| 58 | + setFieldsValue({ | ||
| 59 | + [CommandFieldsEnum.SERVICE]: null, | ||
| 60 | + [CommandFieldsEnum.MODEL_INPUT]: null, | ||
| 61 | + [CommandFieldsEnum.COMAND_VALUE]: null, | ||
| 62 | + [CommandFieldsEnum.TCP_COMMAND_VALUE]: null, | ||
| 63 | + }); | ||
| 64 | + }, | ||
| 65 | + }; | ||
| 66 | + }, | ||
| 67 | + }, | ||
| 68 | + { | ||
| 69 | + field: CommandFieldsEnum.CALL_TYPE, | ||
| 70 | + component: 'RadioGroup', | ||
| 71 | + label: '单向/双向', | ||
| 72 | + required: true, | ||
| 73 | + defaultValue: CommandDeliveryWayEnum.ONE_WAY, | ||
| 74 | + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
| 75 | + componentProps: { | ||
| 76 | + options: Object.keys(CommandDeliveryWayEnum).map((key) => ({ | ||
| 77 | + label: CommandDeliveryWayNameEnum[key], | ||
| 78 | + value: CommandDeliveryWayEnum[key], | ||
| 79 | + })), | ||
| 80 | + }, | ||
| 81 | + }, | ||
| 82 | + { | ||
| 83 | + field: CommandFieldsEnum.TCP_COMMAND_VALUE, | ||
| 84 | + label: '命令', | ||
| 85 | + required: true, | ||
| 86 | + ifShow: ({ model }) => | ||
| 87 | + transportType === TransportTypeEnum.TCP && | ||
| 88 | + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
| 89 | + component: 'Input', | ||
| 90 | + rules: [{ validator: validateTCPCustomCommand }], | ||
| 91 | + componentProps: { | ||
| 92 | + placeholder: '请输入命令', | ||
| 93 | + }, | ||
| 94 | + }, | ||
| 95 | + { | ||
| 96 | + field: CommandFieldsEnum.COMAND_VALUE, | ||
| 97 | + label: '命令', | ||
| 98 | + component: 'JSONEditor', | ||
| 99 | + colProps: { span: 20 }, | ||
| 100 | + changeEvent: 'update:value', | ||
| 101 | + valueField: 'value', | ||
| 102 | + required: true, | ||
| 103 | + rules: JSONEditorValidator(), | ||
| 104 | + ifShow: ({ model }) => | ||
| 105 | + transportType !== TransportTypeEnum.TCP && | ||
| 106 | + model[CommandFieldsEnum.COMMAND_TYPE] === CommandTypeEnum.CUSTOM, | ||
| 107 | + componentProps: { | ||
| 108 | + height: 250, | ||
| 109 | + }, | ||
| 110 | + }, | ||
| 111 | + { | ||
| 112 | + field: CommandFieldsEnum.SERVICE, | ||
| 113 | + label: '服务', | ||
| 114 | + component: 'ApiSelect', | ||
| 115 | + required: true, | ||
| 116 | + ifShow: ({ model }) => model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM, | ||
| 117 | + rules: [{ required: true, message: '请选择服务' }], | ||
| 118 | + componentProps: ({ formActionType }) => { | ||
| 119 | + const { setFieldsValue } = formActionType; | ||
| 120 | + return { | ||
| 121 | + api: getModelServices, | ||
| 122 | + params: { | ||
| 123 | + deviceProfileId, | ||
| 124 | + }, | ||
| 125 | + valueField: 'identifier', | ||
| 126 | + labelField: 'functionName', | ||
| 127 | + getPopupContainer: () => document.body, | ||
| 128 | + placeholder: '请选择服务', | ||
| 129 | + onChange( | ||
| 130 | + value: string, | ||
| 131 | + options: ModelOfMatterParams & Record<'label' | 'value', string> | ||
| 132 | + ) { | ||
| 133 | + if (!value) return; | ||
| 134 | + setFieldsValue({ | ||
| 135 | + [CommandFieldsEnum.CALL_TYPE]: | ||
| 136 | + options.callType === ServiceCallTypeEnum.ASYNC | ||
| 137 | + ? CommandDeliveryWayEnum.ONE_WAY | ||
| 138 | + : CommandDeliveryWayEnum.TWO_WAY, | ||
| 139 | + [CommandFieldsEnum.MODEL_INPUT]: null, | ||
| 140 | + [CommandFieldsEnum.SERVICE_OBJECT_MODEL]: Object.assign(options, { | ||
| 141 | + functionName: options.label, | ||
| 142 | + identifier: options.value, | ||
| 143 | + }), | ||
| 144 | + }); | ||
| 145 | + }, | ||
| 146 | + }; | ||
| 147 | + }, | ||
| 148 | + }, | ||
| 149 | + { | ||
| 150 | + field: CommandFieldsEnum.SERVICE_OBJECT_MODEL, | ||
| 151 | + label: '服务物模型', | ||
| 152 | + component: 'Input', | ||
| 153 | + ifShow: false, | ||
| 154 | + }, | ||
| 155 | + { | ||
| 156 | + field: CommandFieldsEnum.MODEL_INPUT, | ||
| 157 | + component: 'Input', | ||
| 158 | + label: '输入参数', | ||
| 159 | + changeEvent: 'update:value', | ||
| 160 | + valueField: 'value', | ||
| 161 | + ifShow: ({ model }) => | ||
| 162 | + model[CommandFieldsEnum.SERVICE] && | ||
| 163 | + model[CommandFieldsEnum.SERVICE_OBJECT_MODEL] && | ||
| 164 | + model[CommandFieldsEnum.COMMAND_TYPE] !== CommandTypeEnum.CUSTOM, | ||
| 165 | + componentProps: { | ||
| 166 | + formProps: { | ||
| 167 | + wrapperCol: { span: 24 }, | ||
| 168 | + } as FormProps, | ||
| 169 | + }, | ||
| 170 | + slot: 'serviceCommand', | ||
| 171 | + }, | ||
| 172 | + ]; | ||
| 173 | +}; |
| 1 | +export { default as CommandDeliveryModal } from './index.vue'; |
| 1 | +<template> | ||
| 2 | + <BasicModal | ||
| 3 | + title="命令下发" | ||
| 4 | + :width="650" | ||
| 5 | + @register="registerModal" | ||
| 6 | + @ok="handleOk" | ||
| 7 | + @cancel="handleCancel" | ||
| 8 | + > | ||
| 9 | + <BasicForm @register="registerForm"> | ||
| 10 | + <template #serviceCommand="{ field, model }"> | ||
| 11 | + <ThingsModelForm | ||
| 12 | + :disabled="deviceDetail?.transportType === TransportTypeEnum.TCP" | ||
| 13 | + ref="thingsModelFormRef" | ||
| 14 | + v-model:value="model[field]" | ||
| 15 | + :key="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.identifier" | ||
| 16 | + :inputData="model[CommandFieldsEnum.SERVICE_OBJECT_MODEL]?.functionJson?.inputData" | ||
| 17 | + :transportType="deviceDetail?.transportType" | ||
| 18 | + /> | ||
| 19 | + </template> | ||
| 20 | + </BasicForm> | ||
| 21 | + </BasicModal> | ||
| 22 | +</template> | ||
| 23 | +<script lang="ts" setup> | ||
| 24 | + import { nextTick, ref, unref } from 'vue'; | ||
| 25 | + import { BasicForm, ThingsModelForm, useForm } from '/@/components/Form'; | ||
| 26 | + import { CommandDeliveryFormFieldType, CommandFieldsEnum, CommandSchemas } from './config'; | ||
| 27 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 28 | + import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
| 29 | + import { CommandDeliveryWayEnum } from '/@/enums/deviceEnum'; | ||
| 30 | + import { CommandTypeEnum, RPCCommandMethodEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 31 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
| 32 | + import { getDeviceActiveTime } from '/@/api/alarm/position'; | ||
| 33 | + import { RpcCommandType } from '/@/api/device/model/deviceConfigModel'; | ||
| 34 | + import { parseStringToJSON } from '/@/components/CodeEditor'; | ||
| 35 | + import { commandIssuanceApi } from '/@/api/device/deviceManager'; | ||
| 36 | + | ||
| 37 | + defineEmits(['register']); | ||
| 38 | + | ||
| 39 | + const thingsModelFormRef = ref<InstanceType<typeof ThingsModelForm>>(); | ||
| 40 | + const deviceDetail = ref<DeviceRecord>(); | ||
| 41 | + | ||
| 42 | + const [registerModal, { setModalProps }] = useModalInner( | ||
| 43 | + (params: ModalParamsType<DeviceRecord>) => { | ||
| 44 | + const { record } = params; | ||
| 45 | + deviceDetail.value = record; | ||
| 46 | + setProps({ | ||
| 47 | + schemas: CommandSchemas(record.transportType as TransportTypeEnum, record.deviceProfileId), | ||
| 48 | + }); | ||
| 49 | + } | ||
| 50 | + ); | ||
| 51 | + | ||
| 52 | + const { createMessage } = useMessage(); | ||
| 53 | + | ||
| 54 | + const [registerForm, { setProps, getFieldsValue, validate, resetFields, clearValidate }] = | ||
| 55 | + useForm({ | ||
| 56 | + labelWidth: 120, | ||
| 57 | + baseColProps: { span: 20 }, | ||
| 58 | + labelAlign: 'right', | ||
| 59 | + showSubmitButton: false, | ||
| 60 | + showResetButton: false, | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + const handleCancel = async () => { | ||
| 64 | + await resetFields(); | ||
| 65 | + await nextTick(); | ||
| 66 | + await clearValidate(); | ||
| 67 | + }; | ||
| 68 | + | ||
| 69 | + const handleValidate = async () => { | ||
| 70 | + await validate(); | ||
| 71 | + await unref(thingsModelFormRef)?.validate?.(); | ||
| 72 | + }; | ||
| 73 | + | ||
| 74 | + const handleValidateDeviceActive = async (): Promise<boolean> => { | ||
| 75 | + const result = await getDeviceActiveTime(unref(deviceDetail)!.tbDeviceId); | ||
| 76 | + const [firstItem] = result; | ||
| 77 | + return !!firstItem.value; | ||
| 78 | + }; | ||
| 79 | + | ||
| 80 | + const handleCommandParams = ( | ||
| 81 | + values: CommandDeliveryFormFieldType, | ||
| 82 | + serviceCommand: Recordable | ||
| 83 | + ) => { | ||
| 84 | + const { commandType, service } = values; | ||
| 85 | + | ||
| 86 | + const isTcpDevice = unref(deviceDetail)?.transportType === TransportTypeEnum.TCP; | ||
| 87 | + if (commandType === CommandTypeEnum.CUSTOM) { | ||
| 88 | + if (isTcpDevice) { | ||
| 89 | + return values.tcpCommandValue; | ||
| 90 | + } | ||
| 91 | + return parseStringToJSON(values.commandValue!).json; | ||
| 92 | + } else { | ||
| 93 | + if (isTcpDevice) return Reflect.get(serviceCommand, CommandFieldsEnum.SERVICE_COMMAND); | ||
| 94 | + return { | ||
| 95 | + [service!]: serviceCommand, | ||
| 96 | + }; | ||
| 97 | + } | ||
| 98 | + }; | ||
| 99 | + | ||
| 100 | + const handleOk = async () => { | ||
| 101 | + await handleValidate(); | ||
| 102 | + | ||
| 103 | + try { | ||
| 104 | + setModalProps({ loading: true, confirmLoading: true }); | ||
| 105 | + | ||
| 106 | + const values = getFieldsValue() as CommandDeliveryFormFieldType; | ||
| 107 | + const { callType, commandType } = values; | ||
| 108 | + const serviceCommand = unref(thingsModelFormRef)?.getFieldsValue() || {}; | ||
| 109 | + | ||
| 110 | + if (callType === CommandDeliveryWayEnum.TWO_WAY && !(await handleValidateDeviceActive())) { | ||
| 111 | + createMessage.warn('当前设备不在线'); | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + const rpcCommands: RpcCommandType = { | ||
| 116 | + additionalInfo: { | ||
| 117 | + cmdType: | ||
| 118 | + commandType === CommandTypeEnum.CUSTOM | ||
| 119 | + ? CommandTypeEnum.CUSTOM | ||
| 120 | + : CommandTypeEnum.SERVICE, | ||
| 121 | + }, | ||
| 122 | + method: RPCCommandMethodEnum.THINGSKIT, | ||
| 123 | + persistent: true, | ||
| 124 | + params: handleCommandParams(values, serviceCommand), | ||
| 125 | + }; | ||
| 126 | + | ||
| 127 | + await commandIssuanceApi(callType, unref(deviceDetail)!.tbDeviceId, rpcCommands); | ||
| 128 | + | ||
| 129 | + createMessage.success('命令下发成功'); | ||
| 130 | + } finally { | ||
| 131 | + setModalProps({ loading: false, confirmLoading: false }); | ||
| 132 | + } | ||
| 133 | + }; | ||
| 134 | +</script> | ||
| 135 | +<style scoped lang="less"></style> |
src/views/device/list/cpns/tabs/CommandIssuance.vue
deleted
100644 → 0
| 1 | -<template> | ||
| 2 | - <div class="tabs-detail"> | ||
| 3 | - <div> | ||
| 4 | - <BasicForm @register="registerForm" /> | ||
| 5 | - <Space class="w-full justify-end py-2" justify="end"> | ||
| 6 | - <Button :loading="loading" type="primary" @click="handleOk" class="mr-2">确定</Button> | ||
| 7 | - <Button type="default" @click="handleCancel" class="mr-2">重置</Button> | ||
| 8 | - </Space> | ||
| 9 | - </div> | ||
| 10 | - </div> | ||
| 11 | -</template> | ||
| 12 | -<script lang="ts"> | ||
| 13 | - import { defineComponent, nextTick, ref } from 'vue'; | ||
| 14 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
| 15 | - import { CommandFieldsEnum, CommandSchemas, CommandType, ValueType } from '../../config/data'; | ||
| 16 | - import { commandIssuanceApi } from '/@/api/device/deviceManager'; | ||
| 17 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 18 | - import { Button } from '/@/components/Button'; | ||
| 19 | - import { Space } from 'ant-design-vue'; | ||
| 20 | - import { DeviceRecord } from '/@/api/device/model/deviceModel'; | ||
| 21 | - import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const'; | ||
| 22 | - import { parseStringToJSON } from '/@/components/CodeEditor/src/JSONEditor'; | ||
| 23 | - import { CommandDeliveryWayEnum } from '/@/enums/toolEnum'; | ||
| 24 | - | ||
| 25 | - export default defineComponent({ | ||
| 26 | - components: { BasicForm, Button, Space }, | ||
| 27 | - props: { | ||
| 28 | - deviceDetail: { | ||
| 29 | - type: Object as PropType<DeviceRecord>, | ||
| 30 | - required: true, | ||
| 31 | - }, | ||
| 32 | - }, | ||
| 33 | - emits: ['register'], | ||
| 34 | - setup(props) { | ||
| 35 | - const { createMessage } = useMessage(); | ||
| 36 | - const loading = ref(false); | ||
| 37 | - | ||
| 38 | - const [registerForm, { getFieldsValue, validate, resetFields, clearValidate }] = useForm({ | ||
| 39 | - labelWidth: 120, | ||
| 40 | - schemas: CommandSchemas( | ||
| 41 | - props.deviceDetail.deviceProfile.transportType as TransportTypeEnum, | ||
| 42 | - props.deviceDetail.deviceProfileId | ||
| 43 | - ), | ||
| 44 | - baseColProps: { span: 20 }, | ||
| 45 | - labelAlign: 'right', | ||
| 46 | - showSubmitButton: false, | ||
| 47 | - showResetButton: false, | ||
| 48 | - }); | ||
| 49 | - | ||
| 50 | - const handleCancel = async () => { | ||
| 51 | - await resetFields(); | ||
| 52 | - await nextTick(); | ||
| 53 | - await clearValidate(); | ||
| 54 | - }; | ||
| 55 | - | ||
| 56 | - const handleOk = async () => { | ||
| 57 | - loading.value = true; | ||
| 58 | - try { | ||
| 59 | - // 验证 | ||
| 60 | - const valid = await validate(); | ||
| 61 | - if (!valid) return; | ||
| 62 | - // 收集表单数据 | ||
| 63 | - const field = getFieldsValue(); | ||
| 64 | - let command: Recordable = { | ||
| 65 | - persistent: true, | ||
| 66 | - method: 'methodThingskit', | ||
| 67 | - params: field[CommandFieldsEnum.COMMAND_TEXT], | ||
| 68 | - }; | ||
| 69 | - | ||
| 70 | - if (field[CommandFieldsEnum.COMMAND_TYPE] === CommandType.CUSTOM) { | ||
| 71 | - if (field[CommandFieldsEnum.VALUE_TYPE] === ValueType.JSON) { | ||
| 72 | - const { json } = parseStringToJSON(field.commandValue); | ||
| 73 | - command.params = json; | ||
| 74 | - } | ||
| 75 | - } else { | ||
| 76 | - const { transportType } = props.deviceDetail.deviceProfile; | ||
| 77 | - command.params = | ||
| 78 | - transportType === TransportTypeEnum.TCP | ||
| 79 | - ? field[CommandFieldsEnum.TCP_SERVICE] | ||
| 80 | - : { | ||
| 81 | - [field[CommandFieldsEnum.SERVICE]]: field[CommandFieldsEnum.MODEL_INPUT], | ||
| 82 | - }; | ||
| 83 | - command.additionalInfo = { cmdType: 1 }; | ||
| 84 | - } | ||
| 85 | - commandIssuanceApi( | ||
| 86 | - field[CommandFieldsEnum.CUSTOM_TYPE] as CommandDeliveryWayEnum, | ||
| 87 | - props.deviceDetail.tbDeviceId, | ||
| 88 | - command | ||
| 89 | - ) | ||
| 90 | - .then((res) => { | ||
| 91 | - if (!res) return; | ||
| 92 | - createMessage.success('命令下发成功'); | ||
| 93 | - loading.value = true; | ||
| 94 | - // 请求 | ||
| 95 | - handleCancel(); | ||
| 96 | - }) | ||
| 97 | - .catch((e) => { | ||
| 98 | - if (e?.message) { | ||
| 99 | - createMessage.error(e?.message); | ||
| 100 | - } | ||
| 101 | - handleCancel(); | ||
| 102 | - }) | ||
| 103 | - .finally(() => { | ||
| 104 | - setTimeout(() => { | ||
| 105 | - loading.value = false; | ||
| 106 | - }, 300); | ||
| 107 | - }); | ||
| 108 | - } catch (e) { | ||
| 109 | - throw e; | ||
| 110 | - } finally { | ||
| 111 | - loading.value = false; | ||
| 112 | - } | ||
| 113 | - }; | ||
| 114 | - return { | ||
| 115 | - registerForm, | ||
| 116 | - handleCancel, | ||
| 117 | - handleOk, | ||
| 118 | - loading, | ||
| 119 | - }; | ||
| 120 | - }, | ||
| 121 | - }); | ||
| 122 | -</script> | ||
| 123 | -<style scoped lang="less"> | ||
| 124 | - .jsoneditor-transform { | ||
| 125 | - background-position: -144px -96px; | ||
| 126 | - display: none !important; | ||
| 127 | - } | ||
| 128 | - | ||
| 129 | - .tabs-detail:deep(.object-model-validate-form) { | ||
| 130 | - > .ant-row { | ||
| 131 | - @apply w-full; | ||
| 132 | - } | ||
| 133 | - } | ||
| 134 | -</style> |
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | import { ColEx } from '/@/components/Form/src/types'; | 14 | import { ColEx } from '/@/components/Form/src/types'; |
| 15 | import { useHistoryData } from '../../hook/useHistoryData'; | 15 | import { useHistoryData } from '../../hook/useHistoryData'; |
| 16 | import { formatToDateTime } from '/@/utils/dateUtil'; | 16 | import { formatToDateTime } from '/@/utils/dateUtil'; |
| 17 | - import { useTable, BasicTable, BasicColumn } from '/@/components/Table'; | 17 | + import { useTable, BasicTable, BasicColumn, SorterResult } from '/@/components/Table'; |
| 18 | import { | 18 | import { |
| 19 | ModeSwitchButton, | 19 | ModeSwitchButton, |
| 20 | TABLE_CHART_MODE_LIST, | 20 | TABLE_CHART_MODE_LIST, |
| @@ -64,7 +64,7 @@ | @@ -64,7 +64,7 @@ | ||
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | const sortOrder = ref<any>('descend'); | 66 | const sortOrder = ref<any>('descend'); |
| 67 | - const columns: BasicColumn[] | any = computed(() => { | 67 | + const columns = computed<BasicColumn[]>(() => { |
| 68 | return [ | 68 | return [ |
| 69 | { | 69 | { |
| 70 | title: '属性', | 70 | title: '属性', |
| @@ -119,7 +119,7 @@ | @@ -119,7 +119,7 @@ | ||
| 119 | loading.value = false; | 119 | loading.value = false; |
| 120 | }; | 120 | }; |
| 121 | 121 | ||
| 122 | - const handleTableChange = async (_pag, _filters, sorter: any) => { | 122 | + const handleTableChange = async (_pag, _filters, sorter: SorterResult) => { |
| 123 | sortOrder.value = sorter.order; | 123 | sortOrder.value = sorter.order; |
| 124 | await setColumns(unref(columns)); | 124 | await setColumns(unref(columns)); |
| 125 | if (sorter.field == 'ts') { | 125 | if (sorter.field == 'ts') { |
| @@ -18,7 +18,8 @@ | @@ -18,7 +18,8 @@ | ||
| 18 | import { useGlobSetting } from '/@/hooks/setting'; | 18 | import { useGlobSetting } from '/@/hooks/setting'; |
| 19 | import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; | 19 | import { ModeSwitchButton, EnumTableCardMode } from '/@/components/Widget'; |
| 20 | import { toRaw } from 'vue'; | 20 | import { toRaw } from 'vue'; |
| 21 | - import { DataActionModeEnum, ReadAndWriteEnum } from '/@/enums/toolEnum'; | 21 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; |
| 22 | + import { ReadAndWriteEnum } from '/@/enums/deviceEnum'; | ||
| 22 | import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; | 23 | import { ObjectModelCommandDeliveryModal } from './ObjectModelCommandDeliveryModal'; |
| 23 | import { ModalParamsType } from '/#/utils'; | 24 | import { ModalParamsType } from '/#/utils'; |
| 24 | import { AreaChartOutlined } from '@ant-design/icons-vue'; | 25 | import { AreaChartOutlined } from '@ant-design/icons-vue'; |
| 1 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | 1 | import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; |
| 2 | import { FormSchema } from '/@/components/Form'; | 2 | import { FormSchema } from '/@/components/Form'; |
| 3 | -import { validateTCPCustomCommand } from '/@/components/Form/src/externalCompns/components/ThingsModelForm'; | 3 | +import { validateTCPCustomCommand } from '/@/components/Form/src/components/ThingsModelForm'; |
| 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 4 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 5 | 5 | ||
| 6 | const InsertString = (t, c, n) => { | 6 | const InsertString = (t, c, n) => { |
| @@ -12,8 +12,8 @@ | @@ -12,8 +12,8 @@ | ||
| 12 | import { genModbusCommand } from '/@/api/task'; | 12 | import { genModbusCommand } from '/@/api/task'; |
| 13 | import { TaskTypeEnum } from '/@/views/task/center/config'; | 13 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 14 | import { SingleToHex, formSchemasConfig } from './config'; | 14 | import { SingleToHex, formSchemasConfig } from './config'; |
| 15 | - import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
| 16 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 15 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 16 | + import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 17 | 17 | ||
| 18 | defineEmits(['register']); | 18 | defineEmits(['register']); |
| 19 | const props = defineProps<{ deviceId: string; deviceName: string }>(); | 19 | const props = defineProps<{ deviceId: string; deviceName: string }>(); |
| @@ -97,7 +97,7 @@ | @@ -97,7 +97,7 @@ | ||
| 97 | 97 | ||
| 98 | const handleControl = (action: number, direction: string) => { | 98 | const handleControl = (action: number, direction: string) => { |
| 99 | const organizationId = getId(); | 99 | const organizationId = getId(); |
| 100 | - controlling({ cameralndexCode: organizationId, action, command: direction }); | 100 | + controlling({ cameraIndexCode: organizationId, action, command: direction }); |
| 101 | }; | 101 | }; |
| 102 | 102 | ||
| 103 | const isPlay = ref<Boolean | null | undefined>(false); | 103 | const isPlay = ref<Boolean | null | undefined>(false); |
| @@ -6,7 +6,7 @@ | @@ -6,7 +6,7 @@ | ||
| 6 | <template #toolbar> | 6 | <template #toolbar> |
| 7 | <Space> | 7 | <Space> |
| 8 | <Authority value="api:yt:device:rpc"> | 8 | <Authority value="api:yt:device:rpc"> |
| 9 | - <Button type="primary" @click="openModal(true)">命令下发</Button> | 9 | + <Button type="primary" @click="handleOpenModal">命令下发</Button> |
| 10 | </Authority> | 10 | </Authority> |
| 11 | </Space> | 11 | </Space> |
| 12 | </template> | 12 | </template> |
| @@ -24,16 +24,7 @@ | @@ -24,16 +24,7 @@ | ||
| 24 | </template> | 24 | </template> |
| 25 | </BasicTable> | 25 | </BasicTable> |
| 26 | 26 | ||
| 27 | - <BasicModal | ||
| 28 | - @register="registerCommandIssuanceModal" | ||
| 29 | - width="700px" | ||
| 30 | - title="命令下发" | ||
| 31 | - :showOkBtn="false" | ||
| 32 | - cancelText="关闭" | ||
| 33 | - :footer="null" | ||
| 34 | - > | ||
| 35 | - <CommandIssuance :deviceDetail="deviceDetail" /> | ||
| 36 | - </BasicModal> | 27 | + <CommandDeliveryModal @register="registerCommandDeliverModal" :deviceDetail="deviceDetail" /> |
| 37 | </template> | 28 | </template> |
| 38 | <script lang="ts" setup> | 29 | <script lang="ts" setup> |
| 39 | import { h } from 'vue'; | 30 | import { h } from 'vue'; |
| @@ -43,9 +34,10 @@ | @@ -43,9 +34,10 @@ | ||
| 43 | import { Button, Modal, Space } from 'ant-design-vue'; | 34 | import { Button, Modal, Space } from 'ant-design-vue'; |
| 44 | import { JsonPreview } from '/@/components/CodeEditor'; | 35 | import { JsonPreview } from '/@/components/CodeEditor'; |
| 45 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; | 36 | import { DeviceRecord } from '/@/api/device/model/deviceModel'; |
| 46 | - import { BasicModal, useModal } from '/@/components/Modal'; | ||
| 47 | - import CommandIssuance from '../CommandIssuance.vue'; | 37 | + import { useModal } from '/@/components/Modal'; |
| 38 | + import { CommandDeliveryModal } from '../CommandDeliveryModal'; | ||
| 48 | import { Authority } from '/@/components/Authority'; | 39 | import { Authority } from '/@/components/Authority'; |
| 40 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
| 49 | 41 | ||
| 50 | const props = defineProps({ | 42 | const props = defineProps({ |
| 51 | fromId: { | 43 | fromId: { |
| @@ -58,7 +50,7 @@ | @@ -58,7 +50,7 @@ | ||
| 58 | }, | 50 | }, |
| 59 | }); | 51 | }); |
| 60 | 52 | ||
| 61 | - const [registerCommandIssuanceModal, { openModal }] = useModal(); | 53 | + const [registerCommandDeliverModal, { openModal }] = useModal(); |
| 62 | 54 | ||
| 63 | const [registerTable] = useTable({ | 55 | const [registerTable] = useTable({ |
| 64 | api: deviceCommandRecordGetQuery, | 56 | api: deviceCommandRecordGetQuery, |
| @@ -79,6 +71,7 @@ | @@ -79,6 +71,7 @@ | ||
| 79 | }, | 71 | }, |
| 80 | useSearchForm: true, | 72 | useSearchForm: true, |
| 81 | }); | 73 | }); |
| 74 | + | ||
| 82 | const commonModalInfo = (title, value) => { | 75 | const commonModalInfo = (title, value) => { |
| 83 | Modal.info({ | 76 | Modal.info({ |
| 84 | title, | 77 | title, |
| @@ -86,14 +79,23 @@ | @@ -86,14 +79,23 @@ | ||
| 86 | content: h(JsonPreview, { data: value }), | 79 | content: h(JsonPreview, { data: value }), |
| 87 | }); | 80 | }); |
| 88 | }; | 81 | }; |
| 82 | + | ||
| 89 | const handleRecordContent = (record) => { | 83 | const handleRecordContent = (record) => { |
| 90 | if (!record?.request?.body) return; | 84 | if (!record?.request?.body) return; |
| 91 | if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return; | 85 | if (Object.prototype.toString.call(record?.request?.body) !== '[object Object]') return; |
| 92 | const jsonParams = record?.request?.body?.params; | 86 | const jsonParams = record?.request?.body?.params; |
| 93 | commonModalInfo('命令下发内容', jsonParams); | 87 | commonModalInfo('命令下发内容', jsonParams); |
| 94 | }; | 88 | }; |
| 89 | + | ||
| 95 | const handleRecordResponseContent = (record) => { | 90 | const handleRecordResponseContent = (record) => { |
| 96 | const jsonParams = record?.response; | 91 | const jsonParams = record?.response; |
| 97 | commonModalInfo('响应内容', jsonParams); | 92 | commonModalInfo('响应内容', jsonParams); |
| 98 | }; | 93 | }; |
| 94 | + | ||
| 95 | + function handleOpenModal() { | ||
| 96 | + openModal(true, { | ||
| 97 | + mode: DataActionModeEnum.READ, | ||
| 98 | + record: props.deviceDetail, | ||
| 99 | + } as ModalParamsType<DeviceRecord>); | ||
| 100 | + } | ||
| 99 | </script> | 101 | </script> |
| @@ -287,6 +287,7 @@ export function getPacketIntervalByRange( | @@ -287,6 +287,7 @@ export function getPacketIntervalByRange( | ||
| 287 | break; | 287 | break; |
| 288 | } | 288 | } |
| 289 | } | 289 | } |
| 290 | + if (!options.length) options = rangeIntervalOption[rangeIntervalOption.length - 1].linkage; | ||
| 290 | return options; | 291 | return options; |
| 291 | } | 292 | } |
| 292 | return []; | 293 | return []; |
| 1 | +import { unref } from 'vue'; | ||
| 2 | +import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | ||
| 3 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
| 4 | +import { Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 5 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 6 | +import { FormSchema } from '/@/components/Form'; | ||
| 7 | +import { TransportTypeEnum } from '/@/enums/deviceEnum'; | ||
| 8 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
| 9 | +import { DataTypeEnum, FunctionTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 10 | +import { isNullOrUnDef } from '/@/utils/is'; | ||
| 11 | +import { ValidatorRule } from 'ant-design-vue/lib/form/interface'; | ||
| 12 | + | ||
| 13 | +export interface DataTypeFormGetFieldsValueType { | ||
| 14 | + [FormFieldsEnum.FUNCTION_NAME]: string; | ||
| 15 | + [FormFieldsEnum.IDENTIFIER]: string; | ||
| 16 | + [FormFieldsEnum.DATA_TYPE]: DataTypeEnum; | ||
| 17 | + [FormFieldsEnum.VALUE_RANGE]?: Record<'min' | 'max', number>; | ||
| 18 | + [FormFieldsEnum.STEP]?: number; | ||
| 19 | + [FormFieldsEnum.UNIT]?: string; | ||
| 20 | + [FormFieldsEnum.UNIT_NAME]?: string; | ||
| 21 | + [FormFieldsEnum.BOOL_CLOSE]?: string; | ||
| 22 | + [FormFieldsEnum.BOOL_OPEN]?: string; | ||
| 23 | + [FormFieldsEnum.LENGTH]?: number; | ||
| 24 | + [FormFieldsEnum.ENUMS_DATA]?: Specs[]; | ||
| 25 | + [FormFieldsEnum.STRUCT_DATA]?: StructJSON[]; | ||
| 26 | + [FormFieldsEnum.REMARK]?: string; | ||
| 27 | +} | ||
| 28 | + | ||
| 29 | +export const validateValueRange: ValidatorRule['validator'] = ( | ||
| 30 | + _rule, | ||
| 31 | + value: Record<'min' | 'max', number>, | ||
| 32 | + _callback | ||
| 33 | +) => { | ||
| 34 | + value = value || {}; | ||
| 35 | + const { min, max } = value; | ||
| 36 | + if (min > max) { | ||
| 37 | + return Promise.reject('最大值小于最小值'); | ||
| 38 | + } | ||
| 39 | + return Promise.resolve(); | ||
| 40 | +}; | ||
| 41 | + | ||
| 42 | +export const validateFunctionName: ValidatorRule['validator'] = (_rule, value: any) => { | ||
| 43 | + if (/^[a-zA-Z0-9_\-\u4e00-\u9fa5]+$/.test(value)) return Promise.resolve(); | ||
| 44 | + return Promise.reject('支持中文、大小写字母、数字、短划线、下划线.'); | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +export const validateIdentifier: ValidatorRule['validator'] = (_rule, value: any) => { | ||
| 48 | + if (/^[a-zA-Z0-9_]+$/.test(value)) { | ||
| 49 | + return Promise.resolve(); | ||
| 50 | + } | ||
| 51 | + return Promise.reject('支持大小写字母、数字和下划线.'); | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +export const createFunctionNameFormItem = (params: Partial<FormSchema> = {}): FormSchema => { | ||
| 55 | + return { | ||
| 56 | + field: FormFieldsEnum.FUNCTION_NAME, | ||
| 57 | + label: FormFieldsNameEnum.FUNCTION_NAME, | ||
| 58 | + component: 'Input', | ||
| 59 | + required: true, | ||
| 60 | + helpMessage: '支持中文、大小写字母、数字、短划线、下划线。', | ||
| 61 | + rules: [ | ||
| 62 | + { | ||
| 63 | + required: true, | ||
| 64 | + validator: validateFunctionName, | ||
| 65 | + }, | ||
| 66 | + ], | ||
| 67 | + componentProps: { | ||
| 68 | + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | ||
| 69 | + }, | ||
| 70 | + ...params, | ||
| 71 | + }; | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +export const createIdentifierFormItem = (params: Partial<FormSchema> = {}): FormSchema => { | ||
| 75 | + return { | ||
| 76 | + field: FormFieldsEnum.IDENTIFIER, | ||
| 77 | + label: FormFieldsNameEnum.IDENTIFIER, | ||
| 78 | + required: true, | ||
| 79 | + component: 'Input', | ||
| 80 | + helpMessage: '支持大小写字母、数字和下划线.', | ||
| 81 | + componentProps: { | ||
| 82 | + maxLength: 128, | ||
| 83 | + placeholder: '请输入标识符', | ||
| 84 | + }, | ||
| 85 | + rules: [ | ||
| 86 | + { | ||
| 87 | + required: true, | ||
| 88 | + validator: validateIdentifier, | ||
| 89 | + }, | ||
| 90 | + ], | ||
| 91 | + ...params, | ||
| 92 | + }; | ||
| 93 | +}; | ||
| 94 | + | ||
| 95 | +export const getFormSchemas = (dataType: DataTypeEnum[], showRemark: boolean): FormSchema[] => { | ||
| 96 | + const { getTransportType } = useObjectModelFormContext(); | ||
| 97 | + return [ | ||
| 98 | + createFunctionNameFormItem(), | ||
| 99 | + createIdentifierFormItem(), | ||
| 100 | + { | ||
| 101 | + field: FormFieldsEnum.DATA_TYPE, | ||
| 102 | + label: FormFieldsNameEnum.DATA_TYPE, | ||
| 103 | + required: true, | ||
| 104 | + component: 'ApiSelect', | ||
| 105 | + defaultValue: DataTypeEnum.NUMBER_INT, | ||
| 106 | + ifShow: () => !!dataType.length, | ||
| 107 | + componentProps: () => { | ||
| 108 | + return { | ||
| 109 | + placeholder: '请选择数据类型', | ||
| 110 | + api: async (params: Recordable) => { | ||
| 111 | + let result = await findDictItemByCode(params); | ||
| 112 | + if (unref(getTransportType) === TransportTypeEnum.TCP) { | ||
| 113 | + result = result.filter( | ||
| 114 | + (item) => | ||
| 115 | + ![DataTypeEnum.STRUCT, DataTypeEnum.ENUM].includes(item.itemValue as DataTypeEnum) | ||
| 116 | + ); | ||
| 117 | + } | ||
| 118 | + return result.filter((item) => dataType.includes(item.itemValue as DataTypeEnum)); | ||
| 119 | + }, | ||
| 120 | + params: { | ||
| 121 | + dictCode: DictEnum.DATA_TYPE, | ||
| 122 | + }, | ||
| 123 | + allowClear: false, | ||
| 124 | + labelField: 'itemText', | ||
| 125 | + valueField: 'itemValue', | ||
| 126 | + getPopupContainer: () => document.body, | ||
| 127 | + }; | ||
| 128 | + }, | ||
| 129 | + }, | ||
| 130 | + { | ||
| 131 | + field: FormFieldsEnum.VALUE_RANGE, | ||
| 132 | + label: FormFieldsNameEnum.VALUE_RANGE, | ||
| 133 | + component: 'CustomMinMaxInput', | ||
| 134 | + valueField: 'value', | ||
| 135 | + changeEvent: 'update:value', | ||
| 136 | + ifShow: ({ model }) => | ||
| 137 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 138 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 139 | + ) && | ||
| 140 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 141 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 142 | + dynamicRules: ({ model }) => [ | ||
| 143 | + { | ||
| 144 | + required: | ||
| 145 | + model[FormFieldsEnum.VALUE_RANGE]?.min || model[FormFieldsEnum.VALUE_RANGE]?.max, | ||
| 146 | + validator: validateValueRange, | ||
| 147 | + }, | ||
| 148 | + ], | ||
| 149 | + }, | ||
| 150 | + { | ||
| 151 | + field: FormFieldsEnum.STEP, | ||
| 152 | + label: FormFieldsNameEnum.STEP, | ||
| 153 | + component: 'InputNumber', | ||
| 154 | + componentProps: { | ||
| 155 | + maxLength: 255, | ||
| 156 | + placeholder: '请输入步长', | ||
| 157 | + min: 1, | ||
| 158 | + formatter: (value: number | string) => { | ||
| 159 | + return value ? Math.floor(Number(value)) : value; | ||
| 160 | + }, | ||
| 161 | + }, | ||
| 162 | + ifShow: ({ model }) => | ||
| 163 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 164 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 165 | + ) && | ||
| 166 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 167 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 168 | + dynamicRules: ({ model }) => { | ||
| 169 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | ||
| 170 | + const { min, max } = valueRange; | ||
| 171 | + const step = model[FormFieldsEnum.STEP]; | ||
| 172 | + return [ | ||
| 173 | + { | ||
| 174 | + validator: () => { | ||
| 175 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
| 176 | + if (step > max - min) { | ||
| 177 | + return Promise.reject('步长不能大于取值范围的差值'); | ||
| 178 | + } | ||
| 179 | + return Promise.resolve(); | ||
| 180 | + }, | ||
| 181 | + }, | ||
| 182 | + ]; | ||
| 183 | + }, | ||
| 184 | + }, | ||
| 185 | + { | ||
| 186 | + field: FormFieldsEnum.UNIT_NAME, | ||
| 187 | + label: FormFieldsNameEnum.UNIT_NAME, | ||
| 188 | + component: 'Input', | ||
| 189 | + show: false, | ||
| 190 | + }, | ||
| 191 | + { | ||
| 192 | + field: FormFieldsEnum.UNIT, | ||
| 193 | + label: FormFieldsNameEnum.UNIT, | ||
| 194 | + component: 'ApiSelect', | ||
| 195 | + ifShow: ({ model }) => | ||
| 196 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 197 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 198 | + ) && | ||
| 199 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 200 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 201 | + componentProps: ({ formActionType }) => { | ||
| 202 | + const { setFieldsValue } = formActionType; | ||
| 203 | + return { | ||
| 204 | + placeholder: '请选择单位', | ||
| 205 | + api: async (params: Recordable) => { | ||
| 206 | + const list = await findDictItemByCode(params); | ||
| 207 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
| 208 | + return list; | ||
| 209 | + }, | ||
| 210 | + params: { | ||
| 211 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
| 212 | + }, | ||
| 213 | + labelField: 'itemText', | ||
| 214 | + valueField: 'itemValue', | ||
| 215 | + onChange(_, record: Record<'label' | 'value', string>) { | ||
| 216 | + if (record) { | ||
| 217 | + const { label } = record; | ||
| 218 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
| 219 | + } | ||
| 220 | + }, | ||
| 221 | + getPopupContainer: () => document.body, | ||
| 222 | + showSearch: true, | ||
| 223 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
| 224 | + let { label, value } = option; | ||
| 225 | + label = label.toLowerCase(); | ||
| 226 | + value = value.toLowerCase(); | ||
| 227 | + inputValue = inputValue.toLowerCase(); | ||
| 228 | + return label.includes(inputValue) || value.includes(inputValue); | ||
| 229 | + }, | ||
| 230 | + }; | ||
| 231 | + }, | ||
| 232 | + }, | ||
| 233 | + { | ||
| 234 | + field: FormFieldsEnum.BOOL_CLOSE, | ||
| 235 | + component: 'Input', | ||
| 236 | + required: true, | ||
| 237 | + label: FormFieldsNameEnum.BOOL_CLOSE, | ||
| 238 | + componentProps: { | ||
| 239 | + placeholder: '如:关', | ||
| 240 | + }, | ||
| 241 | + defaultValue: '关', | ||
| 242 | + ifShow: ({ model }) => | ||
| 243 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 244 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 245 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
| 246 | + dynamicRules: ({ model }) => { | ||
| 247 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
| 248 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
| 249 | + return [ | ||
| 250 | + { | ||
| 251 | + required: true, | ||
| 252 | + message: `布尔值不能为空`, | ||
| 253 | + }, | ||
| 254 | + { | ||
| 255 | + validator() { | ||
| 256 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 257 | + return Promise.resolve(); | ||
| 258 | + }, | ||
| 259 | + }, | ||
| 260 | + ]; | ||
| 261 | + }, | ||
| 262 | + }, | ||
| 263 | + { | ||
| 264 | + field: FormFieldsEnum.BOOL_OPEN, | ||
| 265 | + component: 'Input', | ||
| 266 | + required: true, | ||
| 267 | + label: FormFieldsNameEnum.BOOL_OPEN, | ||
| 268 | + componentProps: { | ||
| 269 | + placeholder: '如:开', | ||
| 270 | + }, | ||
| 271 | + defaultValue: '开', | ||
| 272 | + ifShow: ({ model }) => | ||
| 273 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 274 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 275 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
| 276 | + dynamicRules: ({ model }) => { | ||
| 277 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
| 278 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
| 279 | + return [ | ||
| 280 | + { | ||
| 281 | + required: true, | ||
| 282 | + message: `布尔值不能为空`, | ||
| 283 | + }, | ||
| 284 | + { | ||
| 285 | + validator() { | ||
| 286 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 287 | + return Promise.resolve(); | ||
| 288 | + }, | ||
| 289 | + }, | ||
| 290 | + ]; | ||
| 291 | + }, | ||
| 292 | + }, | ||
| 293 | + { | ||
| 294 | + field: FormFieldsEnum.LENGTH, | ||
| 295 | + component: 'InputNumber', | ||
| 296 | + required: true, | ||
| 297 | + label: FormFieldsNameEnum.LENGTH, | ||
| 298 | + defaultValue: 1024, | ||
| 299 | + componentProps: { | ||
| 300 | + placeholder: '请输入数据长度', | ||
| 301 | + }, | ||
| 302 | + ifShow: ({ model }) => | ||
| 303 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 304 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 305 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | ||
| 306 | + }, | ||
| 307 | + { | ||
| 308 | + field: FormFieldsEnum.ENUMS_DATA, | ||
| 309 | + label: FormFieldsNameEnum.ENUMS_DATA, | ||
| 310 | + component: 'Input', | ||
| 311 | + slot: FormFieldsEnum.ENUMS_DATA, | ||
| 312 | + changeEvent: 'update:value', | ||
| 313 | + valueField: 'value', | ||
| 314 | + ifShow: ({ model }) => | ||
| 315 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 316 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 317 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | ||
| 318 | + }, | ||
| 319 | + { | ||
| 320 | + field: FormFieldsEnum.STRUCT_DATA, | ||
| 321 | + label: FormFieldsNameEnum.STRUCT_DATA, | ||
| 322 | + component: 'Input', | ||
| 323 | + slot: FormFieldsEnum.STRUCT_DATA, | ||
| 324 | + changeEvent: 'update:value', | ||
| 325 | + valueField: 'value', | ||
| 326 | + required: true, | ||
| 327 | + ifShow: ({ model }) => | ||
| 328 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | ||
| 329 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 330 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 331 | + ), | ||
| 332 | + }, | ||
| 333 | + | ||
| 334 | + { | ||
| 335 | + field: FormFieldsEnum.REMARK, | ||
| 336 | + label: FormFieldsNameEnum.REMARK, | ||
| 337 | + component: 'InputTextArea', | ||
| 338 | + ifShow: showRemark, | ||
| 339 | + componentProps: { | ||
| 340 | + rows: 4, | ||
| 341 | + maxLength: 100, | ||
| 342 | + placeholder: '请输入描述', | ||
| 343 | + }, | ||
| 344 | + }, | ||
| 345 | + ]; | ||
| 346 | +}; |
| 1 | +export { default as DataTypeForm } from './index.vue'; |
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { useForm, BasicForm } from '/@/components/Form'; | ||
| 3 | + import { getFormSchemas } from './config'; | ||
| 4 | + import { EnumList } from '../EnumList'; | ||
| 5 | + import { ref } from 'vue'; | ||
| 6 | + import { StructFormItem } from '../StructFormItem'; | ||
| 7 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 8 | + import { useDataTypeFormData } from './useDataTypeFormData'; | ||
| 9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
| 10 | + | ||
| 11 | + const props = withDefaults( | ||
| 12 | + defineProps<{ | ||
| 13 | + dataType?: DataTypeEnum[]; | ||
| 14 | + mode?: DataActionModeEnum; | ||
| 15 | + showRemark?: boolean; | ||
| 16 | + }>(), | ||
| 17 | + { | ||
| 18 | + dataType: () => Object.values(DataTypeEnum), | ||
| 19 | + showRemark: false, | ||
| 20 | + } | ||
| 21 | + ); | ||
| 22 | + | ||
| 23 | + defineEmits<{ | ||
| 24 | + (event: 'field-value-change', field: string, value: any): void; | ||
| 25 | + }>(); | ||
| 26 | + | ||
| 27 | + const [register, formActionType] = useForm({ | ||
| 28 | + schemas: getFormSchemas(props.dataType, props.showRemark), | ||
| 29 | + layout: 'vertical', | ||
| 30 | + showActionButtonGroup: false, | ||
| 31 | + }); | ||
| 32 | + | ||
| 33 | + const enumListRef = ref<InstanceType<typeof EnumList>>(); | ||
| 34 | + | ||
| 35 | + const { getFieldsValue, setFieldsValue, validate, resetFieldsValue } = useDataTypeFormData({ | ||
| 36 | + formActionType, | ||
| 37 | + enumListRef, | ||
| 38 | + }); | ||
| 39 | + | ||
| 40 | + defineExpose({ getFieldsValue, setFieldsValue, validate, resetFieldsValue }); | ||
| 41 | +</script> | ||
| 42 | + | ||
| 43 | +<template> | ||
| 44 | + <BasicForm | ||
| 45 | + @register="register" | ||
| 46 | + class="data-type-form" | ||
| 47 | + :disabled="mode === DataActionModeEnum.READ" | ||
| 48 | + @field-value-change="(field, value) => $emit('field-value-change', field, value)" | ||
| 49 | + > | ||
| 50 | + <template #enumsData="{ model, field }"> | ||
| 51 | + <EnumList | ||
| 52 | + ref="enumListRef" | ||
| 53 | + v-model:value="model[field]" | ||
| 54 | + :disabled="mode === DataActionModeEnum.READ" | ||
| 55 | + /> | ||
| 56 | + </template> | ||
| 57 | + <template #structData="{ model, field }"> | ||
| 58 | + <StructFormItem | ||
| 59 | + v-model:value="model[field]" | ||
| 60 | + :mode="mode" | ||
| 61 | + :dataType="[ | ||
| 62 | + DataTypeEnum.BOOL, | ||
| 63 | + DataTypeEnum.ENUM, | ||
| 64 | + DataTypeEnum.NUMBER_DOUBLE, | ||
| 65 | + DataTypeEnum.NUMBER_INT, | ||
| 66 | + DataTypeEnum.STRING, | ||
| 67 | + ]" | ||
| 68 | + /> | ||
| 69 | + </template> | ||
| 70 | + </BasicForm> | ||
| 71 | +</template> | ||
| 72 | + | ||
| 73 | +<style lang="less" scoped> | ||
| 74 | + .data-type-form { | ||
| 75 | + :deep(.ant-input-number) { | ||
| 76 | + width: 100%; | ||
| 77 | + } | ||
| 78 | + } | ||
| 79 | +</style> | ||
| 80 | +./config |
| 1 | +import { Ref, unref } from 'vue'; | ||
| 2 | +import { FormActionType } from '/@/components/Form'; | ||
| 3 | +import EnumList from './EnumList.vue'; | ||
| 4 | +import { DefineComponentsBasicExpose } from '/#/utils'; | ||
| 5 | +import { DataTypeFormGetFieldsValueType } from './config'; | ||
| 6 | +import { DataType, Specs, StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 7 | +import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 8 | +import { isObject } from '/@/utils/is'; | ||
| 9 | + | ||
| 10 | +interface UseDataTypeFormDataParams { | ||
| 11 | + formActionType: FormActionType; | ||
| 12 | + enumListRef: Ref<InstanceType<typeof EnumList> | undefined>; | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +function getDataType(value: DataTypeFormGetFieldsValueType, enumListFormValue?: Specs[]): DataType { | ||
| 16 | + const { dataType, valueRange, unit, unitName, step, length, boolClose, boolOpen, structData } = | ||
| 17 | + value; | ||
| 18 | + | ||
| 19 | + const basic: DataType = { type: dataType }; | ||
| 20 | + | ||
| 21 | + if (dataType === DataTypeEnum.BOOL) { | ||
| 22 | + basic.specs = { | ||
| 23 | + boolClose, | ||
| 24 | + boolOpen, | ||
| 25 | + }; | ||
| 26 | + } else if (dataType === DataTypeEnum.ENUM) { | ||
| 27 | + basic.specsList = enumListFormValue; | ||
| 28 | + } else if ([DataTypeEnum.NUMBER_DOUBLE, DataTypeEnum.NUMBER_INT].includes(dataType)) { | ||
| 29 | + basic.specs = { | ||
| 30 | + valueRange, | ||
| 31 | + unit, | ||
| 32 | + unitName, | ||
| 33 | + step, | ||
| 34 | + }; | ||
| 35 | + } else if (dataType === DataTypeEnum.STRING) { | ||
| 36 | + basic.specs = { | ||
| 37 | + length, | ||
| 38 | + }; | ||
| 39 | + } else if (dataType === DataTypeEnum.STRUCT) { | ||
| 40 | + basic.specs = structData || []; | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + return basic; | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +export function useDataTypeFormData({ | ||
| 47 | + formActionType, | ||
| 48 | + enumListRef, | ||
| 49 | +}: UseDataTypeFormDataParams): DefineComponentsBasicExpose<StructJSON> { | ||
| 50 | + const getFieldsValue = (): StructJSON => { | ||
| 51 | + const value = formActionType.getFieldsValue() as DataTypeFormGetFieldsValueType; | ||
| 52 | + const enumListFormValue = unref(enumListRef)?.getFieldsValue(); | ||
| 53 | + const { functionName, identifier, remark } = value; | ||
| 54 | + | ||
| 55 | + return { | ||
| 56 | + functionName, | ||
| 57 | + identifier, | ||
| 58 | + remark, | ||
| 59 | + dataType: getDataType(value, enumListFormValue), | ||
| 60 | + }; | ||
| 61 | + }; | ||
| 62 | + | ||
| 63 | + const setFieldsValue = (record: StructJSON) => { | ||
| 64 | + const { dataType, identifier, functionName, remark } = record; | ||
| 65 | + const { type, specs, specsList = [] } = dataType || {}; | ||
| 66 | + | ||
| 67 | + // 兼容处理 unit | ||
| 68 | + if (isObject(specs) && isObject((specs as Specs).unit)) { | ||
| 69 | + (specs as Specs).unit = ((specs as Specs).unit as unknown as Record<'value', string>)?.value; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + // 兼容处理 length 数据长度类型 | ||
| 73 | + if (isObject(specs) && isObject((specs as Specs).length)) { | ||
| 74 | + (specs as Specs).length = Number((specs as Specs).length); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + formActionType.setFieldsValue({ | ||
| 78 | + dataType: type, | ||
| 79 | + identifier, | ||
| 80 | + functionName, | ||
| 81 | + remark, | ||
| 82 | + ...(isObject(specs) ? specs : {}), | ||
| 83 | + structData: type === DataTypeEnum.STRUCT ? specs : [], | ||
| 84 | + enumsData: type === DataTypeEnum.ENUM ? specsList : [], | ||
| 85 | + } as DataTypeFormGetFieldsValueType); | ||
| 86 | + }; | ||
| 87 | + | ||
| 88 | + const validate = async () => { | ||
| 89 | + await formActionType.validate(); | ||
| 90 | + await unref(enumListRef)?.validate?.(); | ||
| 91 | + }; | ||
| 92 | + | ||
| 93 | + const resetFieldsValue = () => { | ||
| 94 | + formActionType.resetFields(); | ||
| 95 | + }; | ||
| 96 | + | ||
| 97 | + return { | ||
| 98 | + validate, | ||
| 99 | + getFieldsValue, | ||
| 100 | + setFieldsValue, | ||
| 101 | + resetFieldsValue, | ||
| 102 | + }; | ||
| 103 | +} |
src/views/device/profiles/components/ObjectModelForm/EnumList/config.ts
renamed from
src/components/Form/src/externalCompns/components/StructForm/EnumList.config.ts
| 1 | -import { FormSchema } from '/@/components/Table'; | 1 | +import { FormSchema } from '/@/components/Form'; |
| 2 | 2 | ||
| 3 | export enum FormFieldsEnum { | 3 | export enum FormFieldsEnum { |
| 4 | VALUE = 'value', | 4 | VALUE = 'value', |
| @@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { | @@ -41,14 +41,7 @@ export const getFormSchemas = (): FormSchema[] => { | ||
| 41 | field: FormFieldsEnum.NAME, | 41 | field: FormFieldsEnum.NAME, |
| 42 | label: '', | 42 | label: '', |
| 43 | component: 'Input', | 43 | component: 'Input', |
| 44 | - rules: [ | ||
| 45 | - { | ||
| 46 | - required: true, | ||
| 47 | - message: `支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符`, | ||
| 48 | - type: 'string', | ||
| 49 | - pattern: /^[a-zA-Z0-9\u4e00-\u9fa5a][\u4e00-\u9fa5a-zA-Z0-9_-]*$/, | ||
| 50 | - }, | ||
| 51 | - ], | 44 | + rules: [{ required: true, message: `参数描述不能为空`, type: 'string' }], |
| 52 | componentProps: () => { | 45 | componentProps: () => { |
| 53 | return { | 46 | return { |
| 54 | placeholder: '对该枚举项的描述', | 47 | placeholder: '对该枚举项的描述', |
| 1 | +export { default as EnumList } from './index.vue'; |
src/views/device/profiles/components/ObjectModelForm/EnumList/index.vue
renamed from
src/components/Form/src/externalCompns/components/StructForm/EnumList.vue
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | import { Button, Tooltip } from 'ant-design-vue'; | 2 | import { Button, Tooltip } from 'ant-design-vue'; |
| 3 | import { computed, nextTick, ref, unref, watch } from 'vue'; | 3 | import { computed, nextTick, ref, unref, watch } from 'vue'; |
| 4 | - import { useForm, BasicForm } from '/@/components/Form'; | 4 | + import { useForm, BasicForm, FormActionType } from '/@/components/Form'; |
| 5 | import { Specs } from '/@/api/device/model/modelOfMatterModel'; | 5 | import { Specs } from '/@/api/device/model/modelOfMatterModel'; |
| 6 | import { Icon } from '/@/components/Icon'; | 6 | import { Icon } from '/@/components/Icon'; |
| 7 | - import { getFormSchemas } from './EnumList.config'; | ||
| 8 | - import { FormActionType } from '../../../types/form'; | 7 | + import { getFormSchemas } from './config'; |
| 9 | import { buildUUID } from '/@/utils/uuid'; | 8 | import { buildUUID } from '/@/utils/uuid'; |
| 10 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 9 | import { DataTypeEnum } from '/@/enums/objectModelEnum'; |
| 11 | import { isNullOrUnDef } from '/@/utils/is'; | 10 | import { isNullOrUnDef } from '/@/utils/is'; |
| @@ -65,7 +64,6 @@ | @@ -65,7 +64,6 @@ | ||
| 65 | 64 | ||
| 66 | const setFieldsValue = (spaceList: Specs[]) => { | 65 | const setFieldsValue = (spaceList: Specs[]) => { |
| 67 | enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); | 66 | enumsListElRef.value = spaceList.map((item) => ({ uuid: buildUUID(), dataSource: item })); |
| 68 | - | ||
| 69 | nextTick(() => { | 67 | nextTick(() => { |
| 70 | unref(enumsListElRef).forEach((item) => | 68 | unref(enumsListElRef).forEach((item) => |
| 71 | item.formActionType?.setFieldsValue?.(item.dataSource) | 69 | item.formActionType?.setFieldsValue?.(item.dataSource) |
| @@ -76,10 +74,7 @@ | @@ -76,10 +74,7 @@ | ||
| 76 | const handleDeleteEnums = (item: EnumElItemType) => { | 74 | const handleDeleteEnums = (item: EnumElItemType) => { |
| 77 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); | 75 | const index = unref(enumsListElRef).findIndex((temp) => item.uuid === temp.uuid); |
| 78 | 76 | ||
| 79 | - if (~index) { | ||
| 80 | - enumsListElRef.value.splice(index, 1); | ||
| 81 | - validateSameEnum(); | ||
| 82 | - } | 77 | + ~index && enumsListElRef.value.splice(index, 1); |
| 83 | }; | 78 | }; |
| 84 | 79 | ||
| 85 | const handleAddEnums = () => { | 80 | const handleAddEnums = () => { |
| @@ -107,15 +102,17 @@ | @@ -107,15 +102,17 @@ | ||
| 107 | <section class="w-full"> | 102 | <section class="w-full"> |
| 108 | <header class="flex h-8 items-center"> | 103 | <header class="flex h-8 items-center"> |
| 109 | <div class="w-1/2"> | 104 | <div class="w-1/2"> |
| 110 | - <span>参考值</span> | 105 | + <span class="mr-1 text-red-400">*</span> |
| 106 | + <span> 参考值 </span> | ||
| 111 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> | 107 | <Tooltip title="支持整型,取值范围:-2147483648 ~ 2147483647"> |
| 112 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | 108 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
| 113 | </Tooltip> | 109 | </Tooltip> |
| 114 | </div> | 110 | </div> |
| 115 | <div class="w-1/2"> | 111 | <div class="w-1/2"> |
| 116 | - <span>参考描述</span> | 112 | + <span class="mr-1 text-red-400">*</span> |
| 113 | + <span> 参考描述 </span> | ||
| 117 | <Tooltip | 114 | <Tooltip |
| 118 | - title="支持中文、英文大小写、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" | 115 | + title="支持中文、英文大小写、日文、数字、下划线和短划线,必须以中文、英文或数字开头,不超过20个字符" |
| 119 | > | 116 | > |
| 120 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> | 117 | <Icon icon="ant-design:question-circle-outlined" class="cursor-pointer ml-1" /> |
| 121 | </Tooltip> | 118 | </Tooltip> |
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/config.ts
renamed from
src/components/Form/src/externalCompns/components/ExtendDesc/config.ts
| 1 | +import { unref } from 'vue'; | ||
| 2 | +import { useObjectModelFormContext } from '../useObjectModelFormContext'; | ||
| 1 | import { findDictItemByCode } from '/@/api/system/dict'; | 3 | import { findDictItemByCode } from '/@/api/system/dict'; |
| 2 | import { FormSchema } from '/@/components/Table'; | 4 | import { FormSchema } from '/@/components/Table'; |
| 3 | import { DictEnum } from '/@/enums/dictEnum'; | 5 | import { DictEnum } from '/@/enums/dictEnum'; |
| @@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { | @@ -37,66 +39,62 @@ function getActionTypeByObjectModelType(dataType: DataTypeEnum) { | ||
| 37 | return list; | 39 | return list; |
| 38 | } | 40 | } |
| 39 | 41 | ||
| 40 | -export const formSchemas: FormSchema[] = [ | ||
| 41 | - { | ||
| 42 | - field: FormFieldsEnum.OBJECT_MODEL_TYPE, | ||
| 43 | - component: 'Input', | ||
| 44 | - label: '物模型数据类型', | ||
| 45 | - ifShow: false, | ||
| 46 | - }, | ||
| 47 | - { | ||
| 48 | - field: FormFieldsEnum.REGISTER_ADDRESS, | ||
| 49 | - component: 'RegisterAddressInput', | ||
| 50 | - label: '寄存器地址', | ||
| 51 | - changeEvent: 'update:value', | ||
| 52 | - valueField: 'value', | ||
| 53 | - rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }], | ||
| 54 | - componentProps: { | ||
| 55 | - placeholder: '请输入寄存器地址', | ||
| 56 | - }, | ||
| 57 | - }, | ||
| 58 | - { | ||
| 59 | - field: FormFieldsEnum.DATA_TYPE, | ||
| 60 | - component: 'ApiSelect', | ||
| 61 | - label: '数据格式', | ||
| 62 | - rules: [{ message: '请选择数据格式', required: true }], | ||
| 63 | - defaultValue: RegisterDataTypeEnum.UN_SHORT, | ||
| 64 | - componentProps: { | ||
| 65 | - api: findDictItemByCode, | ||
| 66 | - params: { | ||
| 67 | - dictCode: DictEnum.REGISTER_DATA_FORMAT, | 42 | +export const getFormSchemas = (): FormSchema[] => { |
| 43 | + const { getDataType } = useObjectModelFormContext(); | ||
| 44 | + return [ | ||
| 45 | + { | ||
| 46 | + field: FormFieldsEnum.REGISTER_ADDRESS, | ||
| 47 | + component: 'RegisterAddressInput', | ||
| 48 | + label: '寄存器地址', | ||
| 49 | + changeEvent: 'update:value', | ||
| 50 | + valueField: 'value', | ||
| 51 | + rules: [{ message: '请输入寄存器地址', required: true, type: 'number' }], | ||
| 52 | + componentProps: { | ||
| 53 | + placeholder: '请输入寄存器地址', | ||
| 68 | }, | 54 | }, |
| 69 | - labelField: 'itemText', | ||
| 70 | - valueField: 'itemValue', | ||
| 71 | - placeholder: '请选择数据格式', | ||
| 72 | - getPopupContainer: () => document.body, | ||
| 73 | }, | 55 | }, |
| 74 | - }, | ||
| 75 | - { | ||
| 76 | - field: FormFieldsEnum.ACTION_TYPE, | ||
| 77 | - component: 'Select', | ||
| 78 | - label: '操作类型', | ||
| 79 | - rules: [{ message: '请选择操作类型', required: true }], | ||
| 80 | - componentProps: ({ formModel }) => { | ||
| 81 | - const objectModelType = formModel[FormFieldsEnum.OBJECT_MODEL_TYPE]; | ||
| 82 | - return { | ||
| 83 | - options: getActionTypeByObjectModelType(objectModelType), | ||
| 84 | - placeholder: '请选择操作类型', | 56 | + { |
| 57 | + field: FormFieldsEnum.DATA_TYPE, | ||
| 58 | + component: 'ApiSelect', | ||
| 59 | + label: '数据格式', | ||
| 60 | + rules: [{ message: '请选择数据格式', required: true }], | ||
| 61 | + defaultValue: RegisterDataTypeEnum.UN_SHORT, | ||
| 62 | + componentProps: { | ||
| 63 | + api: findDictItemByCode, | ||
| 64 | + params: { | ||
| 65 | + dictCode: DictEnum.REGISTER_DATA_FORMAT, | ||
| 66 | + }, | ||
| 67 | + labelField: 'itemText', | ||
| 68 | + valueField: 'itemValue', | ||
| 69 | + placeholder: '请选择数据格式', | ||
| 85 | getPopupContainer: () => document.body, | 70 | getPopupContainer: () => document.body, |
| 86 | - }; | 71 | + }, |
| 87 | }, | 72 | }, |
| 88 | - }, | ||
| 89 | - { | ||
| 90 | - field: FormFieldsEnum.ZOOM_FACTOR, | ||
| 91 | - component: 'InputNumber', | ||
| 92 | - label: '缩放因子', | ||
| 93 | - helpMessage: ['缩放因子不能为0,默认为1'], | ||
| 94 | - defaultValue: 1, | ||
| 95 | - ifShow: ({ model }) => | ||
| 96 | - ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]), | ||
| 97 | - componentProps: { | ||
| 98 | - min: 1, | ||
| 99 | - placeholder: '请输入缩放因子', | 73 | + { |
| 74 | + field: FormFieldsEnum.ACTION_TYPE, | ||
| 75 | + component: 'Select', | ||
| 76 | + label: '操作类型', | ||
| 77 | + rules: [{ message: '请选择操作类型', required: true }], | ||
| 78 | + componentProps: () => { | ||
| 79 | + return { | ||
| 80 | + options: getActionTypeByObjectModelType(unref(getDataType)), | ||
| 81 | + placeholder: '请选择操作类型', | ||
| 82 | + getPopupContainer: () => document.body, | ||
| 83 | + }; | ||
| 84 | + }, | ||
| 85 | + }, | ||
| 86 | + { | ||
| 87 | + field: FormFieldsEnum.ZOOM_FACTOR, | ||
| 88 | + component: 'InputNumber', | ||
| 89 | + label: '缩放因子', | ||
| 90 | + helpMessage: ['缩放因子不能为0,默认为1'], | ||
| 91 | + defaultValue: 1, | ||
| 92 | + ifShow: ({ model }) => | ||
| 93 | + ![DataTypeEnum.BOOL, DataTypeEnum.STRING].includes(model[FormFieldsEnum.OBJECT_MODEL_TYPE]), | ||
| 94 | + componentProps: { | ||
| 95 | + min: 1, | ||
| 96 | + placeholder: '请输入缩放因子', | ||
| 97 | + }, | ||
| 100 | }, | 98 | }, |
| 101 | - }, | ||
| 102 | -]; | 99 | + ]; |
| 100 | +}; |
| 1 | +export { default as ExtendDesc } from './index.vue'; |
src/views/device/profiles/components/ObjectModelForm/ExtendDesc/index.vue
renamed from
src/components/Form/src/externalCompns/components/ExtendDesc/index.vue
| @@ -4,19 +4,18 @@ | @@ -4,19 +4,18 @@ | ||
| 4 | import { BasicForm, useForm } from '/@/components/Form'; | 4 | import { BasicForm, useForm } from '/@/components/Form'; |
| 5 | import { BasicModal } from '/@/components/Modal'; | 5 | import { BasicModal } from '/@/components/Modal'; |
| 6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; | 6 | import { PlusCircleOutlined } from '@ant-design/icons-vue'; |
| 7 | - import { FormFieldsEnum, formSchemas } from './config'; | ||
| 8 | - import { DataTypeEnum } from '/@/enums/objectModelEnum'; | 7 | + import { getFormSchemas } from './config'; |
| 8 | + import { ExtensionDesc } from '/@/api/device/model/modelOfMatterModel'; | ||
| 9 | 9 | ||
| 10 | const show = ref(false); | 10 | const show = ref(false); |
| 11 | 11 | ||
| 12 | const props = withDefaults( | 12 | const props = withDefaults( |
| 13 | defineProps<{ | 13 | defineProps<{ |
| 14 | - value?: object; | 14 | + value?: ExtensionDesc; |
| 15 | disabled?: boolean; | 15 | disabled?: boolean; |
| 16 | - dataType?: DataTypeEnum; | ||
| 17 | }>(), | 16 | }>(), |
| 18 | { | 17 | { |
| 19 | - value: () => ({}), | 18 | + value: () => ({} as ExtensionDesc), |
| 20 | } | 19 | } |
| 21 | ); | 20 | ); |
| 22 | 21 | ||
| @@ -24,7 +23,7 @@ | @@ -24,7 +23,7 @@ | ||
| 24 | 23 | ||
| 25 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = | 24 | const [registerForm, { setFieldsValue, getFieldsValue, setProps, validate, resetFields }] = |
| 26 | useForm({ | 25 | useForm({ |
| 27 | - schemas: formSchemas, | 26 | + schemas: getFormSchemas(), |
| 28 | showActionButtonGroup: false, | 27 | showActionButtonGroup: false, |
| 29 | }); | 28 | }); |
| 30 | 29 | ||
| @@ -39,7 +38,6 @@ | @@ -39,7 +38,6 @@ | ||
| 39 | const handleSubmit = async () => { | 38 | const handleSubmit = async () => { |
| 40 | await validate(); | 39 | await validate(); |
| 41 | const value = getFieldsValue(); | 40 | const value = getFieldsValue(); |
| 42 | - Reflect.deleteProperty(value, FormFieldsEnum.OBJECT_MODEL_TYPE); | ||
| 43 | emit('update:value', value); | 41 | emit('update:value', value); |
| 44 | show.value = false; | 42 | show.value = false; |
| 45 | }; | 43 | }; |
| @@ -47,22 +45,15 @@ | @@ -47,22 +45,15 @@ | ||
| 47 | watch(show, async (value) => { | 45 | watch(show, async (value) => { |
| 48 | if (value) { | 46 | if (value) { |
| 49 | await nextTick(); | 47 | await nextTick(); |
| 50 | - setFieldsValue({ [FormFieldsEnum.OBJECT_MODEL_TYPE]: props.dataType }); | 48 | + setFieldsValue({ ...props.value }); |
| 51 | } | 49 | } |
| 52 | }); | 50 | }); |
| 53 | - | ||
| 54 | - watch( | ||
| 55 | - () => props.value, | ||
| 56 | - (value) => { | ||
| 57 | - setFieldsValue(value); | ||
| 58 | - } | ||
| 59 | - ); | ||
| 60 | </script> | 51 | </script> |
| 61 | 52 | ||
| 62 | <template> | 53 | <template> |
| 63 | <section> | 54 | <section> |
| 64 | <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> | 55 | <Button type="link" @click="handleClick"><PlusCircleOutlined />新增扩展描述</Button> |
| 65 | - <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit"> | 56 | + <BasicModal title="扩展描述" v-model:visible="show" @ok="handleSubmit" :show-ok-btn="!disabled"> |
| 66 | <BasicForm class="extension-form" @register="registerForm" /> | 57 | <BasicForm class="extension-form" @register="registerForm" /> |
| 67 | </BasicModal> | 58 | </BasicModal> |
| 68 | </section> | 59 | </section> |
src/views/device/profiles/components/ObjectModelForm/StructFormItem/CreateStructModal.vue
0 → 100644
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
| 3 | + import { ref, unref } from 'vue'; | ||
| 4 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 5 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
| 6 | + import { DataTypeForm } from '../DataTypeForm'; | ||
| 7 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 8 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 9 | + | ||
| 10 | + const props = withDefaults( | ||
| 11 | + defineProps<{ value?: StructJSON[]; dataType?: DataTypeEnum[]; mode?: DataActionModeEnum }>(), | ||
| 12 | + { | ||
| 13 | + value: () => [], | ||
| 14 | + dataType: () => Object.values(DataTypeEnum), | ||
| 15 | + } | ||
| 16 | + ); | ||
| 17 | + | ||
| 18 | + const dataTypeFormRef = ref<InstanceType<typeof DataTypeForm>>(); | ||
| 19 | + | ||
| 20 | + const emits = defineEmits(['register', 'complete']); | ||
| 21 | + | ||
| 22 | + const currentMode = ref(DataActionModeEnum.CREATE); | ||
| 23 | + const [registerModal] = useModalInner(({ mode, record }: ModalParamsType<StructJSON>) => { | ||
| 24 | + unref(dataTypeFormRef)?.resetFieldsValue?.(); | ||
| 25 | + currentMode.value = mode; | ||
| 26 | + if (mode === DataActionModeEnum.UPDATE) { | ||
| 27 | + unref(dataTypeFormRef)?.setFieldsValue?.(record); | ||
| 28 | + } | ||
| 29 | + }); | ||
| 30 | + | ||
| 31 | + const { createMessage } = useMessage(); | ||
| 32 | + | ||
| 33 | + const handleValidateHasSameIdentifier = (formValue?: StructJSON) => { | ||
| 34 | + const { identifier } = formValue || {}; | ||
| 35 | + if ( | ||
| 36 | + unref(currentMode) === DataActionModeEnum.CREATE && | ||
| 37 | + props.value.filter((item) => item.identifier === identifier).length >= 1 | ||
| 38 | + ) { | ||
| 39 | + const message = '存在一致的标识符'; | ||
| 40 | + createMessage.warn(message); | ||
| 41 | + return Promise.reject(message); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + return Promise.resolve(); | ||
| 45 | + }; | ||
| 46 | + | ||
| 47 | + const handleOk = async () => { | ||
| 48 | + await unref(dataTypeFormRef)?.validate?.(); | ||
| 49 | + const value = unref(dataTypeFormRef)?.getFieldsValue?.(); | ||
| 50 | + await handleValidateHasSameIdentifier(value); | ||
| 51 | + emits('complete', value); | ||
| 52 | + }; | ||
| 53 | +</script> | ||
| 54 | + | ||
| 55 | +<template> | ||
| 56 | + <BasicModal | ||
| 57 | + @register="registerModal" | ||
| 58 | + title="新增参数" | ||
| 59 | + @ok="handleOk" | ||
| 60 | + :width="480" | ||
| 61 | + :show-ok-btn="mode !== DataActionModeEnum.READ" | ||
| 62 | + > | ||
| 63 | + <DataTypeForm ref="dataTypeFormRef" :data-type="dataType" show-remark :mode="mode" /> | ||
| 64 | + </BasicModal> | ||
| 65 | +</template> |
| 1 | +import { FormFieldsEnum, FormFieldsNameEnum } from '../config'; | ||
| 2 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 3 | +import { FormSchema } from '/@/components/Form'; | ||
| 4 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
| 5 | +import { DataTypeEnum, FunctionTypeEnum, FunctionTypeNameEnum } from '/@/enums/objectModelEnum'; | ||
| 6 | +import { isNullOrUnDef } from '/@/utils/is'; | ||
| 7 | + | ||
| 8 | +export const validateValueRange = (_rule, value: Record<'min' | 'max', number>, _callback) => { | ||
| 9 | + value = value || {}; | ||
| 10 | + const { min, max } = value; | ||
| 11 | + if (min > max) { | ||
| 12 | + return Promise.reject('最大值小于最小值'); | ||
| 13 | + } | ||
| 14 | + return Promise.resolve(); | ||
| 15 | +}; | ||
| 16 | + | ||
| 17 | +export const getFormSchemas = (): FormSchema[] => { | ||
| 18 | + return [ | ||
| 19 | + { | ||
| 20 | + field: FormFieldsEnum.FUNCTION_TYPE, | ||
| 21 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | ||
| 22 | + component: 'Segmented', | ||
| 23 | + defaultValue: FunctionTypeEnum.PROPERTIES, | ||
| 24 | + required: true, | ||
| 25 | + componentProps: ({ formActionType }) => { | ||
| 26 | + return { | ||
| 27 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
| 28 | + title: FunctionTypeNameEnum[key], | ||
| 29 | + value: FunctionTypeEnum[key], | ||
| 30 | + })), | ||
| 31 | + onChange() { | ||
| 32 | + const { setFieldsValue, clearValidate } = formActionType; | ||
| 33 | + setFieldsValue({ | ||
| 34 | + [FormFieldsEnum.INPUT_DATA]: [], | ||
| 35 | + [FormFieldsEnum.OUTPUT_DATA]: [], | ||
| 36 | + [FormFieldsEnum.STRUCT_DATA]: [], | ||
| 37 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
| 38 | + }); | ||
| 39 | + clearValidate(); | ||
| 40 | + }, | ||
| 41 | + }; | ||
| 42 | + }, | ||
| 43 | + }, | ||
| 44 | + { | ||
| 45 | + field: FormFieldsEnum.FUNCTION_NAME, | ||
| 46 | + label: FormFieldsNameEnum.FUNCTION_NAME, | ||
| 47 | + component: 'Input', | ||
| 48 | + required: true, | ||
| 49 | + dynamicRules: ({ values }) => { | ||
| 50 | + return [ | ||
| 51 | + { required: true, message: '请输入功能名称' }, | ||
| 52 | + { | ||
| 53 | + validator: () => { | ||
| 54 | + const reg = /[,,]+/; | ||
| 55 | + if (reg.test(values?.[FormFieldsEnum.FUNCTION_NAME])) { | ||
| 56 | + return Promise.reject(`${FormFieldsNameEnum.FUNCTION_NAME}不能包含逗号`); | ||
| 57 | + } | ||
| 58 | + return Promise.resolve(); | ||
| 59 | + }, | ||
| 60 | + }, | ||
| 61 | + ]; | ||
| 62 | + }, | ||
| 63 | + componentProps: { | ||
| 64 | + placeholder: `请输入${FormFieldsNameEnum.FUNCTION_NAME}`, | ||
| 65 | + }, | ||
| 66 | + }, | ||
| 67 | + { | ||
| 68 | + field: FormFieldsEnum.IDENTIFIER, | ||
| 69 | + label: FormFieldsNameEnum.IDENTIFIER, | ||
| 70 | + required: true, | ||
| 71 | + component: 'Input', | ||
| 72 | + componentProps: { | ||
| 73 | + maxLength: 128, | ||
| 74 | + placeholder: '请输入标识符', | ||
| 75 | + }, | ||
| 76 | + dynamicRules: ({ values }) => { | ||
| 77 | + return [ | ||
| 78 | + { required: true, message: '请输入标识符' }, | ||
| 79 | + { | ||
| 80 | + validator: () => { | ||
| 81 | + const reg = /[,,]+/; | ||
| 82 | + if (reg.test(values?.[FormFieldsEnum.IDENTIFIER])) { | ||
| 83 | + return Promise.reject(`${FormFieldsNameEnum.IDENTIFIER}不能包含逗号`); | ||
| 84 | + } | ||
| 85 | + return Promise.resolve(); | ||
| 86 | + }, | ||
| 87 | + }, | ||
| 88 | + ]; | ||
| 89 | + }, | ||
| 90 | + }, | ||
| 91 | + { | ||
| 92 | + field: FormFieldsEnum.DATA_TYPE, | ||
| 93 | + label: FormFieldsNameEnum.DATA_TYPE, | ||
| 94 | + required: true, | ||
| 95 | + component: 'ApiSelect', | ||
| 96 | + defaultValue: DataTypeEnum.NUMBER_INT, | ||
| 97 | + ifShow: ({ model }) => | ||
| 98 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 99 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 100 | + ), | ||
| 101 | + componentProps: () => { | ||
| 102 | + return { | ||
| 103 | + placeholder: '请选择数据类型', | ||
| 104 | + api: async (params: Recordable) => { | ||
| 105 | + const result = await findDictItemByCode(params); | ||
| 106 | + return result; | ||
| 107 | + }, | ||
| 108 | + params: { | ||
| 109 | + dictCode: DictEnum.DATA_TYPE, | ||
| 110 | + }, | ||
| 111 | + labelField: 'itemText', | ||
| 112 | + valueField: 'itemValue', | ||
| 113 | + getPopupContainer: () => document.body, | ||
| 114 | + }; | ||
| 115 | + }, | ||
| 116 | + }, | ||
| 117 | + { | ||
| 118 | + field: FormFieldsEnum.VALUE_RANGE, | ||
| 119 | + label: FormFieldsNameEnum.VALUE_RANGE, | ||
| 120 | + component: 'CustomMinMaxInput', | ||
| 121 | + valueField: 'value', | ||
| 122 | + changeEvent: 'update:value', | ||
| 123 | + ifShow: ({ model }) => | ||
| 124 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 125 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 126 | + ) && | ||
| 127 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 128 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 129 | + rules: [{ validator: validateValueRange }], | ||
| 130 | + }, | ||
| 131 | + { | ||
| 132 | + field: FormFieldsEnum.STEP, | ||
| 133 | + label: FormFieldsNameEnum.STEP, | ||
| 134 | + component: 'InputNumber', | ||
| 135 | + componentProps: { | ||
| 136 | + maxLength: 255, | ||
| 137 | + placeholder: '请输入步长', | ||
| 138 | + min: 1, | ||
| 139 | + formatter: (value: number | string) => { | ||
| 140 | + return value ? Math.floor(Number(value)) : value; | ||
| 141 | + }, | ||
| 142 | + }, | ||
| 143 | + ifShow: ({ model }) => | ||
| 144 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 145 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 146 | + ) && | ||
| 147 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 148 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 149 | + dynamicRules: ({ model }) => { | ||
| 150 | + const valueRange = model[FormFieldsEnum.VALUE_RANGE] || {}; | ||
| 151 | + const { min, max } = valueRange; | ||
| 152 | + const step = model[FormFieldsEnum.STEP]; | ||
| 153 | + return [ | ||
| 154 | + { | ||
| 155 | + validator: () => { | ||
| 156 | + if ([min, max].every(isNullOrUnDef)) return Promise.resolve(); | ||
| 157 | + if (step > max - min) { | ||
| 158 | + return Promise.reject('步长不能大于取值范围的差值'); | ||
| 159 | + } | ||
| 160 | + return Promise.resolve(); | ||
| 161 | + }, | ||
| 162 | + }, | ||
| 163 | + ]; | ||
| 164 | + }, | ||
| 165 | + }, | ||
| 166 | + { | ||
| 167 | + field: FormFieldsEnum.UNIT_NAME, | ||
| 168 | + label: FormFieldsNameEnum.UNIT_NAME, | ||
| 169 | + component: 'Input', | ||
| 170 | + show: false, | ||
| 171 | + }, | ||
| 172 | + { | ||
| 173 | + field: FormFieldsEnum.UNIT, | ||
| 174 | + label: FormFieldsNameEnum.UNIT, | ||
| 175 | + component: 'ApiSelect', | ||
| 176 | + componentProps: ({ formActionType }) => { | ||
| 177 | + const { setFieldsValue } = formActionType; | ||
| 178 | + return { | ||
| 179 | + placeholder: '请选择单位', | ||
| 180 | + api: async (params: Recordable) => { | ||
| 181 | + const list = await findDictItemByCode(params); | ||
| 182 | + list.map((item) => (item.itemText = `${item.itemText} / ${item.itemValue}`)); | ||
| 183 | + return list; | ||
| 184 | + }, | ||
| 185 | + params: { | ||
| 186 | + dictCode: DictEnum.ATTRIBUTE_UNIT, | ||
| 187 | + }, | ||
| 188 | + labelInValue: true, | ||
| 189 | + labelField: 'itemText', | ||
| 190 | + valueField: 'itemValue', | ||
| 191 | + onChange(_, record: Record<'label' | 'value', string>) { | ||
| 192 | + if (record) { | ||
| 193 | + const { label } = record; | ||
| 194 | + setFieldsValue({ [FormFieldsEnum.UNIT_NAME]: label }); | ||
| 195 | + } | ||
| 196 | + }, | ||
| 197 | + getPopupContainer: () => document.body, | ||
| 198 | + showSearch: true, | ||
| 199 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => { | ||
| 200 | + let { label, value } = option; | ||
| 201 | + label = label.toLowerCase(); | ||
| 202 | + value = value.toLowerCase(); | ||
| 203 | + inputValue = inputValue.toLowerCase(); | ||
| 204 | + return label.includes(inputValue) || value.includes(inputValue); | ||
| 205 | + }, | ||
| 206 | + }; | ||
| 207 | + }, | ||
| 208 | + ifShow: ({ model }) => | ||
| 209 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 210 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 211 | + ) && | ||
| 212 | + (model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_INT || | ||
| 213 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.NUMBER_DOUBLE), | ||
| 214 | + }, | ||
| 215 | + { | ||
| 216 | + field: FormFieldsEnum.BOOL_CLOSE, | ||
| 217 | + component: 'Input', | ||
| 218 | + required: true, | ||
| 219 | + label: FormFieldsNameEnum.BOOL_CLOSE, | ||
| 220 | + componentProps: { | ||
| 221 | + placeholder: '如:关', | ||
| 222 | + }, | ||
| 223 | + defaultValue: '关', | ||
| 224 | + ifShow: ({ model }) => | ||
| 225 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 226 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 227 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
| 228 | + dynamicRules: ({ model }) => { | ||
| 229 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
| 230 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
| 231 | + return [ | ||
| 232 | + { | ||
| 233 | + required: true, | ||
| 234 | + message: `布尔值不能为空`, | ||
| 235 | + }, | ||
| 236 | + { | ||
| 237 | + validator() { | ||
| 238 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 239 | + return Promise.resolve(); | ||
| 240 | + }, | ||
| 241 | + }, | ||
| 242 | + ]; | ||
| 243 | + }, | ||
| 244 | + }, | ||
| 245 | + { | ||
| 246 | + field: FormFieldsEnum.BOOL_OPEN, | ||
| 247 | + component: 'Input', | ||
| 248 | + required: true, | ||
| 249 | + label: FormFieldsNameEnum.BOOL_OPEN, | ||
| 250 | + componentProps: { | ||
| 251 | + placeholder: '如:开', | ||
| 252 | + }, | ||
| 253 | + defaultValue: '开', | ||
| 254 | + ifShow: ({ model }) => | ||
| 255 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 256 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 257 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.BOOL, | ||
| 258 | + dynamicRules: ({ model }) => { | ||
| 259 | + const close = model[FormFieldsEnum.BOOL_CLOSE]; | ||
| 260 | + const open = model[FormFieldsEnum.BOOL_OPEN]; | ||
| 261 | + return [ | ||
| 262 | + { | ||
| 263 | + required: true, | ||
| 264 | + message: `布尔值不能为空`, | ||
| 265 | + }, | ||
| 266 | + { | ||
| 267 | + validator() { | ||
| 268 | + if (open === close) return Promise.reject('布尔值不能相同'); | ||
| 269 | + return Promise.resolve(); | ||
| 270 | + }, | ||
| 271 | + }, | ||
| 272 | + ]; | ||
| 273 | + }, | ||
| 274 | + }, | ||
| 275 | + { | ||
| 276 | + field: FormFieldsEnum.LENGTH, | ||
| 277 | + component: 'Input', | ||
| 278 | + required: true, | ||
| 279 | + label: FormFieldsNameEnum.LENGTH, | ||
| 280 | + defaultValue: '10240', | ||
| 281 | + colProps: { | ||
| 282 | + span: 8, | ||
| 283 | + }, | ||
| 284 | + componentProps: { | ||
| 285 | + placeholder: '请输入数据长度', | ||
| 286 | + }, | ||
| 287 | + renderComponentContent: () => { | ||
| 288 | + return { | ||
| 289 | + suffix: () => '字节', | ||
| 290 | + }; | ||
| 291 | + }, | ||
| 292 | + ifShow: ({ model }) => | ||
| 293 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 294 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 295 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRING, | ||
| 296 | + }, | ||
| 297 | + { | ||
| 298 | + field: FormFieldsEnum.ENUMS_DATA, | ||
| 299 | + label: FormFieldsNameEnum.ENUMS_DATA, | ||
| 300 | + component: 'Input', | ||
| 301 | + slot: FormFieldsEnum.ENUMS_DATA, | ||
| 302 | + changeEvent: 'update:value', | ||
| 303 | + valueField: 'value', | ||
| 304 | + ifShow: ({ model }) => | ||
| 305 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 306 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 307 | + ) && model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.ENUM, | ||
| 308 | + }, | ||
| 309 | + { | ||
| 310 | + field: FormFieldsEnum.STRUCT_DATA, | ||
| 311 | + label: FormFieldsNameEnum.STRUCT_DATA, | ||
| 312 | + component: 'Input', | ||
| 313 | + slot: FormFieldsEnum.STRUCT_DATA, | ||
| 314 | + changeEvent: 'update:value', | ||
| 315 | + valueField: 'value', | ||
| 316 | + required: true, | ||
| 317 | + ifShow: ({ model }) => | ||
| 318 | + model[FormFieldsEnum.DATA_TYPE] === DataTypeEnum.STRUCT && | ||
| 319 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 320 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 321 | + ), | ||
| 322 | + }, | ||
| 323 | + ]; | ||
| 324 | +}; |
| 1 | +export { default as StructFormItem } from './index.vue'; |
| 1 | +<script setup lang="ts"> | ||
| 2 | + import { Button, Divider } from 'ant-design-vue'; | ||
| 3 | + import { cloneDeep } from 'lodash'; | ||
| 4 | + import { toRaw, unref } from 'vue'; | ||
| 5 | + import CreateStructModal from './CreateStructModal.vue'; | ||
| 6 | + import { StructJSON } from '/@/api/device/model/modelOfMatterModel'; | ||
| 7 | + import { useModal } from '/@/components/Modal'; | ||
| 8 | + import { DataTypeEnum } from '/@/enums/objectModelEnum'; | ||
| 9 | + import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
| 10 | + | ||
| 11 | + const props = withDefaults( | ||
| 12 | + defineProps<{ | ||
| 13 | + buttonName?: string; | ||
| 14 | + disabled?: boolean; | ||
| 15 | + value?: StructJSON[]; | ||
| 16 | + dataType?: DataTypeEnum[]; | ||
| 17 | + mode?: DataActionModeEnum; | ||
| 18 | + }>(), | ||
| 19 | + { | ||
| 20 | + buttonName: '+ 新增参数', | ||
| 21 | + value: () => [], | ||
| 22 | + dataType: () => Object.values(DataTypeEnum), | ||
| 23 | + } | ||
| 24 | + ); | ||
| 25 | + | ||
| 26 | + const emits = defineEmits(['update:value']); | ||
| 27 | + | ||
| 28 | + const [registerModal, { openModal, closeModal }] = useModal(); | ||
| 29 | + | ||
| 30 | + const handleOpenCreateModal = () => { | ||
| 31 | + openModal(true, { | ||
| 32 | + mode: DataActionModeEnum.CREATE, | ||
| 33 | + } as ModalParamsType); | ||
| 34 | + }; | ||
| 35 | + | ||
| 36 | + const handleEdit = (item: StructJSON) => { | ||
| 37 | + openModal(true, { | ||
| 38 | + mode: DataActionModeEnum.UPDATE, | ||
| 39 | + record: cloneDeep(item), | ||
| 40 | + } as ModalParamsType); | ||
| 41 | + }; | ||
| 42 | + | ||
| 43 | + const handleDelete = (item: StructJSON) => { | ||
| 44 | + const index = props.value.findIndex((temp) => item.identifier === temp.identifier); | ||
| 45 | + | ||
| 46 | + if (~index) { | ||
| 47 | + const _values = cloneDeep(props.value); | ||
| 48 | + _values.splice(index, 1); | ||
| 49 | + emits('update:value', _values); | ||
| 50 | + } | ||
| 51 | + }; | ||
| 52 | + | ||
| 53 | + const handleEditComplete = (value: StructJSON) => { | ||
| 54 | + const _value = cloneDeep(toRaw(unref(props.value))); | ||
| 55 | + const index = _value.findIndex((item) => item.identifier === value.identifier); | ||
| 56 | + ~index ? _value.splice(index, 1, value) : _value.push(value); | ||
| 57 | + emits('update:value', _value); | ||
| 58 | + closeModal(); | ||
| 59 | + }; | ||
| 60 | +</script> | ||
| 61 | + | ||
| 62 | +<template> | ||
| 63 | + <section> | ||
| 64 | + <main> | ||
| 65 | + <div | ||
| 66 | + v-for="item in value" | ||
| 67 | + :key="item.identifier" | ||
| 68 | + class="flex items-center justify-between px-2 py-1 mb-2 bg-blue-50 text-gray-600" | ||
| 69 | + > | ||
| 70 | + <div> | ||
| 71 | + <span>参数名称:</span> | ||
| 72 | + <span class="ml-1">{{ item.functionName }}</span> | ||
| 73 | + </div> | ||
| 74 | + <div> | ||
| 75 | + <Button type="link" @click="handleEdit(item)"> | ||
| 76 | + {{ mode === DataActionModeEnum.READ ? '查看' : '编辑' }} | ||
| 77 | + </Button> | ||
| 78 | + <Divider type="vertical" /> | ||
| 79 | + <Button | ||
| 80 | + type="link" | ||
| 81 | + @click="handleDelete(item)" | ||
| 82 | + :disabled="mode === DataActionModeEnum.READ" | ||
| 83 | + > | ||
| 84 | + 删除 | ||
| 85 | + </Button> | ||
| 86 | + </div> | ||
| 87 | + </div> | ||
| 88 | + </main> | ||
| 89 | + <Button type="link" @click="handleOpenCreateModal" :disabled="mode === DataActionModeEnum.READ"> | ||
| 90 | + {{ buttonName }} | ||
| 91 | + </Button> | ||
| 92 | + <CreateStructModal | ||
| 93 | + :value="value" | ||
| 94 | + @register="registerModal" | ||
| 95 | + @complete="handleEditComplete" | ||
| 96 | + :dataType="dataType" | ||
| 97 | + :mode="mode" | ||
| 98 | + /> | ||
| 99 | + </section> | ||
| 100 | +</template> | ||
| 101 | +./index.config |
| 1 | +import { unref } from 'vue'; | ||
| 2 | +import { createHexCommandRuleValidator } from '.'; | ||
| 3 | +import { createFunctionNameFormItem, createIdentifierFormItem } from './DataTypeForm/config'; | ||
| 4 | +import { useObjectModelFormContext } from './useObjectModelFormContext'; | ||
| 5 | +import { findDictItemByCode } from '/@/api/system/dict'; | ||
| 6 | +import { FormSchema } from '/@/components/Form'; | ||
| 7 | +import { | ||
| 8 | + ServiceCallTypeEnum, | ||
| 9 | + ServiceCallTypeNameEnum, | ||
| 10 | + TransportTypeEnum, | ||
| 11 | +} from '/@/enums/deviceEnum'; | ||
| 12 | +import { DictEnum } from '/@/enums/dictEnum'; | ||
| 13 | +import { | ||
| 14 | + ObjectModelAccessModeEnum, | ||
| 15 | + FunctionTypeEnum, | ||
| 16 | + FunctionTypeNameEnum, | ||
| 17 | + ObjectEventTypeEnum, | ||
| 18 | +} from '/@/enums/objectModelEnum'; | ||
| 19 | +import { DataActionModeEnum } from '/@/enums/toolEnum'; | ||
| 20 | + | ||
| 21 | +export enum FormFieldsEnum { | ||
| 22 | + FUNCTION_TYPE = 'functionType', | ||
| 23 | + FUNCTION_NAME = 'functionName', | ||
| 24 | + IDENTIFIER = 'identifier', | ||
| 25 | + DATA_TYPE = 'dataType', | ||
| 26 | + ACCESS_MODE = 'accessMode', | ||
| 27 | + VALUE_RANGE = 'valueRange', | ||
| 28 | + STEP = 'step', | ||
| 29 | + UNIT = 'unit', | ||
| 30 | + UNIT_NAME = 'unitName', | ||
| 31 | + REMARK = 'remark', | ||
| 32 | + BOOL_CLOSE = 'boolClose', | ||
| 33 | + BOOL_OPEN = 'boolOpen', | ||
| 34 | + LENGTH = 'length', | ||
| 35 | + EXTENSION_DESC = 'extensionDesc', | ||
| 36 | + ENUMS_DATA = 'enumsData', | ||
| 37 | + STRUCT_DATA = 'structData', | ||
| 38 | + | ||
| 39 | + CALL_TYPE = 'callType', | ||
| 40 | + SERVICE_COMMAND = 'serviceCommand', | ||
| 41 | + INPUT_DATA = 'inputData', | ||
| 42 | + OUTPUT_DATA = 'outputData', | ||
| 43 | + | ||
| 44 | + EVENT_TYPE = 'eventType', | ||
| 45 | + | ||
| 46 | + DATA_TYPE_FORM = 'dataTypeForm', | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +export enum FormFieldsNameEnum { | ||
| 50 | + FUNCTION_TYPE = '功能类型', | ||
| 51 | + FUNCTION_NAME = '功能名称', | ||
| 52 | + IDENTIFIER = '功能标识符', | ||
| 53 | + DATA_TYPE = '数据类型', | ||
| 54 | + ACCESS_MODE = '读写类型', | ||
| 55 | + VALUE_RANGE = '取值范围', | ||
| 56 | + STEP = '步长', | ||
| 57 | + UNIT = '单位', | ||
| 58 | + UNIT_NAME = '单位名称', | ||
| 59 | + REMARK = '备注', | ||
| 60 | + BOOL_CLOSE = '0 - 关', | ||
| 61 | + BOOL_OPEN = '1 - 开', | ||
| 62 | + LENGTH = '数据长度(字节)', | ||
| 63 | + EXTENSION_DESC = '拓展描述符', | ||
| 64 | + ENUMS_DATA = '枚举项', | ||
| 65 | + STRUCT_DATA = 'JSON对象', | ||
| 66 | + | ||
| 67 | + CALL_TYPE = '调用方式', | ||
| 68 | + SERVICE_COMMAND = '输入参数', | ||
| 69 | + INPUT_DATA = '输入参数', | ||
| 70 | + OUTPUT_DATA = '输出参数', | ||
| 71 | + | ||
| 72 | + EVENT_TYPE = '事件类型', | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +export const getFormSchemas = ({ | ||
| 76 | + transportType, | ||
| 77 | +}: { | ||
| 78 | + transportType?: string; | ||
| 79 | + mode?: DataActionModeEnum; | ||
| 80 | +}): FormSchema[] => { | ||
| 81 | + return [ | ||
| 82 | + { | ||
| 83 | + field: FormFieldsEnum.FUNCTION_TYPE, | ||
| 84 | + label: FormFieldsNameEnum.FUNCTION_TYPE, | ||
| 85 | + component: 'Segmented', | ||
| 86 | + defaultValue: FunctionTypeEnum.PROPERTIES, | ||
| 87 | + required: true, | ||
| 88 | + dynamicDisabled: () => { | ||
| 89 | + const { getModalMode } = useObjectModelFormContext(); | ||
| 90 | + return unref(getModalMode) !== DataActionModeEnum.CREATE; | ||
| 91 | + }, | ||
| 92 | + componentProps: ({ formActionType }) => { | ||
| 93 | + return { | ||
| 94 | + options: Object.keys(FunctionTypeEnum).map((key) => ({ | ||
| 95 | + title: FunctionTypeNameEnum[key], | ||
| 96 | + value: FunctionTypeEnum[key], | ||
| 97 | + })), | ||
| 98 | + onChange() { | ||
| 99 | + const { setFieldsValue, clearValidate } = formActionType; | ||
| 100 | + setFieldsValue({ | ||
| 101 | + [FormFieldsEnum.INPUT_DATA]: [], | ||
| 102 | + [FormFieldsEnum.OUTPUT_DATA]: [], | ||
| 103 | + [FormFieldsEnum.STRUCT_DATA]: [], | ||
| 104 | + [FormFieldsEnum.SERVICE_COMMAND]: null, | ||
| 105 | + }); | ||
| 106 | + clearValidate(); | ||
| 107 | + }, | ||
| 108 | + }; | ||
| 109 | + }, | ||
| 110 | + }, | ||
| 111 | + { | ||
| 112 | + field: FormFieldsEnum.DATA_TYPE_FORM, | ||
| 113 | + component: 'Input', | ||
| 114 | + label: '', | ||
| 115 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.PROPERTIES, | ||
| 116 | + colSlot: FormFieldsEnum.DATA_TYPE_FORM, | ||
| 117 | + }, | ||
| 118 | + createFunctionNameFormItem({ | ||
| 119 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | ||
| 120 | + }), | ||
| 121 | + createIdentifierFormItem({ | ||
| 122 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] !== FunctionTypeEnum.PROPERTIES, | ||
| 123 | + }), | ||
| 124 | + { | ||
| 125 | + field: FormFieldsEnum.EXTENSION_DESC, | ||
| 126 | + component: 'Input', | ||
| 127 | + label: FormFieldsNameEnum.EXTENSION_DESC, | ||
| 128 | + slot: FormFieldsEnum.EXTENSION_DESC, | ||
| 129 | + ifShow: ({ model }) => | ||
| 130 | + transportType === TransportTypeEnum.TCP && | ||
| 131 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 132 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 133 | + ), | ||
| 134 | + }, | ||
| 135 | + { | ||
| 136 | + field: FormFieldsEnum.ACCESS_MODE, | ||
| 137 | + component: 'ApiRadioGroup', | ||
| 138 | + label: FormFieldsNameEnum.ACCESS_MODE, | ||
| 139 | + required: true, | ||
| 140 | + ifShow: ({ model }) => | ||
| 141 | + ![FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 142 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 143 | + ), | ||
| 144 | + defaultValue: ObjectModelAccessModeEnum.READ_AND_WRITE, | ||
| 145 | + componentProps: { | ||
| 146 | + placeholder: '请选择读写类型', | ||
| 147 | + api: findDictItemByCode, | ||
| 148 | + params: { | ||
| 149 | + dictCode: DictEnum.READ_WRITE_TYP, | ||
| 150 | + }, | ||
| 151 | + labelField: 'itemText', | ||
| 152 | + valueField: 'itemValue', | ||
| 153 | + }, | ||
| 154 | + }, | ||
| 155 | + | ||
| 156 | + // 服务 | ||
| 157 | + { | ||
| 158 | + field: FormFieldsEnum.CALL_TYPE, | ||
| 159 | + label: FormFieldsNameEnum.CALL_TYPE, | ||
| 160 | + component: 'RadioGroup', | ||
| 161 | + defaultValue: ServiceCallTypeEnum.ASYNC, | ||
| 162 | + required: true, | ||
| 163 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE, | ||
| 164 | + componentProps: () => { | ||
| 165 | + return { | ||
| 166 | + options: Object.keys(ServiceCallTypeEnum).map((value) => ({ | ||
| 167 | + label: ServiceCallTypeNameEnum[value], | ||
| 168 | + value, | ||
| 169 | + })), | ||
| 170 | + }; | ||
| 171 | + }, | ||
| 172 | + }, | ||
| 173 | + { | ||
| 174 | + field: FormFieldsEnum.SERVICE_COMMAND, | ||
| 175 | + label: FormFieldsNameEnum.SERVICE_COMMAND, | ||
| 176 | + component: 'Input', | ||
| 177 | + required: true, | ||
| 178 | + rules: [{ message: '输入参数为必填项', required: true }, ...createHexCommandRuleValidator()], | ||
| 179 | + ifShow: ({ model }) => | ||
| 180 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | ||
| 181 | + transportType === TransportTypeEnum.TCP, | ||
| 182 | + componentProps: () => { | ||
| 183 | + return { | ||
| 184 | + placeholder: '请输入ASCII或HEX服务命令', | ||
| 185 | + }; | ||
| 186 | + }, | ||
| 187 | + }, | ||
| 188 | + { | ||
| 189 | + field: FormFieldsEnum.INPUT_DATA, | ||
| 190 | + label: FormFieldsNameEnum.INPUT_DATA, | ||
| 191 | + component: 'Input', | ||
| 192 | + slot: FormFieldsEnum.INPUT_DATA, | ||
| 193 | + changeEvent: 'update:value', | ||
| 194 | + valueField: 'value', | ||
| 195 | + required: true, | ||
| 196 | + ifShow: ({ model }) => | ||
| 197 | + model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.SERVICE && | ||
| 198 | + transportType !== TransportTypeEnum.TCP, | ||
| 199 | + }, | ||
| 200 | + | ||
| 201 | + // 事件 | ||
| 202 | + { | ||
| 203 | + field: FormFieldsEnum.EVENT_TYPE, | ||
| 204 | + label: FormFieldsNameEnum.EVENT_TYPE, | ||
| 205 | + required: true, | ||
| 206 | + component: 'ApiRadioGroup', | ||
| 207 | + defaultValue: ObjectEventTypeEnum.INFO, | ||
| 208 | + ifShow: ({ model }) => model[FormFieldsEnum.FUNCTION_TYPE] === FunctionTypeEnum.EVENTS, | ||
| 209 | + componentProps: () => { | ||
| 210 | + return { | ||
| 211 | + placeholder: '请选择事件类型', | ||
| 212 | + api: findDictItemByCode, | ||
| 213 | + params: { | ||
| 214 | + dictCode: DictEnum.EVENT_TYPE, | ||
| 215 | + }, | ||
| 216 | + labelField: 'itemText', | ||
| 217 | + valueField: 'itemValue', | ||
| 218 | + getPopupContainer: () => document.body, | ||
| 219 | + }; | ||
| 220 | + }, | ||
| 221 | + }, | ||
| 222 | + { | ||
| 223 | + field: FormFieldsEnum.OUTPUT_DATA, | ||
| 224 | + label: FormFieldsNameEnum.OUTPUT_DATA, | ||
| 225 | + component: 'Input', | ||
| 226 | + slot: FormFieldsEnum.OUTPUT_DATA, | ||
| 227 | + changeEvent: 'update:value', | ||
| 228 | + valueField: 'value', | ||
| 229 | + ifShow: ({ model }) => | ||
| 230 | + [FunctionTypeEnum.SERVICE, FunctionTypeEnum.EVENTS].includes( | ||
| 231 | + model[FormFieldsEnum.FUNCTION_TYPE] | ||
| 232 | + ), | ||
| 233 | + }, | ||
| 234 | + | ||
| 235 | + { | ||
| 236 | + field: FormFieldsEnum.REMARK, | ||
| 237 | + label: FormFieldsNameEnum.REMARK, | ||
| 238 | + component: 'InputTextArea', | ||
| 239 | + componentProps: { | ||
| 240 | + rows: 4, | ||
| 241 | + maxLength: 100, | ||
| 242 | + placeholder: '请输入描述', | ||
| 243 | + }, | ||
| 244 | + }, | ||
| 245 | + ]; | ||
| 246 | +}; |
| 1 | +import { Rule } from '/@/components/Form'; | ||
| 2 | + | ||
| 3 | +export { default as ObjectModelForm } from './index.vue'; | ||
| 4 | + | ||
| 5 | +export function createHexCommandRuleValidator(): Rule[] { | ||
| 6 | + return [ | ||
| 7 | + { | ||
| 8 | + message: '请输入ASCII或HEX服务命令(0~9/A~F)', | ||
| 9 | + validator(_rule, value, _callback) { | ||
| 10 | + const reg = /^[\s0-9a-fA-F]+$/; | ||
| 11 | + | ||
| 12 | + if (reg.test(value)) return Promise.resolve(); | ||
| 13 | + return Promise.reject('请输入ASCII或HEX服务命令(0~9/A~F)'); | ||
| 14 | + }, | ||
| 15 | + }, | ||
| 16 | + ]; | ||
| 17 | +} |