Commit e277f68336d4acc1e0012c72d2e28b296e1093ae
1 parent
8c3860d7
feat: 产品编辑时新增ota升级包配置&&变更ApiSearchSelect组件用法
Showing
11 changed files
with
187 additions
and
281 deletions
| @@ -17,7 +17,7 @@ enum Api { | @@ -17,7 +17,7 @@ enum Api { | ||
| 17 | CREATE_OTA_PACKAGES = '/otaPackage', | 17 | CREATE_OTA_PACKAGES = '/otaPackage', |
| 18 | UPLOAD_OTA_PACKAGES = '/otaPackage', | 18 | UPLOAD_OTA_PACKAGES = '/otaPackage', |
| 19 | DELETE_OTA_PACKAGES = '/otaPackage', | 19 | DELETE_OTA_PACKAGES = '/otaPackage', |
| 20 | - GET_DEVICE_PROFILE_INFO_DEFAULT = '/deviceProfileInfo/default', | 20 | + GET_DEVICE_PROFILE_INFO_DEFAULT = '/deviceProfileInfo', |
| 21 | GET_OTA_PACKAGE_INFO = '/otaPackage/info', | 21 | GET_OTA_PACKAGE_INFO = '/otaPackage/info', |
| 22 | DOWNLOAD_PACKAGE = '/otaPackage', | 22 | DOWNLOAD_PACKAGE = '/otaPackage', |
| 23 | 23 | ||
| @@ -75,13 +75,13 @@ export const uploadOtaPackages = (params: UploadOtaPackagesParams) => { | @@ -75,13 +75,13 @@ export const uploadOtaPackages = (params: UploadOtaPackagesParams) => { | ||
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | /** | 77 | /** |
| 78 | - * @description 获取设备默认信息 | 78 | + * @description 获取设备信息 |
| 79 | * @returns | 79 | * @returns |
| 80 | */ | 80 | */ |
| 81 | -export const getDefaultDeviceProfile = () => { | 81 | +export const getDeviceProfileInfo = (id: string) => { |
| 82 | return defHttp.get<DefaultDeviceProfileInfo>( | 82 | return defHttp.get<DefaultDeviceProfileInfo>( |
| 83 | { | 83 | { |
| 84 | - url: Api.GET_DEVICE_PROFILE_INFO_DEFAULT, | 84 | + url: `${Api.GET_DEVICE_PROFILE_INFO_DEFAULT}/${id}`, |
| 85 | }, | 85 | }, |
| 86 | { | 86 | { |
| 87 | joinPrefix: false, | 87 | joinPrefix: false, |
| @@ -88,6 +88,7 @@ export interface CreateOtaPackageParams { | @@ -88,6 +88,7 @@ export interface CreateOtaPackageParams { | ||
| 88 | type: string; | 88 | type: string; |
| 89 | deviceProfileId: IdRecord; | 89 | deviceProfileId: IdRecord; |
| 90 | isURL: boolean; | 90 | isURL: boolean; |
| 91 | + url: Nullable<string>; | ||
| 91 | additionalInfo: AdditionalInfo; | 92 | additionalInfo: AdditionalInfo; |
| 92 | } | 93 | } |
| 93 | 94 |
src/components/Form/src/components/ApiQuerySelect.vue
deleted
100644 → 0
| 1 | -<script lang="ts"> | ||
| 2 | - export type OptionsItem = { label: string; value: string; disabled?: boolean }; | ||
| 3 | - export interface OnChangeHookParams { | ||
| 4 | - options: Ref<OptionsItem[]>; | ||
| 5 | - } | ||
| 6 | -</script> | ||
| 7 | - | ||
| 8 | -<script lang="ts" setup> | ||
| 9 | - import { ref, watchEffect, computed, unref, watch, Ref } from 'vue'; | ||
| 10 | - import { Select } from 'ant-design-vue'; | ||
| 11 | - import { isFunction } from '/@/utils/is'; | ||
| 12 | - import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | ||
| 13 | - import { get, omit } from 'lodash-es'; | ||
| 14 | - import { LoadingOutlined } from '@ant-design/icons-vue'; | ||
| 15 | - import { useI18n } from '/@/hooks/web/useI18n'; | ||
| 16 | - import { useDebounceFn } from '@vueuse/shared'; | ||
| 17 | - | ||
| 18 | - const emit = defineEmits(['options-change', 'change']); | ||
| 19 | - const props = withDefaults( | ||
| 20 | - defineProps<{ | ||
| 21 | - value?: Recordable | number | string; | ||
| 22 | - numberToString?: boolean; | ||
| 23 | - api?: (arg?: Recordable) => Promise<Recordable>; | ||
| 24 | - queryApi?: (value?: any) => Promise<Recordable>; | ||
| 25 | - params?: Recordable | ((searchText?: string) => Recordable); | ||
| 26 | - resultField?: string; | ||
| 27 | - labelField?: string; | ||
| 28 | - valueField?: string; | ||
| 29 | - immediate?: boolean; | ||
| 30 | - queryEmptyDataAgin?: boolean; | ||
| 31 | - }>(), | ||
| 32 | - { | ||
| 33 | - resultField: '', | ||
| 34 | - labelField: 'label', | ||
| 35 | - valueField: 'value', | ||
| 36 | - searchField: 'text', | ||
| 37 | - immediate: true, | ||
| 38 | - queryEmptyDataAgin: true, | ||
| 39 | - } | ||
| 40 | - ); | ||
| 41 | - | ||
| 42 | - const selectOption = ref<OptionsItem>(); | ||
| 43 | - const options = ref<OptionsItem[]>([]); | ||
| 44 | - const loading = ref(false); | ||
| 45 | - const isFirstLoad = ref(true); | ||
| 46 | - const emitData = ref<any[]>([]); | ||
| 47 | - const { t } = useI18n(); | ||
| 48 | - | ||
| 49 | - // Embedded in the form, just use the hook binding to perform form verification | ||
| 50 | - const [state] = useRuleFormItem(props, 'value', 'change', emitData); | ||
| 51 | - | ||
| 52 | - const getOptions = computed(() => { | ||
| 53 | - const { labelField, valueField = 'value', numberToString } = props; | ||
| 54 | - const _options = unref(options); | ||
| 55 | - | ||
| 56 | - if ( | ||
| 57 | - unref(selectOption) && | ||
| 58 | - !_options.find((item) => get(item, valueField) === get(unref(selectOption), valueField)) | ||
| 59 | - ) { | ||
| 60 | - _options.push(unref(selectOption)!); | ||
| 61 | - } | ||
| 62 | - return _options.reduce((prev, next: Recordable) => { | ||
| 63 | - if (next) { | ||
| 64 | - const value = get(next, valueField); | ||
| 65 | - const label = get(next, labelField); | ||
| 66 | - prev.push({ | ||
| 67 | - ...omit(next, [labelField, valueField]), | ||
| 68 | - label, | ||
| 69 | - value: numberToString ? `${value}` : value, | ||
| 70 | - }); | ||
| 71 | - } | ||
| 72 | - return prev; | ||
| 73 | - }, [] as OptionsItem[]); | ||
| 74 | - }); | ||
| 75 | - | ||
| 76 | - watchEffect(() => { | ||
| 77 | - props.immediate && fetch(); | ||
| 78 | - }); | ||
| 79 | - | ||
| 80 | - watch( | ||
| 81 | - () => props.params, | ||
| 82 | - () => { | ||
| 83 | - !unref(isFirstLoad) && fetch(); | ||
| 84 | - }, | ||
| 85 | - { deep: true } | ||
| 86 | - ); | ||
| 87 | - | ||
| 88 | - watch( | ||
| 89 | - () => props.value, | ||
| 90 | - async (target) => { | ||
| 91 | - if (target && props.queryApi && isFunction(props.queryApi)) { | ||
| 92 | - if (unref(getOptions).find((item) => item.value === target)) return; | ||
| 93 | - const detail = await props.queryApi(target); | ||
| 94 | - if ( | ||
| 95 | - unref(options).find( | ||
| 96 | - (item) => get(item, props.valueField) === get(detail, props.valueField) | ||
| 97 | - ) | ||
| 98 | - ) | ||
| 99 | - return; | ||
| 100 | - | ||
| 101 | - selectOption.value = detail as OptionsItem; | ||
| 102 | - } | ||
| 103 | - }, | ||
| 104 | - { | ||
| 105 | - immediate: true, | ||
| 106 | - } | ||
| 107 | - ); | ||
| 108 | - | ||
| 109 | - async function fetch(searchText?: string) { | ||
| 110 | - const api = props.api; | ||
| 111 | - if (!api || !isFunction(api)) return; | ||
| 112 | - options.value = []; | ||
| 113 | - try { | ||
| 114 | - loading.value = true; | ||
| 115 | - const params = | ||
| 116 | - props.params && isFunction(props.params) ? props.params(searchText) : props.params; | ||
| 117 | - | ||
| 118 | - const res = await api(params); | ||
| 119 | - | ||
| 120 | - if (Array.isArray(res)) { | ||
| 121 | - options.value = res; | ||
| 122 | - emitChange(); | ||
| 123 | - return; | ||
| 124 | - } | ||
| 125 | - | ||
| 126 | - if (props.resultField) { | ||
| 127 | - options.value = get(res, props.resultField) || []; | ||
| 128 | - } | ||
| 129 | - emitChange(); | ||
| 130 | - } catch (error) { | ||
| 131 | - console.warn(error); | ||
| 132 | - } finally { | ||
| 133 | - loading.value = false; | ||
| 134 | - } | ||
| 135 | - } | ||
| 136 | - | ||
| 137 | - async function handleFetch() { | ||
| 138 | - const { immediate } = props; | ||
| 139 | - if (!immediate && unref(isFirstLoad)) { | ||
| 140 | - await fetch(); | ||
| 141 | - isFirstLoad.value = false; | ||
| 142 | - } | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - function emitChange() { | ||
| 146 | - emit('options-change', unref(getOptions)); | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - function handleChange(value: string, ...args) { | ||
| 150 | - emitData.value = args; | ||
| 151 | - if (!value && props.queryEmptyDataAgin) fetch(); | ||
| 152 | - } | ||
| 153 | - | ||
| 154 | - const debounceSearchFunction = useDebounceFn(fetch, 300); | ||
| 155 | -</script> | ||
| 156 | - | ||
| 157 | -<template> | ||
| 158 | - <Select | ||
| 159 | - v-bind="$attrs" | ||
| 160 | - show-search | ||
| 161 | - @dropdownVisibleChange="handleFetch" | ||
| 162 | - @change="handleChange" | ||
| 163 | - :options="getOptions" | ||
| 164 | - :filter-option="false" | ||
| 165 | - @search="debounceSearchFunction" | ||
| 166 | - v-model:value="state" | ||
| 167 | - > | ||
| 168 | - <template #[item]="data" v-for="item in Object.keys($slots)"> | ||
| 169 | - <slot :name="item" v-bind="data || {}"></slot> | ||
| 170 | - </template> | ||
| 171 | - <template #suffixIcon v-if="loading"> | ||
| 172 | - <LoadingOutlined spin /> | ||
| 173 | - </template> | ||
| 174 | - <template #notFoundContent v-if="loading"> | ||
| 175 | - <span> | ||
| 176 | - <LoadingOutlined spin class="mr-1" /> | ||
| 177 | - {{ t('component.form.apiSelectNotFound') }} | ||
| 178 | - </span> | ||
| 179 | - </template> | ||
| 180 | - </Select> | ||
| 181 | -</template> |
| @@ -10,7 +10,6 @@ | @@ -10,7 +10,6 @@ | ||
| 10 | import { Select } from 'ant-design-vue'; | 10 | import { Select } from 'ant-design-vue'; |
| 11 | import { isFunction } from '/@/utils/is'; | 11 | import { isFunction } from '/@/utils/is'; |
| 12 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; | 12 | import { useRuleFormItem } from '/@/hooks/component/useFormItem'; |
| 13 | - import { useAttrs } from '/@/hooks/core/useAttrs'; | ||
| 14 | import { get, omit } from 'lodash-es'; | 13 | import { get, omit } from 'lodash-es'; |
| 15 | import { LoadingOutlined } from '@ant-design/icons-vue'; | 14 | import { LoadingOutlined } from '@ant-design/icons-vue'; |
| 16 | import { useI18n } from '/@/hooks/web/useI18n'; | 15 | import { useI18n } from '/@/hooks/web/useI18n'; |
| @@ -21,17 +20,14 @@ | @@ -21,17 +20,14 @@ | ||
| 21 | defineProps<{ | 20 | defineProps<{ |
| 22 | value?: Recordable | number | string; | 21 | value?: Recordable | number | string; |
| 23 | numberToString?: boolean; | 22 | numberToString?: boolean; |
| 24 | - api?: (arg?: Recordable) => Promise<OptionsItem[]>; | ||
| 25 | - searchApi?: (arg?: Recordable) => Promise<OptionsItem[]>; | ||
| 26 | - params?: Recordable; | 23 | + api?: (arg?: Recordable) => Promise<Recordable>; |
| 24 | + queryApi?: (value?: any) => Promise<Recordable>; | ||
| 25 | + params?: Recordable | ((searchText?: string) => Recordable); | ||
| 27 | resultField?: string; | 26 | resultField?: string; |
| 28 | labelField?: string; | 27 | labelField?: string; |
| 29 | valueField?: string; | 28 | valueField?: string; |
| 30 | immediate?: boolean; | 29 | immediate?: boolean; |
| 31 | - searchField?: string; | ||
| 32 | queryEmptyDataAgin?: boolean; | 30 | queryEmptyDataAgin?: boolean; |
| 33 | - onChangeHook?: ({ options }: OnChangeHookParams) => void; | ||
| 34 | - dropdownVisibleChangeHook?: ({ options }: OnChangeHookParams) => void; | ||
| 35 | }>(), | 31 | }>(), |
| 36 | { | 32 | { |
| 37 | resultField: '', | 33 | resultField: '', |
| @@ -42,11 +38,12 @@ | @@ -42,11 +38,12 @@ | ||
| 42 | queryEmptyDataAgin: true, | 38 | queryEmptyDataAgin: true, |
| 43 | } | 39 | } |
| 44 | ); | 40 | ); |
| 41 | + | ||
| 42 | + const selectOption = ref<OptionsItem>(); | ||
| 45 | const options = ref<OptionsItem[]>([]); | 43 | const options = ref<OptionsItem[]>([]); |
| 46 | const loading = ref(false); | 44 | const loading = ref(false); |
| 47 | const isFirstLoad = ref(true); | 45 | const isFirstLoad = ref(true); |
| 48 | const emitData = ref<any[]>([]); | 46 | const emitData = ref<any[]>([]); |
| 49 | - const attrs = useAttrs(); | ||
| 50 | const { t } = useI18n(); | 47 | const { t } = useI18n(); |
| 51 | 48 | ||
| 52 | // Embedded in the form, just use the hook binding to perform form verification | 49 | // Embedded in the form, just use the hook binding to perform form verification |
| @@ -54,7 +51,15 @@ | @@ -54,7 +51,15 @@ | ||
| 54 | 51 | ||
| 55 | const getOptions = computed(() => { | 52 | const getOptions = computed(() => { |
| 56 | const { labelField, valueField = 'value', numberToString } = props; | 53 | const { labelField, valueField = 'value', numberToString } = props; |
| 57 | - return unref(options).reduce((prev, next: Recordable) => { | 54 | + const _options = unref(options); |
| 55 | + | ||
| 56 | + if ( | ||
| 57 | + unref(selectOption) && | ||
| 58 | + !_options.find((item) => get(item, valueField) === get(unref(selectOption), valueField)) | ||
| 59 | + ) { | ||
| 60 | + _options.push(unref(selectOption)!); | ||
| 61 | + } | ||
| 62 | + return _options.reduce((prev, next: Recordable) => { | ||
| 58 | if (next) { | 63 | if (next) { |
| 59 | const value = get(next, valueField); | 64 | const value = get(next, valueField); |
| 60 | const label = get(next, labelField); | 65 | const label = get(next, labelField); |
| @@ -68,15 +73,6 @@ | @@ -68,15 +73,6 @@ | ||
| 68 | }, [] as OptionsItem[]); | 73 | }, [] as OptionsItem[]); |
| 69 | }); | 74 | }); |
| 70 | 75 | ||
| 71 | - const getBindProps = computed(() => { | ||
| 72 | - const { searchApi } = props; | ||
| 73 | - return { | ||
| 74 | - ...attrs, | ||
| 75 | - showSearch: true, | ||
| 76 | - filterOption: !searchApi, | ||
| 77 | - }; | ||
| 78 | - }); | ||
| 79 | - | ||
| 80 | watchEffect(() => { | 76 | watchEffect(() => { |
| 81 | props.immediate && fetch(); | 77 | props.immediate && fetch(); |
| 82 | }); | 78 | }); |
| @@ -89,18 +85,45 @@ | @@ -89,18 +85,45 @@ | ||
| 89 | { deep: true } | 85 | { deep: true } |
| 90 | ); | 86 | ); |
| 91 | 87 | ||
| 92 | - async function fetch() { | 88 | + watch( |
| 89 | + () => props.value, | ||
| 90 | + async (target) => { | ||
| 91 | + if (target && props.queryApi && isFunction(props.queryApi)) { | ||
| 92 | + if (unref(getOptions).find((item) => item.value === target)) return; | ||
| 93 | + const detail = await props.queryApi(target); | ||
| 94 | + if ( | ||
| 95 | + !detail || | ||
| 96 | + unref(options).find( | ||
| 97 | + (item) => get(item, props.valueField) === get(detail, props.valueField) | ||
| 98 | + ) | ||
| 99 | + ) | ||
| 100 | + return; | ||
| 101 | + | ||
| 102 | + selectOption.value = detail as OptionsItem; | ||
| 103 | + } | ||
| 104 | + }, | ||
| 105 | + { | ||
| 106 | + immediate: true, | ||
| 107 | + } | ||
| 108 | + ); | ||
| 109 | + | ||
| 110 | + async function fetch(searchText?: string) { | ||
| 93 | const api = props.api; | 111 | const api = props.api; |
| 94 | if (!api || !isFunction(api)) return; | 112 | if (!api || !isFunction(api)) return; |
| 95 | options.value = []; | 113 | options.value = []; |
| 96 | try { | 114 | try { |
| 97 | loading.value = true; | 115 | loading.value = true; |
| 98 | - const res = await api(props.params); | 116 | + const params = |
| 117 | + props.params && isFunction(props.params) ? props.params(searchText) : props.params; | ||
| 118 | + | ||
| 119 | + const res = await api(params); | ||
| 120 | + | ||
| 99 | if (Array.isArray(res)) { | 121 | if (Array.isArray(res)) { |
| 100 | options.value = res; | 122 | options.value = res; |
| 101 | emitChange(); | 123 | emitChange(); |
| 102 | return; | 124 | return; |
| 103 | } | 125 | } |
| 126 | + | ||
| 104 | if (props.resultField) { | 127 | if (props.resultField) { |
| 105 | options.value = get(res, props.resultField) || []; | 128 | options.value = get(res, props.resultField) || []; |
| 106 | } | 129 | } |
| @@ -113,14 +136,11 @@ | @@ -113,14 +136,11 @@ | ||
| 113 | } | 136 | } |
| 114 | 137 | ||
| 115 | async function handleFetch() { | 138 | async function handleFetch() { |
| 116 | - const { immediate, dropdownVisibleChangeHook } = props; | 139 | + const { immediate } = props; |
| 117 | if (!immediate && unref(isFirstLoad)) { | 140 | if (!immediate && unref(isFirstLoad)) { |
| 118 | await fetch(); | 141 | await fetch(); |
| 119 | isFirstLoad.value = false; | 142 | isFirstLoad.value = false; |
| 120 | } | 143 | } |
| 121 | - if (dropdownVisibleChangeHook && isFunction(dropdownVisibleChangeHook)) { | ||
| 122 | - dropdownVisibleChangeHook({ options }); | ||
| 123 | - } | ||
| 124 | } | 144 | } |
| 125 | 145 | ||
| 126 | function emitChange() { | 146 | function emitChange() { |
| @@ -129,47 +149,20 @@ | @@ -129,47 +149,20 @@ | ||
| 129 | 149 | ||
| 130 | function handleChange(value: string, ...args) { | 150 | function handleChange(value: string, ...args) { |
| 131 | emitData.value = args; | 151 | emitData.value = args; |
| 132 | - if (!value && props.queryEmptyDataAgin) handleSearch(); | ||
| 133 | - const { onChangeHook } = props; | ||
| 134 | - if (!onChangeHook && !isFunction(onChangeHook)) return; | ||
| 135 | - onChangeHook({ options }); | 152 | + if (!value && props.queryEmptyDataAgin) fetch(); |
| 136 | } | 153 | } |
| 137 | 154 | ||
| 138 | - const debounceSearchFunction = useDebounceFn(handleSearch, 300); | ||
| 139 | - async function handleSearch(params?: string) { | ||
| 140 | - let { searchApi, api, searchField } = props; | ||
| 141 | - if (!searchApi || !isFunction(searchApi)) { | ||
| 142 | - if (!api || !isFunction(api)) return; | ||
| 143 | - searchApi = api; | ||
| 144 | - } | ||
| 145 | - options.value = []; | ||
| 146 | - try { | ||
| 147 | - loading.value = true; | ||
| 148 | - const res = await searchApi({ ...props.params, [searchField]: params }); | ||
| 149 | - if (Array.isArray(res)) { | ||
| 150 | - options.value = res; | ||
| 151 | - emitChange(); | ||
| 152 | - return; | ||
| 153 | - } | ||
| 154 | - if (props.resultField) { | ||
| 155 | - options.value = get(res, props.resultField) || []; | ||
| 156 | - } | ||
| 157 | - emitChange(); | ||
| 158 | - } catch (error) { | ||
| 159 | - // eslint-disable-next-line no-console | ||
| 160 | - console.warn(error); | ||
| 161 | - } finally { | ||
| 162 | - loading.value = false; | ||
| 163 | - } | ||
| 164 | - } | 155 | + const debounceSearchFunction = useDebounceFn(fetch, 300); |
| 165 | </script> | 156 | </script> |
| 166 | 157 | ||
| 167 | <template> | 158 | <template> |
| 168 | <Select | 159 | <Select |
| 160 | + v-bind="$attrs" | ||
| 161 | + show-search | ||
| 169 | @dropdownVisibleChange="handleFetch" | 162 | @dropdownVisibleChange="handleFetch" |
| 170 | - v-bind="getBindProps" | ||
| 171 | @change="handleChange" | 163 | @change="handleChange" |
| 172 | :options="getOptions" | 164 | :options="getOptions" |
| 165 | + :filter-option="false" | ||
| 173 | @search="debounceSearchFunction" | 166 | @search="debounceSearchFunction" |
| 174 | v-model:value="state" | 167 | v-model:value="state" |
| 175 | > | 168 | > |
| @@ -13,7 +13,6 @@ import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | @@ -13,7 +13,6 @@ import { OrgTreeSelect } from '/@/views/common/OrgTreeSelect'; | ||
| 13 | import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; | 13 | import { TCPProtocolTypeEnum, TransportTypeEnum } from '/@/enums/deviceEnum'; |
| 14 | import { HexInput, InputTypeEnum } from '../../profiles/components/ObjectModelForm/HexInput'; | 14 | import { HexInput, InputTypeEnum } from '../../profiles/components/ObjectModelForm/HexInput'; |
| 15 | import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; | 15 | import { DeviceProfileDetail } from '/@/api/device/model/deviceConfigModel'; |
| 16 | -import ApiQuerySelectVue from '/@/components/Form/src/components/ApiQuerySelect.vue'; | ||
| 17 | import { getDeviceProfileOtaPackages, getOtaPackageInfo } from '/@/api/ota'; | 16 | import { getDeviceProfileOtaPackages, getOtaPackageInfo } from '/@/api/ota'; |
| 18 | import { QueryDeviceProfileOtaPackagesType } from '/@/api/ota/model'; | 17 | import { QueryDeviceProfileOtaPackagesType } from '/@/api/ota/model'; |
| 19 | import { OTAPackageType } from '/@/enums/otaEnum'; | 18 | import { OTAPackageType } from '/@/enums/otaEnum'; |
| @@ -22,7 +21,6 @@ useComponentRegister('JSONEditor', JSONEditor); | @@ -22,7 +21,6 @@ useComponentRegister('JSONEditor', JSONEditor); | ||
| 22 | useComponentRegister('LockControlGroup', LockControlGroup); | 21 | useComponentRegister('LockControlGroup', LockControlGroup); |
| 23 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); | 22 | useComponentRegister('OrgTreeSelect', OrgTreeSelect); |
| 24 | useComponentRegister('HexInput', HexInput); | 23 | useComponentRegister('HexInput', HexInput); |
| 25 | -useComponentRegister('ApiQuerySelect', ApiQuerySelectVue); | ||
| 26 | 24 | ||
| 27 | export enum TypeEnum { | 25 | export enum TypeEnum { |
| 28 | IS_GATEWAY = 'GATEWAY', | 26 | IS_GATEWAY = 'GATEWAY', |
| @@ -382,7 +380,7 @@ export const step1Schemas: FormSchema[] = [ | @@ -382,7 +380,7 @@ export const step1Schemas: FormSchema[] = [ | ||
| 382 | { | 380 | { |
| 383 | field: 'firmwareId', | 381 | field: 'firmwareId', |
| 384 | label: '分配的固件', | 382 | label: '分配的固件', |
| 385 | - component: 'ApiQuerySelect', | 383 | + component: 'ApiSearchSelect', |
| 386 | ifShow: ({ model }) => model?.isUpdate, | 384 | ifShow: ({ model }) => model?.isUpdate, |
| 387 | componentProps: ({ formModel }) => { | 385 | componentProps: ({ formModel }) => { |
| 388 | return { | 386 | return { |
| @@ -411,7 +409,7 @@ export const step1Schemas: FormSchema[] = [ | @@ -411,7 +409,7 @@ export const step1Schemas: FormSchema[] = [ | ||
| 411 | { | 409 | { |
| 412 | field: 'softwareId', | 410 | field: 'softwareId', |
| 413 | label: '分配的软件', | 411 | label: '分配的软件', |
| 414 | - component: 'ApiQuerySelect', | 412 | + component: 'ApiSearchSelect', |
| 415 | ifShow: ({ model }) => model?.isUpdate, | 413 | ifShow: ({ model }) => model?.isUpdate, |
| 416 | componentProps: ({ formModel }) => { | 414 | componentProps: ({ formModel }) => { |
| 417 | return { | 415 | return { |
| @@ -16,6 +16,9 @@ import { useMessage } from '/@/hooks/web/useMessage'; | @@ -16,6 +16,9 @@ import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 16 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 16 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
| 17 | import { createImgPreview } from '/@/components/Preview'; | 17 | import { createImgPreview } from '/@/components/Preview'; |
| 18 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; | 18 | import { uploadThumbnail } from '/@/api/configuration/center/configurationCenter'; |
| 19 | +import { getDeviceProfileOtaPackages, getOtaPackageInfo } from '/@/api/ota'; | ||
| 20 | +import { QueryDeviceProfileOtaPackagesType } from '/@/api/ota/model'; | ||
| 21 | +import { OTAPackageType } from '/@/enums/otaEnum'; | ||
| 19 | 22 | ||
| 20 | export enum Mode { | 23 | export enum Mode { |
| 21 | CARD = 'card', | 24 | CARD = 'card', |
| @@ -305,7 +308,78 @@ export const step1Schemas: FormSchema[] = [ | @@ -305,7 +308,78 @@ export const step1Schemas: FormSchema[] = [ | ||
| 305 | resultField: 'items', | 308 | resultField: 'items', |
| 306 | }, | 309 | }, |
| 307 | }, | 310 | }, |
| 308 | - | 311 | + { |
| 312 | + field: 'isUpdate', | ||
| 313 | + component: 'Switch', | ||
| 314 | + label: '', | ||
| 315 | + ifShow: false, | ||
| 316 | + }, | ||
| 317 | + { | ||
| 318 | + field: 'profileId', | ||
| 319 | + component: 'Input', | ||
| 320 | + label: '', | ||
| 321 | + ifShow: false, | ||
| 322 | + }, | ||
| 323 | + { | ||
| 324 | + field: 'firmwareId', | ||
| 325 | + label: '分配的固件', | ||
| 326 | + component: 'ApiSearchSelect', | ||
| 327 | + colProps: { span: 14 }, | ||
| 328 | + ifShow: ({ model }) => model?.isUpdate, | ||
| 329 | + componentProps: ({ formModel }) => { | ||
| 330 | + return { | ||
| 331 | + placeholder: '请选择分配的固件', | ||
| 332 | + api: async (params: QueryDeviceProfileOtaPackagesType) => { | ||
| 333 | + if (!params.deviceProfileId) return []; | ||
| 334 | + const result = await getDeviceProfileOtaPackages(params); | ||
| 335 | + return result.data.map((item) => ({ label: item.title, value: item.id.id })); | ||
| 336 | + }, | ||
| 337 | + params: (textSearch: string) => { | ||
| 338 | + return { | ||
| 339 | + textSearch, | ||
| 340 | + page: 0, | ||
| 341 | + type: OTAPackageType.FIRMWARE, | ||
| 342 | + pageSize: 10, | ||
| 343 | + deviceProfileId: formModel?.profileId, | ||
| 344 | + }; | ||
| 345 | + }, | ||
| 346 | + queryApi: async (id: string) => { | ||
| 347 | + const result = await getOtaPackageInfo(id); | ||
| 348 | + return { label: result.title, value: result.id.id }; | ||
| 349 | + }, | ||
| 350 | + }; | ||
| 351 | + }, | ||
| 352 | + }, | ||
| 353 | + { | ||
| 354 | + field: 'softwareId', | ||
| 355 | + label: '分配的软件', | ||
| 356 | + component: 'ApiSearchSelect', | ||
| 357 | + colProps: { span: 14 }, | ||
| 358 | + ifShow: ({ model }) => model?.isUpdate, | ||
| 359 | + componentProps: ({ formModel }) => { | ||
| 360 | + return { | ||
| 361 | + placeholder: '请选择分配的软件', | ||
| 362 | + api: async (params: QueryDeviceProfileOtaPackagesType) => { | ||
| 363 | + if (!params.deviceProfileId) return []; | ||
| 364 | + const result = await getDeviceProfileOtaPackages(params); | ||
| 365 | + return result.data.map((item) => ({ label: item.title, value: item.id.id })); | ||
| 366 | + }, | ||
| 367 | + params: (textSearch: string) => { | ||
| 368 | + return { | ||
| 369 | + textSearch, | ||
| 370 | + page: 0, | ||
| 371 | + type: OTAPackageType.SOFTWARE, | ||
| 372 | + pageSize: 10, | ||
| 373 | + deviceProfileId: formModel?.profileId, | ||
| 374 | + }; | ||
| 375 | + }, | ||
| 376 | + queryApi: async (id: string) => { | ||
| 377 | + const result = await getOtaPackageInfo(id); | ||
| 378 | + return { label: result.title, value: result.id.id }; | ||
| 379 | + }, | ||
| 380 | + }; | ||
| 381 | + }, | ||
| 382 | + }, | ||
| 309 | { | 383 | { |
| 310 | label: '描述', | 384 | label: '描述', |
| 311 | field: 'description', | 385 | field: 'description', |
| @@ -90,7 +90,12 @@ | @@ -90,7 +90,12 @@ | ||
| 90 | // 不能把image字段回显进去,页面会显示大量警告 | 90 | // 不能把image字段回显进去,页面会显示大量警告 |
| 91 | const { image, ...params } = v; | 91 | const { image, ...params } = v; |
| 92 | imageUrl.value = image; | 92 | imageUrl.value = image; |
| 93 | - setFieldsValue({ ...params, category: params?.categoryId ? 1 : 2 }); | 93 | + setFieldsValue({ |
| 94 | + ...params, | ||
| 95 | + category: params?.categoryId ? 1 : 2, | ||
| 96 | + isUpdate: true, | ||
| 97 | + profileId: v?.tbProfileId, | ||
| 98 | + }); | ||
| 94 | }; | 99 | }; |
| 95 | //获取数据 | 100 | //获取数据 |
| 96 | async function getFormData() { | 101 | async function getFormData() { |
| @@ -55,9 +55,12 @@ | @@ -55,9 +55,12 @@ | ||
| 55 | try { | 55 | try { |
| 56 | setLoading(true); | 56 | setLoading(true); |
| 57 | const { id } = await createOtaPackage(value); | 57 | const { id } = await createOtaPackage(value); |
| 58 | - const { isURL } = value; | ||
| 59 | - if (!isURL) { | 58 | + const { url } = value; |
| 59 | + if (!url) { | ||
| 60 | await handleUploadFile(value, id.id); | 60 | await handleUploadFile(value, id.id); |
| 61 | + } else { | ||
| 62 | + closeModal(); | ||
| 63 | + emit('update:list'); | ||
| 61 | } | 64 | } |
| 62 | } catch (error) { | 65 | } catch (error) { |
| 63 | } finally { | 66 | } finally { |
| @@ -69,7 +72,10 @@ | @@ -69,7 +72,10 @@ | ||
| 69 | try { | 72 | try { |
| 70 | await validate(); | 73 | await validate(); |
| 71 | const value = getFieldsValue(); | 74 | const value = getFieldsValue(); |
| 72 | - value[PackageField.DEVICE_PROFILE_INFO] = JSON.parse(value[PackageField.DEVICE_PROFILE_INFO]); | 75 | + value[PackageField.DEVICE_PROFILE_INFO] = { |
| 76 | + id: value[PackageField.DEVICE_PROFILE_INFO], | ||
| 77 | + entityType: 'DEVICE_PROFILE', | ||
| 78 | + }; | ||
| 73 | value[PackageField.ADDITIONAL_INFO] = { | 79 | value[PackageField.ADDITIONAL_INFO] = { |
| 74 | [PackageField.DESCRIPTION]: value[PackageField.DESCRIPTION], | 80 | [PackageField.DESCRIPTION]: value[PackageField.DESCRIPTION], |
| 75 | }; | 81 | }; |
| 1 | -import { getDefaultDeviceProfile, getDeviceProfileInfos } from '/@/api/ota'; | ||
| 2 | -import { Id } from '/@/api/ota/model'; | 1 | +import { getDeviceProfileInfo, getDeviceProfileInfos } from '/@/api/ota'; |
| 2 | + | ||
| 3 | import { FormSchema } from '/@/components/Form'; | 3 | import { FormSchema } from '/@/components/Form'; |
| 4 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | 4 | import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; |
| 5 | 5 | ||
| @@ -109,29 +109,36 @@ export const formSchema: FormSchema[] = [ | @@ -109,29 +109,36 @@ export const formSchema: FormSchema[] = [ | ||
| 109 | label: '所属产品', | 109 | label: '所属产品', |
| 110 | component: 'ApiSearchSelect', | 110 | component: 'ApiSearchSelect', |
| 111 | helpMessage: ['上传的包仅适用于具有所选配置文件的设备'], | 111 | helpMessage: ['上传的包仅适用于具有所选配置文件的设备'], |
| 112 | - defaultValue: 'default', | ||
| 113 | rules: [{ required: true, message: '所属产品为必填项' }], | 112 | rules: [{ required: true, message: '所属产品为必填项' }], |
| 114 | - componentProps: ({ formActionType }) => { | ||
| 115 | - const { setFieldsValue } = formActionType; | 113 | + defaultValue: 'default', |
| 114 | + componentProps: ({ formModel, formActionType }) => { | ||
| 116 | return { | 115 | return { |
| 117 | placeholder: '请选择所属产品', | 116 | placeholder: '请选择所属产品', |
| 118 | showSearch: true, | 117 | showSearch: true, |
| 119 | - resultField: 'data', | ||
| 120 | labelField: 'name', | 118 | labelField: 'name', |
| 121 | valueField: 'id', | 119 | valueField: 'id', |
| 122 | - api: async () => { | ||
| 123 | - const data = await getDefaultDeviceProfile(); | ||
| 124 | - data.id = JSON.stringify(data.id) as unknown as Id; | ||
| 125 | - setFieldsValue({ [PackageField.DEVICE_PROFILE_INFO]: data.id }); | ||
| 126 | - return { data: [data] }; | ||
| 127 | - }, | ||
| 128 | - searchApi: async (params: Recordable) => { | ||
| 129 | - const data = await getDeviceProfileInfos({ textSearch: params.text }); | ||
| 130 | - data.data = data.data.map((item) => ({ | 120 | + api: async (params: Recordable) => { |
| 121 | + const data = await getDeviceProfileInfos(params); | ||
| 122 | + return data.data.map((item) => ({ | ||
| 131 | ...item, | 123 | ...item, |
| 132 | - id: JSON.stringify(item.id) as unknown as Id, | 124 | + id: item.id.id, |
| 133 | })); | 125 | })); |
| 134 | - return data; | 126 | + }, |
| 127 | + params: (textSearch: string) => { | ||
| 128 | + return { | ||
| 129 | + page: 0, | ||
| 130 | + pageSize: 10, | ||
| 131 | + textSearch, | ||
| 132 | + }; | ||
| 133 | + }, | ||
| 134 | + queryApi: async (id = 'default') => { | ||
| 135 | + const data = await getDeviceProfileInfo(id); | ||
| 136 | + | ||
| 137 | + if (formModel[PackageField.DEVICE_PROFILE_INFO] === 'default' && id === 'default') { | ||
| 138 | + formActionType.setFieldsValue({ [PackageField.DEVICE_PROFILE_INFO]: data.id.id }); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + return { ...data, id: data.id.id }; | ||
| 135 | }, | 142 | }, |
| 136 | }; | 143 | }; |
| 137 | }, | 144 | }, |
| @@ -17,21 +17,17 @@ const { t } = useI18n(); | @@ -17,21 +17,17 @@ const { t } = useI18n(); | ||
| 17 | 17 | ||
| 18 | import { | 18 | import { |
| 19 | getEntityDevice, | 19 | getEntityDevice, |
| 20 | - getEntityAssets, | ||
| 21 | - getEntityViews, | ||
| 22 | getEntityTenant, | 20 | getEntityTenant, |
| 23 | getEntityCustomer, | 21 | getEntityCustomer, |
| 24 | getEntityUser, | 22 | getEntityUser, |
| 25 | - getEntityDashboard, | ||
| 26 | - getEntityEdge, | ||
| 27 | } from '/@/api/ruleChainDesigner'; | 23 | } from '/@/api/ruleChainDesigner'; |
| 28 | import { useUserStore } from '/@/store/modules/user'; | 24 | import { useUserStore } from '/@/store/modules/user'; |
| 29 | 25 | ||
| 30 | export const getEntityIdSelect = (type: EntityTypeEnum) => { | 26 | export const getEntityIdSelect = (type: EntityTypeEnum) => { |
| 31 | const method = { | 27 | const method = { |
| 32 | [EntityTypeEnum.DEVICE]: getEntityDevice, | 28 | [EntityTypeEnum.DEVICE]: getEntityDevice, |
| 33 | - [EntityTypeEnum.ASSET]: getEntityAssets, | ||
| 34 | - [EntityTypeEnum.ENTITY_VIEW]: getEntityViews, | 29 | + // [EntityTypeEnum.ASSET]: getEntityAssets, |
| 30 | + // [EntityTypeEnum.ENTITY_VIEW]: getEntityViews, | ||
| 35 | [EntityTypeEnum.TENANT]: async () => { | 31 | [EntityTypeEnum.TENANT]: async () => { |
| 36 | const userInfo = useUserStore(); | 32 | const userInfo = useUserStore(); |
| 37 | const params = { tenantId: userInfo.getUserInfo.tenantId! }; | 33 | const params = { tenantId: userInfo.getUserInfo.tenantId! }; |
| @@ -40,8 +36,8 @@ export const getEntityIdSelect = (type: EntityTypeEnum) => { | @@ -40,8 +36,8 @@ export const getEntityIdSelect = (type: EntityTypeEnum) => { | ||
| 40 | }, | 36 | }, |
| 41 | [EntityTypeEnum.CUSTOMER]: getEntityCustomer, | 37 | [EntityTypeEnum.CUSTOMER]: getEntityCustomer, |
| 42 | [EntityTypeEnum.USER]: getEntityUser, | 38 | [EntityTypeEnum.USER]: getEntityUser, |
| 43 | - [EntityTypeEnum.DASHBOARD]: getEntityDashboard, | ||
| 44 | - [EntityTypeEnum.EDGE]: getEntityEdge, | 39 | + // [EntityTypeEnum.DASHBOARD]: getEntityDashboard, |
| 40 | + // [EntityTypeEnum.EDGE]: getEntityEdge, | ||
| 45 | }; | 41 | }; |
| 46 | 42 | ||
| 47 | const params: Recordable = { | 43 | const params: Recordable = { |
| 1 | import { RouteLocationNormalizedLoaded } from 'vue-router'; | 1 | import { RouteLocationNormalizedLoaded } from 'vue-router'; |
| 2 | import { RuleChainFieldsEnum, RuleChainFieldsNameEnum } from '../../../enum/formField/flow'; | 2 | import { RuleChainFieldsEnum, RuleChainFieldsNameEnum } from '../../../enum/formField/flow'; |
| 3 | -import { getRuleChains } from '/@/api/ruleDesigner'; | 3 | +import { getRuleChainDetail, getRuleChains } from '/@/api/ruleDesigner'; |
| 4 | import { FormSchema } from '/@/components/Form'; | 4 | import { FormSchema } from '/@/components/Form'; |
| 5 | import { useI18n } from '/@/hooks/web/useI18n'; | 5 | import { useI18n } from '/@/hooks/web/useI18n'; |
| 6 | 6 | ||
| @@ -31,13 +31,20 @@ export const getFormSchemas = (route: RouteLocationNormalizedLoaded): FormSchema | @@ -31,13 +31,20 @@ export const getFormSchemas = (route: RouteLocationNormalizedLoaded): FormSchema | ||
| 31 | return { | 31 | return { |
| 32 | placeholder: '请选择规则链', | 32 | placeholder: '请选择规则链', |
| 33 | showSearch: true, | 33 | showSearch: true, |
| 34 | - params: { | ||
| 35 | - pageSize: 50, | ||
| 36 | - page: 0, | ||
| 37 | - type: 'CORE', | 34 | + params: (textSearch: string) => { |
| 35 | + return { | ||
| 36 | + pageSize: 10, | ||
| 37 | + page: 0, | ||
| 38 | + type: 'CORE', | ||
| 39 | + textSearch, | ||
| 40 | + }; | ||
| 38 | }, | 41 | }, |
| 39 | api: (params: Recordable) => fetch(params, ruleChainId), | 42 | api: (params: Recordable) => fetch(params, ruleChainId), |
| 40 | - searchApi: (params: Recordable) => fetch(params, ruleChainId), | 43 | + queryApi: async (id: string) => { |
| 44 | + if (!id) return; | ||
| 45 | + const data = await getRuleChainDetail(id); | ||
| 46 | + return { label: data.name, value: data.id.id }; | ||
| 47 | + }, | ||
| 41 | getPopupContainer: () => document.body, | 48 | getPopupContainer: () => document.body, |
| 42 | }; | 49 | }; |
| 43 | }, | 50 | }, |