Commit 416f565c23311ac6284d359be5e41a8a02fe2b14
Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into f…
…eat/device-new-protocol
Showing
27 changed files
with
322 additions
and
28 deletions
| ... | ... | @@ -67,7 +67,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { |
| 67 | 67 | |
| 68 | 68 | if (items.length) { |
| 69 | 69 | const first = items.at(0)!; |
| 70 | - const { deviceName, id, severity, type } = first; | |
| 70 | + const { deviceAlias, deviceName, id, severity, type } = first; | |
| 71 | 71 | |
| 72 | 72 | let key: Nullable<string> = `open-notify-${id}`; |
| 73 | 73 | |
| ... | ... | @@ -80,7 +80,7 @@ export function useAlarmNotify(params: UseAlarmNotifyParams = {}) { |
| 80 | 80 | description: h('div', {}, [ |
| 81 | 81 | h('div', { style: { marginRight: '5px' } }, [ |
| 82 | 82 | h('span', { style: { marginRight: '5px' } }, '设备:'), |
| 83 | - h('span', {}, `[${deviceName}]`), | |
| 83 | + h('span', {}, `[${deviceAlias ?? deviceName}]`), | |
| 84 | 84 | ]), |
| 85 | 85 | h('div', { style: { marginRight: '5px' } }, [ |
| 86 | 86 | h('span', { style: { marginRight: '5px' } }, '告警场景:'), | ... | ... |
| ... | ... | @@ -41,6 +41,7 @@ |
| 41 | 41 | " |
| 42 | 42 | v-else-if="item.key === TableDefaultTypeEnum.ENTITYID" |
| 43 | 43 | v-model:value="item.value" |
| 44 | + v-bind="createPickerSearch()" | |
| 44 | 45 | placeholder="请选择" |
| 45 | 46 | :options="entityOptions" |
| 46 | 47 | allowClear |
| ... | ... | @@ -53,6 +54,7 @@ |
| 53 | 54 | " |
| 54 | 55 | v-else-if="item.key === TableDefaultTypeEnum.KEYS" |
| 55 | 56 | v-model:value="item.value" |
| 57 | + v-bind="createPickerSearch()" | |
| 56 | 58 | placeholder="请选择" |
| 57 | 59 | :options="attributeOptions" |
| 58 | 60 | mode="multiple" |
| ... | ... | @@ -102,6 +104,7 @@ |
| 102 | 104 | " |
| 103 | 105 | v-else |
| 104 | 106 | v-model:value="item.value" |
| 107 | + v-bind="createPickerSearch()" | |
| 105 | 108 | placeholder="请选择" |
| 106 | 109 | :options="valueOptions" |
| 107 | 110 | allowClear |
| ... | ... | @@ -157,6 +160,7 @@ |
| 157 | 160 | import { getPacketIntervalByRange } from '/@/views/device/localtion/cpns/TimePeriodForm/helper'; |
| 158 | 161 | import { uniqBy } from 'lodash'; |
| 159 | 162 | import { TableDefaultTypeEnum } from '../../../config/enum'; |
| 163 | + import { createPickerSearch } from '/@/utils/pickerSearch'; | |
| 160 | 164 | |
| 161 | 165 | const props = defineProps({ |
| 162 | 166 | method: { | ... | ... |
| ... | ... | @@ -212,6 +212,10 @@ export const basicInfoForm: FormSchema[] = [ |
| 212 | 212 | labelField: 'name', |
| 213 | 213 | valueField: 'tbDeviceId', |
| 214 | 214 | getPopupContainer: () => document.body, |
| 215 | + showSearch: true, | |
| 216 | + filterOption: (inputValue: string, options: Record<'label', string>) => { | |
| 217 | + return options.label.includes(inputValue); | |
| 218 | + }, | |
| 215 | 219 | }; |
| 216 | 220 | }, |
| 217 | 221 | }, | ... | ... |
| ... | ... | @@ -2,7 +2,8 @@ import type { BasicColumn } from '/@/components/Table'; |
| 2 | 2 | import type { FormSchema } from '/@/components/Table'; |
| 3 | 3 | import { getOrganizationList } from '/@/api/system/system'; |
| 4 | 4 | import { copyTransFun } from '/@/utils/fnUtils'; |
| 5 | -import { getDeviceDataKeys, getDeviceProfile } from '/@/api/alarm/position'; | |
| 5 | +import { getDeviceDataKeys } from '/@/api/alarm/position'; | |
| 6 | +import { deviceProfile } from '/@/api/device/deviceManager'; | |
| 6 | 7 | import { EChartsOption } from 'echarts'; |
| 7 | 8 | |
| 8 | 9 | export enum AggregateDataEnum { |
| ... | ... | @@ -31,11 +32,17 @@ export const formSchema: FormSchema[] = [ |
| 31 | 32 | field: 'deviceProfileId', |
| 32 | 33 | label: '', |
| 33 | 34 | component: 'ApiSelect', |
| 34 | - componentProps: { | |
| 35 | - api: getDeviceProfile, | |
| 36 | - placeholder: '请选择产品', | |
| 37 | - labelField: 'name', | |
| 38 | - valueField: 'tbProfileId', | |
| 35 | + componentProps: () => { | |
| 36 | + return { | |
| 37 | + showSearch: true, | |
| 38 | + labelField: 'name', | |
| 39 | + valueField: 'tbProfileId', | |
| 40 | + resultField: 'data', | |
| 41 | + placeholder: '请选择产品', | |
| 42 | + api: deviceProfile, | |
| 43 | + filterOption: (inputValue: string, option: Record<'label' | 'value', string>) => | |
| 44 | + option.label.includes(inputValue), | |
| 45 | + }; | |
| 39 | 46 | }, |
| 40 | 47 | }, |
| 41 | 48 | { | ... | ... |
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | v-model:value="param.attributes" |
| 7 | 7 | class="!w-1/2" |
| 8 | 8 | :options="selectOptions" |
| 9 | + v-bind="createPickerSearch()" | |
| 9 | 10 | :disabled="disabled" |
| 10 | 11 | @change="emitChange" |
| 11 | 12 | mode="multiple" |
| ... | ... | @@ -24,6 +25,7 @@ |
| 24 | 25 | import { Select } from 'ant-design-vue'; |
| 25 | 26 | import { Params } from '../type/'; |
| 26 | 27 | import { useHooks } from '../hooks/index.hooks'; |
| 28 | + import { createPickerSearch } from '/@/utils/pickerSearch'; | |
| 27 | 29 | |
| 28 | 30 | const props = defineProps({ |
| 29 | 31 | value: propTypes.object.def({}), | ... | ... |
| ... | ... | @@ -32,12 +32,10 @@ |
| 32 | 32 | class="min-w-25" |
| 33 | 33 | v-model:value="item.active" |
| 34 | 34 | @change="(value) => handleChangeChars(value, item.device, item)" |
| 35 | + v-bind="createPickerSearch()" | |
| 35 | 36 | placeholder="请选择设备属性" |
| 36 | - > | |
| 37 | - <Select.Option v-for="attr in item.attributes" :key="attr" :value="attr"> | |
| 38 | - {{ attr }} | |
| 39 | - </Select.Option> | |
| 40 | - </Select> | |
| 37 | + :options="item?.attributes?.map((item1) => ({ label: item1, value: item1 }))" | |
| 38 | + /> | |
| 41 | 39 | </div> |
| 42 | 40 | <div class="w-full h-full flex justify-center items-center"> |
| 43 | 41 | <Empty v-show="item.notFoundData" description="暂无数据" class="text-dark-50" /> |
| ... | ... | @@ -65,6 +63,7 @@ |
| 65 | 63 | import moment from 'moment'; |
| 66 | 64 | import { ExecuteReportRecord } from '/@/api/export/model/exportModel'; |
| 67 | 65 | import { Select, Spin, Empty } from 'ant-design-vue'; |
| 66 | + import { createPickerSearch } from '/@/utils/pickerSearch'; | |
| 68 | 67 | |
| 69 | 68 | interface ResponsData { |
| 70 | 69 | attr: string; | ... | ... |
| ... | ... | @@ -8,6 +8,7 @@ import { findDictItemByCode } from '/@/api/system/dict'; |
| 8 | 8 | // import { unref } from 'vue'; |
| 9 | 9 | import { getMeetTheConditionsDevice } from '/@/api/dataBoard'; |
| 10 | 10 | import { MasterDeviceList } from '/@/api/dataBoard/model'; |
| 11 | +import { createPickerSearch } from '/@/utils/pickerSearch'; | |
| 11 | 12 | |
| 12 | 13 | export const formSchemas = (): FormSchema[] => { |
| 13 | 14 | // const mode = useSelectWidgetMode(); |
| ... | ... | @@ -150,7 +151,6 @@ export const formSchemas = (): FormSchema[] => { |
| 150 | 151 | return []; |
| 151 | 152 | }, |
| 152 | 153 | onChange(_value, record: MasterDeviceList) { |
| 153 | - console.log(record, 'record'); | |
| 154 | 154 | setFieldsValue({ |
| 155 | 155 | [DataSourceField.DEVICE_NAME]: record?.label, |
| 156 | 156 | deviceIds: record?.id, |
| ... | ... | @@ -158,6 +158,7 @@ export const formSchemas = (): FormSchema[] => { |
| 158 | 158 | }, |
| 159 | 159 | placeholder: '请选择设备', |
| 160 | 160 | getPopupContainer: () => document.body, |
| 161 | + ...createPickerSearch(), | |
| 161 | 162 | }; |
| 162 | 163 | }, |
| 163 | 164 | }, | ... | ... |
| ... | ... | @@ -12,6 +12,7 @@ import { ComponentConfigFieldEnum } from '../../../enum'; |
| 12 | 12 | export const option: PublicPresetOptions = { |
| 13 | 13 | [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, |
| 14 | 14 | [ComponentConfigFieldEnum.FONT_SIZE]: 14, |
| 15 | + [ComponentConfigFieldEnum.PASS_WORD]: '', | |
| 15 | 16 | // [ComponentConfigFieldEnum.FONT_COLOR]: '#000', |
| 16 | 17 | }; |
| 17 | 18 | ... | ... |
| ... | ... | @@ -14,10 +14,10 @@ |
| 14 | 14 | // defaultValue: option.fontColor, |
| 15 | 15 | // }, |
| 16 | 16 | { |
| 17 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | |
| 18 | - label: '显示设备名称', | |
| 19 | - component: 'Checkbox', | |
| 20 | - defaultValue: option.showDeviceName, | |
| 17 | + field: ComponentConfigFieldEnum.PASS_WORD, | |
| 18 | + label: '操作密码', | |
| 19 | + component: 'InputPassword', | |
| 20 | + defaultValue: '', | |
| 21 | 21 | }, |
| 22 | 22 | { |
| 23 | 23 | field: ComponentConfigFieldEnum.FONT_SIZE, |
| ... | ... | @@ -25,6 +25,12 @@ |
| 25 | 25 | component: 'InputNumber', |
| 26 | 26 | defaultValue: 14, |
| 27 | 27 | }, |
| 28 | + { | |
| 29 | + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | |
| 30 | + label: '显示设备名称', | |
| 31 | + component: 'Checkbox', | |
| 32 | + defaultValue: option.showDeviceName, | |
| 33 | + }, | |
| 28 | 34 | ], |
| 29 | 35 | showActionButtonGroup: false, |
| 30 | 36 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -9,6 +9,8 @@ |
| 9 | 9 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 10 | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { getSendValues } from '../config'; |
| 12 | + import { useModal } from '/@/components/Modal'; | |
| 13 | + import PasswordModal from '../component/PasswordModal.vue'; | |
| 12 | 14 | |
| 13 | 15 | const props = defineProps<{ |
| 14 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -31,11 +33,13 @@ |
| 31 | 33 | deviceCode, |
| 32 | 34 | customCommand, |
| 33 | 35 | } = option; |
| 34 | - const { fontSize: persetFontSize } = persetOption || {}; | |
| 35 | - const { fontSize } = componentInfo || {}; | |
| 36 | + | |
| 37 | + const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; | |
| 38 | + const { fontSize, password } = componentInfo || {}; | |
| 36 | 39 | return { |
| 37 | 40 | attribute: attributeRename || attributeName || attribute, |
| 38 | 41 | fontSize: fontSize || persetFontSize || 14, |
| 42 | + password: password || persetPassword, | |
| 39 | 43 | extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, |
| 40 | 44 | commandType, |
| 41 | 45 | codeType, |
| ... | ... | @@ -45,7 +49,31 @@ |
| 45 | 49 | }); |
| 46 | 50 | |
| 47 | 51 | const { sendCommand, loading } = useSendCommand(); |
| 52 | + | |
| 53 | + const handleSendCommand = async (data) => { | |
| 54 | + const { control: event } = data || {}; | |
| 55 | + const target = event.target as HTMLInputElement; | |
| 56 | + const value = !target.checked; | |
| 57 | + const { option } = props.config || {}; | |
| 58 | + | |
| 59 | + const { values, isModbusCommand, sendValue } = | |
| 60 | + (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 61 | + | |
| 62 | + const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 63 | + if (flag) currentValue.value = value; | |
| 64 | + flag ? (currentValue.value = value) : (target.checked = !value); | |
| 65 | + }; | |
| 66 | + | |
| 48 | 67 | const handleChange = async (event: Event) => { |
| 68 | + if (unref(getDesign).password) { | |
| 69 | + const target = event.target as HTMLInputElement; | |
| 70 | + const value = target.checked; | |
| 71 | + target.checked = !value; | |
| 72 | + currentValue.value = !value; | |
| 73 | + openModal(true, { password: unref(getDesign).password, control: event }); | |
| 74 | + return; | |
| 75 | + } | |
| 76 | + | |
| 49 | 77 | const target = event.target as HTMLInputElement; |
| 50 | 78 | const value = target.checked; |
| 51 | 79 | const { option } = props.config || {}; |
| ... | ... | @@ -66,6 +94,8 @@ |
| 66 | 94 | }; |
| 67 | 95 | |
| 68 | 96 | useDataFetch(props, updateFn); |
| 97 | + | |
| 98 | + const [registerModal, { openModal }] = useModal(); | |
| 69 | 99 | </script> |
| 70 | 100 | |
| 71 | 101 | <template> |
| ... | ... | @@ -95,6 +125,8 @@ |
| 95 | 125 | </div> |
| 96 | 126 | </Spin> |
| 97 | 127 | </main> |
| 128 | + | |
| 129 | + <PasswordModal @register="registerModal" @success="handleSendCommand" /> | |
| 98 | 130 | </main> |
| 99 | 131 | </template> |
| 100 | 132 | ... | ... |
| ... | ... | @@ -14,6 +14,7 @@ export const option: PublicPresetOptions = { |
| 14 | 14 | [ComponentConfigFieldEnum.ICON_COLOR]: '#377DFF', |
| 15 | 15 | [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, |
| 16 | 16 | [ComponentConfigFieldEnum.FONT_SIZE]: 14, |
| 17 | + [ComponentConfigFieldEnum.PASS_WORD]: '', | |
| 17 | 18 | }; |
| 18 | 19 | |
| 19 | 20 | export default class Config extends PublicConfigClass implements CreateComponentType { | ... | ... |
| ... | ... | @@ -38,6 +38,12 @@ |
| 38 | 38 | component: 'InputNumber', |
| 39 | 39 | defaultValue: option.fontSize, |
| 40 | 40 | }, |
| 41 | + { | |
| 42 | + field: ComponentConfigFieldEnum.PASS_WORD, | |
| 43 | + label: '操作密码', | |
| 44 | + component: 'InputPassword', | |
| 45 | + defaultValue: '', | |
| 46 | + }, | |
| 41 | 47 | ], |
| 42 | 48 | showActionButtonGroup: false, |
| 43 | 49 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -11,6 +11,8 @@ |
| 11 | 11 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 12 | 12 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 13 | 13 | import { getSendValues } from '../config'; |
| 14 | + import { useModal } from '/@/components/Modal'; | |
| 15 | + import PasswordModal from '../component/PasswordModal.vue'; | |
| 14 | 16 | |
| 15 | 17 | const props = defineProps<{ |
| 16 | 18 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -35,8 +37,9 @@ |
| 35 | 37 | icon: presetIcon, |
| 36 | 38 | iconColor: presetIconColor, |
| 37 | 39 | fontSize: persetFontSize, |
| 40 | + password: persetPassword, | |
| 38 | 41 | } = persetOption || {}; |
| 39 | - const { icon, iconColor, fontSize } = componentInfo || {}; | |
| 42 | + const { icon, iconColor, fontSize, password } = componentInfo || {}; | |
| 40 | 43 | |
| 41 | 44 | return { |
| 42 | 45 | icon: icon ?? presetIcon, |
| ... | ... | @@ -44,6 +47,7 @@ |
| 44 | 47 | attribute: attributeRename || attributeName || attribute, |
| 45 | 48 | extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, |
| 46 | 49 | fontSize: fontSize || persetFontSize || 14, |
| 50 | + password: password || persetPassword, | |
| 47 | 51 | commandType, |
| 48 | 52 | codeType, |
| 49 | 53 | deviceCode, |
| ... | ... | @@ -54,6 +58,11 @@ |
| 54 | 58 | const { sendCommand, loading } = useSendCommand(); |
| 55 | 59 | |
| 56 | 60 | const handleChange = async () => { |
| 61 | + if (unref(getDesign).password) { | |
| 62 | + openModal(true, { password: unref(getDesign).password }); | |
| 63 | + checked.value = !unref(checked); | |
| 64 | + return; | |
| 65 | + } | |
| 57 | 66 | const { option } = props.config || {}; |
| 58 | 67 | |
| 59 | 68 | const { values, isModbusCommand, sendValue } = |
| ... | ... | @@ -67,6 +76,21 @@ |
| 67 | 76 | if (!flag) checked.value = !unref(checked); |
| 68 | 77 | }; |
| 69 | 78 | |
| 79 | + const handleSendCommand = async () => { | |
| 80 | + const { option } = props.config || {}; | |
| 81 | + checked.value = !unref(checked); | |
| 82 | + | |
| 83 | + const { values, isModbusCommand, sendValue } = | |
| 84 | + (await getSendValues(option, unref(getDesign), unref(checked))) || {}; | |
| 85 | + | |
| 86 | + const flag = await sendCommand( | |
| 87 | + values, | |
| 88 | + isModbusCommand ? sendValue : unref(checked), | |
| 89 | + isModbusCommand | |
| 90 | + ); | |
| 91 | + if (!flag) checked.value = !unref(checked); | |
| 92 | + }; | |
| 93 | + | |
| 70 | 94 | const updateFn: DataFetchUpdateFn = (message, attribute) => { |
| 71 | 95 | const { data = {} } = message; |
| 72 | 96 | const [latest] = data[attribute] || []; |
| ... | ... | @@ -76,6 +100,8 @@ |
| 76 | 100 | |
| 77 | 101 | useDataFetch(props, updateFn); |
| 78 | 102 | const { getScale, getRatio } = useComponentScale(props); |
| 103 | + | |
| 104 | + const [registerModal, { openModal }] = useModal(); | |
| 79 | 105 | </script> |
| 80 | 106 | |
| 81 | 107 | <template> |
| ... | ... | @@ -105,5 +131,6 @@ |
| 105 | 131 | @change="handleChange" |
| 106 | 132 | /> |
| 107 | 133 | </main> |
| 134 | + <PasswordModal @register="registerModal" @success="handleSendCommand" /> | |
| 108 | 135 | </main> |
| 109 | 136 | </template> | ... | ... |
| ... | ... | @@ -12,6 +12,7 @@ import { ComponentConfigFieldEnum } from '../../../enum'; |
| 12 | 12 | export const option: PublicPresetOptions = { |
| 13 | 13 | [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, |
| 14 | 14 | [ComponentConfigFieldEnum.FONT_SIZE]: 14, |
| 15 | + [ComponentConfigFieldEnum.PASS_WORD]: '', | |
| 15 | 16 | // [ComponentConfigFieldEnum.FONT_COLOR]: '#000', |
| 16 | 17 | }; |
| 17 | 18 | ... | ... |
| ... | ... | @@ -24,6 +24,12 @@ |
| 24 | 24 | component: 'InputNumber', |
| 25 | 25 | defaultValue: option.fontSize, |
| 26 | 26 | }, |
| 27 | + { | |
| 28 | + field: ComponentConfigFieldEnum.PASS_WORD, | |
| 29 | + label: '操作密码', | |
| 30 | + component: 'InputPassword', | |
| 31 | + defaultValue: '', | |
| 32 | + }, | |
| 27 | 33 | ], |
| 28 | 34 | showActionButtonGroup: false, |
| 29 | 35 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -9,6 +9,8 @@ |
| 9 | 9 | import { DataFetchUpdateFn } from '../../../hook/socket/useSocket.type'; |
| 10 | 10 | import { useDataFetch } from '../../../hook/socket/useSocket'; |
| 11 | 11 | import { getSendValues } from '../config'; |
| 12 | + import PasswordModal from '../component/PasswordModal.vue'; | |
| 13 | + import { useModal } from '/@/components/Modal'; | |
| 12 | 14 | |
| 13 | 15 | const props = defineProps<{ |
| 14 | 16 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -31,11 +33,12 @@ |
| 31 | 33 | customCommand, |
| 32 | 34 | componentInfo, |
| 33 | 35 | } = option; |
| 34 | - const { fontSize: persetFontSize } = persetOption || {}; | |
| 35 | - const { fontSize } = componentInfo || {}; | |
| 36 | + const { fontSize: persetFontSize, password: persetPassword } = persetOption || {}; | |
| 37 | + const { fontSize, password } = componentInfo || {}; | |
| 36 | 38 | return { |
| 37 | 39 | attribute: attributeRename || attributeName || attribute, |
| 38 | 40 | extensionDesc: extensionDesc ? JSON.parse(extensionDesc) : {}, |
| 41 | + password: password || persetPassword, | |
| 39 | 42 | commandType, |
| 40 | 43 | codeType, |
| 41 | 44 | deviceCode, |
| ... | ... | @@ -46,6 +49,15 @@ |
| 46 | 49 | |
| 47 | 50 | const { loading, sendCommand } = useSendCommand(); |
| 48 | 51 | const handleChange = async (event: Event) => { |
| 52 | + if (unref(getDesign).password) { | |
| 53 | + const target = event.target as HTMLInputElement; | |
| 54 | + const value = target.checked; | |
| 55 | + target.checked = !value; | |
| 56 | + currentValue.value = !value; | |
| 57 | + openModal(true, { password: unref(getDesign).password, control: event }); | |
| 58 | + return; | |
| 59 | + } | |
| 60 | + | |
| 49 | 61 | const target = event.target as HTMLInputElement; |
| 50 | 62 | const value = target.checked; |
| 51 | 63 | const { option } = props.config || {}; |
| ... | ... | @@ -57,6 +69,19 @@ |
| 57 | 69 | flag ? (currentValue.value = value) : (target.checked = !value); |
| 58 | 70 | }; |
| 59 | 71 | |
| 72 | + const handleSendCommand = async (data) => { | |
| 73 | + const { control: event } = data || {}; | |
| 74 | + const target = event.target as HTMLInputElement; | |
| 75 | + const value = !target.checked; | |
| 76 | + const { option } = props.config || {}; | |
| 77 | + | |
| 78 | + const { values, isModbusCommand, sendValue } = | |
| 79 | + (await getSendValues(option, unref(getDesign), value)) || {}; | |
| 80 | + | |
| 81 | + const flag = await sendCommand(values, isModbusCommand ? sendValue : value, isModbusCommand); | |
| 82 | + flag ? (currentValue.value = value) : (target.checked = !value); | |
| 83 | + }; | |
| 84 | + | |
| 60 | 85 | const updateFn: DataFetchUpdateFn = (message, attribute) => { |
| 61 | 86 | const { data = {} } = message; |
| 62 | 87 | const [latest] = data[attribute] || []; |
| ... | ... | @@ -65,6 +90,7 @@ |
| 65 | 90 | }; |
| 66 | 91 | |
| 67 | 92 | useDataFetch(props, updateFn); |
| 93 | + const [registerModal, { openModal }] = useModal(); | |
| 68 | 94 | </script> |
| 69 | 95 | |
| 70 | 96 | <template> |
| ... | ... | @@ -94,6 +120,8 @@ |
| 94 | 120 | > |
| 95 | 121 | </Spin> |
| 96 | 122 | </main> |
| 123 | + | |
| 124 | + <PasswordModal @register="registerModal" @success="handleSendCommand" /> | |
| 97 | 125 | </main> |
| 98 | 126 | </template> |
| 99 | 127 | ... | ... |
| ... | ... | @@ -18,6 +18,7 @@ export const option: PublicPresetOptions = { |
| 18 | 18 | [ComponentConfigFieldEnum.MAX_NUMBER]: 100, |
| 19 | 19 | [ComponentConfigFieldEnum.FONT_SIZE]: 14, |
| 20 | 20 | [ComponentConfigFieldEnum.VALUE_SIZE]: 20, |
| 21 | + [ComponentConfigFieldEnum.PASS_WORD]: '', | |
| 21 | 22 | }; |
| 22 | 23 | |
| 23 | 24 | export default class Config extends PublicConfigClass implements CreateComponentType { | ... | ... |
| ... | ... | @@ -87,6 +87,13 @@ |
| 87 | 87 | component: 'Checkbox', |
| 88 | 88 | defaultValue: option.showDeviceName, |
| 89 | 89 | }, |
| 90 | + | |
| 91 | + { | |
| 92 | + field: ComponentConfigFieldEnum.PASS_WORD, | |
| 93 | + label: '操作密码', | |
| 94 | + component: 'InputPassword', | |
| 95 | + defaultValue: '', | |
| 96 | + }, | |
| 90 | 97 | ], |
| 91 | 98 | showActionButtonGroup: false, |
| 92 | 99 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -13,6 +13,8 @@ |
| 13 | 13 | import { useMessage } from '/@/hooks/web/useMessage'; |
| 14 | 14 | import { SingleToHex } from '/@/views/device/list/cpns/tabs/ObjectModelCommandDeliveryModal/config'; |
| 15 | 15 | import { genModbusCommand } from '/@/api/task'; |
| 16 | + import PasswordModal from '../component/PasswordModal.vue'; | |
| 17 | + import { useModal } from '/@/components/Modal'; | |
| 16 | 18 | |
| 17 | 19 | const props = defineProps<{ |
| 18 | 20 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -46,9 +48,18 @@ |
| 46 | 48 | textColor: persetTextColor, |
| 47 | 49 | valueSize: persetValueSize, |
| 48 | 50 | fontSize: persetFontSize, |
| 51 | + password: persetPassword, | |
| 49 | 52 | } = persetOption || {}; |
| 50 | - const { controlBarColor, fontColor, minNumber, maxNumber, textColor, valueSize, fontSize } = | |
| 51 | - componentInfo || {}; | |
| 53 | + const { | |
| 54 | + controlBarColor, | |
| 55 | + password, | |
| 56 | + fontColor, | |
| 57 | + minNumber, | |
| 58 | + maxNumber, | |
| 59 | + textColor, | |
| 60 | + valueSize, | |
| 61 | + fontSize, | |
| 62 | + } = componentInfo || {}; | |
| 52 | 63 | return { |
| 53 | 64 | attribute: attributeRename || attributeName || attribute, |
| 54 | 65 | controlBarColor: controlBarColor ?? persetControlBarColor, |
| ... | ... | @@ -63,6 +74,7 @@ |
| 63 | 74 | textColor: textColor || persetTextColor, |
| 64 | 75 | valueSize: valueSize || persetValueSize || 20, |
| 65 | 76 | fontSize: fontSize || persetFontSize || 14, |
| 77 | + password: password || persetPassword, | |
| 66 | 78 | }; |
| 67 | 79 | }); |
| 68 | 80 | |
| ... | ... | @@ -82,6 +94,7 @@ |
| 82 | 94 | const handleAfterChange = async () => { |
| 83 | 95 | unref(sliderEl)?.blur(); |
| 84 | 96 | if (unref(sliderValue) == unref(afterValue)) return; |
| 97 | + if (unref(getDesign).password) return; | |
| 85 | 98 | sliderValue.value = afterValue.value; //这一步是因为当我们是点击不是拖动时候,会让值更新不了,所以需要赋值一次 |
| 86 | 99 | }; |
| 87 | 100 | |
| ... | ... | @@ -169,6 +182,33 @@ |
| 169 | 182 | }; |
| 170 | 183 | |
| 171 | 184 | const handleBlur = async () => { |
| 185 | + if (unref(getDesign).password) { | |
| 186 | + sliderValue.value = oldSliderValue.value; | |
| 187 | + openModal(true, { password: unref(getDesign).password, value: afterValue.value }); | |
| 188 | + return; | |
| 189 | + } | |
| 190 | + | |
| 191 | + if (unref(oldSliderValue) !== unref(sliderValue)) { | |
| 192 | + const { codeType, customCommand } = unref(getDesign) || {}; | |
| 193 | + const { transportType } = customCommand || {}; | |
| 194 | + const sendValue = ref<any>(unref(sliderValue)); | |
| 195 | + const isModbusCommand = ref<boolean>(false); | |
| 196 | + if (transportType == 'TCP' && codeType == TaskTypeEnum.MODBUS_RTU) { | |
| 197 | + sendValue.value = await getSendValue(unref(sliderValue)); | |
| 198 | + isModbusCommand.value = true; | |
| 199 | + } | |
| 200 | + const flag = await sendCommand(props.config.option, unref(sendValue), unref(isModbusCommand)); | |
| 201 | + flag | |
| 202 | + ? ((sliderValue.value = unref(sliderValue)), | |
| 203 | + (oldSliderValue.value = sliderValue.value), | |
| 204 | + (index.value = 0)) | |
| 205 | + : (sliderValue.value = unref(oldSliderValue)); | |
| 206 | + } | |
| 207 | + }; | |
| 208 | + | |
| 209 | + const handleSendCommand = async (data) => { | |
| 210 | + const { value } = data || {}; | |
| 211 | + sliderValue.value = value; | |
| 172 | 212 | if (unref(oldSliderValue) !== unref(sliderValue)) { |
| 173 | 213 | const { codeType, customCommand } = unref(getDesign) || {}; |
| 174 | 214 | const { transportType } = customCommand || {}; |
| ... | ... | @@ -199,6 +239,7 @@ |
| 199 | 239 | |
| 200 | 240 | useDataFetch(props, updateFn); |
| 201 | 241 | const { getScale, getRatio } = useComponentScale(props); |
| 242 | + const [registerModal, { openModal }] = useModal(); | |
| 202 | 243 | </script> |
| 203 | 244 | |
| 204 | 245 | <template> |
| ... | ... | @@ -244,6 +285,7 @@ |
| 244 | 285 | </div> |
| 245 | 286 | </Spin> |
| 246 | 287 | </main> |
| 288 | + <PasswordModal @register="registerModal" @success="handleSendCommand" /> | |
| 247 | 289 | </main> |
| 248 | 290 | </template> |
| 249 | 291 | <style lang="less" scoped> | ... | ... |
| ... | ... | @@ -15,6 +15,7 @@ export const option: PublicPresetOptions = { |
| 15 | 15 | [ComponentConfigFieldEnum.ICON_COLOR]: '#377DFF', |
| 16 | 16 | [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false, |
| 17 | 17 | [ComponentConfigFieldEnum.FONT_SIZE]: 14, |
| 18 | + [ComponentConfigFieldEnum.PASS_WORD]: '', | |
| 18 | 19 | }; |
| 19 | 20 | |
| 20 | 21 | export default class Config extends PublicConfigClass implements CreateComponentType { | ... | ... |
| ... | ... | @@ -38,6 +38,12 @@ |
| 38 | 38 | component: 'Checkbox', |
| 39 | 39 | defaultValue: option.showDeviceName, |
| 40 | 40 | }, |
| 41 | + { | |
| 42 | + field: ComponentConfigFieldEnum.PASS_WORD, | |
| 43 | + label: '操作密码', | |
| 44 | + component: 'InputPassword', | |
| 45 | + defaultValue: '', | |
| 46 | + }, | |
| 41 | 47 | ], |
| 42 | 48 | showActionButtonGroup: false, |
| 43 | 49 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -14,6 +14,8 @@ |
| 14 | 14 | import { useReceiveValue } from '../../../hook/useReceiveValue'; |
| 15 | 15 | import { DataSource } from '/@/views/visual/palette/types'; |
| 16 | 16 | import { getSendValues, CommandTypeEnumLIst } from '../config'; |
| 17 | + import { useModal } from '/@/components/Modal'; | |
| 18 | + import PasswordModal from '../component/PasswordModal.vue'; | |
| 17 | 19 | |
| 18 | 20 | const props = defineProps<{ |
| 19 | 21 | config: ComponentPropsConfigType<typeof option>; |
| ... | ... | @@ -53,10 +55,12 @@ |
| 53 | 55 | icon: persetIcon, |
| 54 | 56 | iconColor: persetIconColor, |
| 55 | 57 | fontSize: persetFontSize, |
| 58 | + password: persetPassword, | |
| 56 | 59 | } = persetOption || {}; |
| 57 | 60 | return { |
| 58 | 61 | dataSource: dataSource.map((item) => { |
| 59 | - const { fontColor, icon, iconColor, unit, showDeviceName, fontSize } = item.componentInfo; | |
| 62 | + const { fontColor, icon, iconColor, unit, showDeviceName, password, fontSize } = | |
| 63 | + item.componentInfo; | |
| 60 | 64 | const { |
| 61 | 65 | attribute, |
| 62 | 66 | attributeRename, |
| ... | ... | @@ -86,13 +90,19 @@ |
| 86 | 90 | deviceCode, |
| 87 | 91 | customCommand, |
| 88 | 92 | fontSize: fontSize || persetFontSize || 14, |
| 93 | + password: password || persetPassword, | |
| 89 | 94 | }; |
| 90 | 95 | }), |
| 91 | 96 | }; |
| 92 | 97 | }); |
| 93 | 98 | |
| 94 | 99 | const { loading, sendCommand } = useSendCommand(); |
| 95 | - const handleChange = async (index: number, checked: Boolean) => { | |
| 100 | + const handleChange = async (index: number, checked: Boolean, item: any) => { | |
| 101 | + if (item.password) { | |
| 102 | + openModal(true, { password: item.password, index, checked }); | |
| 103 | + controlList.value[index].checked = !checked; | |
| 104 | + return; | |
| 105 | + } | |
| 96 | 106 | const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } = |
| 97 | 107 | props.config.option; |
| 98 | 108 | const data = { |
| ... | ... | @@ -120,6 +130,25 @@ |
| 120 | 130 | }) |
| 121 | 131 | : svgList |
| 122 | 132 | ); |
| 133 | + const handleSendCommand = async (modalData) => { | |
| 134 | + const { index, checked } = modalData || {}; | |
| 135 | + const { heightPx, itemHeightRatio, itemWidthRatio, mode, widthPx, dataSource } = | |
| 136 | + props.config.option; | |
| 137 | + const data = { | |
| 138 | + ...dataSource?.[index], | |
| 139 | + heightPx, | |
| 140 | + itemHeightRatio, | |
| 141 | + itemWidthRatio, | |
| 142 | + mode, | |
| 143 | + widthPx, | |
| 144 | + } as DataSource; | |
| 145 | + controlList.value[index].checked = checked; | |
| 146 | + const { values, isModbusCommand, sendValue } = | |
| 147 | + (await getSendValues(data, unref(getDesign).dataSource[index], checked)) || {}; | |
| 148 | + | |
| 149 | + const flag = await sendCommand(values, isModbusCommand ? sendValue : checked, isModbusCommand); | |
| 150 | + if (!flag) controlList.value[index].checked = !checked; | |
| 151 | + }; | |
| 123 | 152 | |
| 124 | 153 | const updateFn: MultipleDataFetchUpdateFn = async (message, deviceId, attribute) => { |
| 125 | 154 | forEachGroupMessage(message, deviceId, attribute, (attribute, value) => { |
| ... | ... | @@ -133,6 +162,7 @@ |
| 133 | 162 | |
| 134 | 163 | useMultipleDataFetch(props, updateFn); |
| 135 | 164 | const { getRatio } = useComponentScale(props); |
| 165 | + const [registerModal, { openModal }] = useModal(); | |
| 136 | 166 | </script> |
| 137 | 167 | |
| 138 | 168 | <template> |
| ... | ... | @@ -166,9 +196,10 @@ |
| 166 | 196 | <Switch |
| 167 | 197 | v-model:checked="item.checked" |
| 168 | 198 | :loading="loading" |
| 169 | - @change="handleChange(index, item.checked)" | |
| 199 | + @change="handleChange(index, item.checked, item)" | |
| 170 | 200 | :style="{ transform: `scale(${getRatio || 1})` }" |
| 171 | 201 | /> |
| 172 | 202 | </div> |
| 203 | + <PasswordModal @register="registerModal" @success="handleSendCommand" /> | |
| 173 | 204 | </main> |
| 174 | 205 | </template> | ... | ... |
| 1 | +<template> | |
| 2 | + <div> | |
| 3 | + <BasicModal | |
| 4 | + destroyOnClose | |
| 5 | + v-bind="$attrs" | |
| 6 | + width="30rem" | |
| 7 | + :height="50" | |
| 8 | + :minHeight="50" | |
| 9 | + @register="register" | |
| 10 | + title="操作密码" | |
| 11 | + @ok="handleSuccess" | |
| 12 | + @cancel="handleClose" | |
| 13 | + > | |
| 14 | + <div> | |
| 15 | + <BasicForm @register="registerForm" /> | |
| 16 | + </div> | |
| 17 | + </BasicModal> | |
| 18 | + </div> | |
| 19 | +</template> | |
| 20 | +<script setup lang="ts"> | |
| 21 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | |
| 22 | + import { useForm, BasicForm } from '/@/components/Form'; | |
| 23 | + import { formSchema } from './config'; | |
| 24 | + import { useMessage } from '/@/hooks/web/useMessage'; | |
| 25 | + import { ref, unref } from 'vue'; | |
| 26 | + | |
| 27 | + const emit = defineEmits(['register', 'success', 'cancel']); | |
| 28 | + | |
| 29 | + const persetData = ref<{ [key: string]: string | number }>({}); | |
| 30 | + | |
| 31 | + const { createMessage } = useMessage(); | |
| 32 | + | |
| 33 | + const [registerForm, { getFieldsValue, validate }] = useForm({ | |
| 34 | + labelWidth: 70, | |
| 35 | + schemas: formSchema, | |
| 36 | + showActionButtonGroup: false, | |
| 37 | + }); | |
| 38 | + | |
| 39 | + const [register, { setModalProps, closeModal }] = useModalInner(async (data) => { | |
| 40 | + setModalProps({ loading: true }); | |
| 41 | + persetData.value = data; | |
| 42 | + setModalProps({ loading: false }); | |
| 43 | + }); | |
| 44 | + | |
| 45 | + const handleClose = () => { | |
| 46 | + emit('cancel'); | |
| 47 | + closeModal(); | |
| 48 | + }; | |
| 49 | + | |
| 50 | + const handleSuccess = async () => { | |
| 51 | + await validate(); | |
| 52 | + const { password: passwordField } = getFieldsValue(); | |
| 53 | + if (unref(persetData).password != passwordField) { | |
| 54 | + createMessage.warning('操作密码不正确'); | |
| 55 | + return; | |
| 56 | + } | |
| 57 | + emit('success', unref(persetData)); | |
| 58 | + closeModal(); | |
| 59 | + }; | |
| 60 | +</script> | |
| 61 | +<style lang="less" scoped></style> | ... | ... |
| 1 | +import { FormSchema } from '/@/components/Form'; | |
| 2 | + | |
| 3 | +export const formSchema: FormSchema[] = [ | |
| 4 | + { | |
| 5 | + field: 'password', | |
| 6 | + label: '操作密码', | |
| 7 | + colProps: { span: 16 }, | |
| 8 | + component: 'InputPassword', | |
| 9 | + required: true, | |
| 10 | + componentProps: { | |
| 11 | + placeholder: '请输入操作密码', | |
| 12 | + max: 120, | |
| 13 | + }, | |
| 14 | + }, | |
| 15 | +]; | ... | ... |
| ... | ... | @@ -15,6 +15,7 @@ import { TransportTypeEnum } from '/@/views/device/profiles/components/Transport |
| 15 | 15 | import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; |
| 16 | 16 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
| 17 | 17 | import { TaskTypeEnum } from '/@/views/task/center/config'; |
| 18 | +import { createPickerSearch } from '/@/utils/pickerSearch'; | |
| 18 | 19 | |
| 19 | 20 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 20 | 21 | |
| ... | ... | @@ -247,6 +248,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 247 | 248 | }, |
| 248 | 249 | placeholder: '请选择设备', |
| 249 | 250 | getPopupContainer: () => document.body, |
| 251 | + ...createPickerSearch(), | |
| 250 | 252 | }; |
| 251 | 253 | }, |
| 252 | 254 | }, |
| ... | ... | @@ -387,6 +389,7 @@ export const commonDataSourceSchemas = (): FormSchema[] => { |
| 387 | 389 | [DataSourceField.EXTENSION_DESC]: value ? JSON.stringify(option.extensionDesc) : '', |
| 388 | 390 | }); |
| 389 | 391 | }, |
| 392 | + ...createPickerSearch(), | |
| 390 | 393 | }; |
| 391 | 394 | }, |
| 392 | 395 | }, | ... | ... |