Commit d7c4e079fbc08bfd26069ba6398213f28a5df646
Merge branch 'fix/DEFECT-2291' into 'main_dev'
fix: 看板组件图标能使用自定义图标 See merge request yunteng/thingskit-front!1432
Showing
18 changed files
with
788 additions
and
102 deletions
| @@ -71,9 +71,12 @@ | @@ -71,9 +71,12 @@ | ||
| 71 | return false; | 71 | return false; |
| 72 | } | 72 | } |
| 73 | } | 73 | } |
| 74 | - | ||
| 75 | if (file.size > props.maxSize) { | 74 | if (file.size > props.maxSize) { |
| 76 | - createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024 / 1024)}mb`); | 75 | + createMessage.warning( |
| 76 | + `文件大小超过${Math.floor( | ||
| 77 | + props.maxSize > 1024 * 1024 ? props.maxSize / 1024 / 1024 : props.maxSize / 1024 | ||
| 78 | + )}${props.maxSize > 1024 * 1024 * 1 ? 'mb' : 'kb'}` | ||
| 79 | + ); | ||
| 77 | return false; | 80 | return false; |
| 78 | } | 81 | } |
| 79 | handleUpload(file); | 82 | handleUpload(file); |
| @@ -30,7 +30,7 @@ | @@ -30,7 +30,7 @@ | ||
| 30 | auth: 'api:yt:sceneLinkage:update', | 30 | auth: 'api:yt:sceneLinkage:update', |
| 31 | icon: 'clarity:note-edit-line', | 31 | icon: 'clarity:note-edit-line', |
| 32 | onClick: handleEdit.bind(null, record), | 32 | onClick: handleEdit.bind(null, record), |
| 33 | - ifShow: record.status !== 1 && record.isEdge !== 1, | 33 | + ifShow: record.status !== 1, |
| 34 | }, | 34 | }, |
| 35 | ]" | 35 | ]" |
| 36 | :drop-down-actions="[ | 36 | :drop-down-actions="[ |
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | auth: 'api:yt:sceneLinkage:delete', | 39 | auth: 'api:yt:sceneLinkage:delete', |
| 40 | icon: 'ant-design:delete-outlined', | 40 | icon: 'ant-design:delete-outlined', |
| 41 | color: 'error', | 41 | color: 'error', |
| 42 | - ifShow: record.status !== 1 && record.isEdge !== 1, | 42 | + ifShow: record.status !== 1, |
| 43 | popConfirm: { | 43 | popConfirm: { |
| 44 | title: '是否确认删除', | 44 | title: '是否确认删除', |
| 45 | confirm: handleDeleteOrBatchDelete.bind(null, record), | 45 | confirm: handleDeleteOrBatchDelete.bind(null, record), |
| @@ -56,7 +56,6 @@ | @@ -56,7 +56,6 @@ | ||
| 56 | :loading="record.pendingStatus" | 56 | :loading="record.pendingStatus" |
| 57 | checkedChildren="启用" | 57 | checkedChildren="启用" |
| 58 | unCheckedChildren="禁用" | 58 | unCheckedChildren="禁用" |
| 59 | - :disabled="record.isEdge === 1" | ||
| 60 | @change="(checked:boolean)=>statusChange(checked,record)" | 59 | @change="(checked:boolean)=>statusChange(checked,record)" |
| 61 | /> | 60 | /> |
| 62 | </Authority> | 61 | </Authority> |
| @@ -56,7 +56,7 @@ | @@ -56,7 +56,7 @@ | ||
| 56 | </script> | 56 | </script> |
| 57 | 57 | ||
| 58 | <template> | 58 | <template> |
| 59 | - <BasicModal @register="register" title="组件设置" @ok="handleOk"> | 59 | + <BasicModal @register="register" title="组件设置" @ok="handleOk" :width="700"> |
| 60 | <!-- --> | 60 | <!-- --> |
| 61 | <component ref="settingFormEl" :is="getSettingComponent" /> | 61 | <component ref="settingFormEl" :is="getSettingComponent" /> |
| 62 | </BasicModal> | 62 | </BasicModal> |
| @@ -29,6 +29,8 @@ | @@ -29,6 +29,8 @@ | ||
| 29 | import { MessageAlert } from './components/MessageAlert'; | 29 | import { MessageAlert } from './components/MessageAlert'; |
| 30 | import { createSelectWidgetKeysContext, createSelectWidgetModeContext } from './useContext'; | 30 | import { createSelectWidgetKeysContext, createSelectWidgetModeContext } from './useContext'; |
| 31 | import { useGetCategoryByComponentKey } from '../packages/hook/useGetCategoryByComponentKey'; | 31 | import { useGetCategoryByComponentKey } from '../packages/hook/useGetCategoryByComponentKey'; |
| 32 | + import { deleteFilePath } from '/@/api/oss/ossFileUploader'; | ||
| 33 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 32 | 34 | ||
| 33 | const props = defineProps<{ | 35 | const props = defineProps<{ |
| 34 | layout: Layout[]; | 36 | layout: Layout[]; |
| @@ -227,6 +229,20 @@ | @@ -227,6 +229,20 @@ | ||
| 227 | return `${category} / ${componentConfig.title}`; | 229 | return `${category} / ${componentConfig.title}`; |
| 228 | }); | 230 | }); |
| 229 | 231 | ||
| 232 | + const countElementOccurrences = (arr) => { | ||
| 233 | + const countMap = {}; | ||
| 234 | + | ||
| 235 | + arr.forEach((element) => { | ||
| 236 | + if (countMap[element]) { | ||
| 237 | + countMap[element]++; | ||
| 238 | + } else { | ||
| 239 | + countMap[element] = 1; | ||
| 240 | + } | ||
| 241 | + }); | ||
| 242 | + | ||
| 243 | + return countMap; | ||
| 244 | + }; | ||
| 245 | + | ||
| 230 | const handleSubmit = async () => { | 246 | const handleSubmit = async () => { |
| 231 | const validateResult = await validate(); | 247 | const validateResult = await validate(); |
| 232 | if (validateResult && !validateResult.flag) { | 248 | if (validateResult && !validateResult.flag) { |
| @@ -241,6 +257,59 @@ | @@ -241,6 +257,59 @@ | ||
| 241 | } | 257 | } |
| 242 | } | 258 | } |
| 243 | const value = getFormValues(); | 259 | const value = getFormValues(); |
| 260 | + const { record } = value || {}; | ||
| 261 | + try { | ||
| 262 | + const currentRecordIconUrl = ref<any>([]); | ||
| 263 | + // 判断当前自定义组件表单以前的自定义图片呢url | ||
| 264 | + currentRecord.value?.dataSource.forEach((item) => { | ||
| 265 | + if (item.componentInfo?.customIcon) { | ||
| 266 | + item.componentInfo?.customIcon.forEach((icon: FileItem) => { | ||
| 267 | + currentRecordIconUrl.value.push(icon.url); | ||
| 268 | + }); | ||
| 269 | + } | ||
| 270 | + }); | ||
| 271 | + // 取当前修改过后的自定义图片url | ||
| 272 | + const dataSourceUrl = record.dataSource?.map( | ||
| 273 | + (item) => item.componentInfo.customIcon?.[0].url | ||
| 274 | + ); | ||
| 275 | + | ||
| 276 | + // 当前自定义组件取出要进行删除的图标url | ||
| 277 | + const dataSourceDeleteUrl = unref(currentRecordIconUrl).filter( | ||
| 278 | + (item) => !dataSourceUrl?.includes(item) | ||
| 279 | + ); | ||
| 280 | + | ||
| 281 | + //查询外部所有组件的自定义图标的url | ||
| 282 | + const oldDataSource = props.layout; | ||
| 283 | + const customIconUrls = ref<any>([]); | ||
| 284 | + oldDataSource?.forEach((item: any) => { | ||
| 285 | + item.dataSource?.forEach((dataSource) => { | ||
| 286 | + if (dataSource.componentInfo?.customIcon) { | ||
| 287 | + dataSource.componentInfo?.customIcon.forEach((icon: FileItem) => { | ||
| 288 | + customIconUrls.value.push(icon.url); | ||
| 289 | + }); | ||
| 290 | + } | ||
| 291 | + }); | ||
| 292 | + }); | ||
| 293 | + // const dataSourceDeleteUrl = record.dataSource?.map((item) => item.componentInfo.deleteUrl); | ||
| 294 | + | ||
| 295 | + if (unref(customIconUrls) && unref(customIconUrls).length && dataSourceDeleteUrl?.length) { | ||
| 296 | + // 判断外部所有组件是否有dataSourceDeleteUrl使用中的url | ||
| 297 | + const deletePromise = unref(customIconUrls)?.filter((item) => | ||
| 298 | + dataSourceDeleteUrl?.includes(item) | ||
| 299 | + ); | ||
| 300 | + const deleteUrlInfo = countElementOccurrences(deletePromise); | ||
| 301 | + const deleteUrl = deletePromise?.filter((item) => deleteUrlInfo?.[item] == 1); | ||
| 302 | + Promise.all( | ||
| 303 | + deleteUrl.map((item) => { | ||
| 304 | + deleteFilePath(item); | ||
| 305 | + }) | ||
| 306 | + ); | ||
| 307 | + } | ||
| 308 | + } catch (err) { | ||
| 309 | + // eslint-disable-next-line no-console | ||
| 310 | + console.log(err); | ||
| 311 | + } | ||
| 312 | + | ||
| 244 | try { | 313 | try { |
| 245 | loading.value = true; | 314 | loading.value = true; |
| 246 | unref(currentMode) === DataActionModeEnum.UPDATE | 315 | unref(currentMode) === DataActionModeEnum.UPDATE |
| @@ -3,15 +3,68 @@ | @@ -3,15 +3,68 @@ | ||
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 7 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 8 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 6 | 9 | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 11 | schemas: [ |
| 9 | { | 12 | { |
| 13 | + field: ComponentConfigFieldEnum.FONT_SIZE, | ||
| 14 | + label: '文本字体大小', | ||
| 15 | + component: 'InputNumber', | ||
| 16 | + defaultValue: 14, | ||
| 17 | + componentProps: { | ||
| 18 | + min: 0, | ||
| 19 | + max: 100, | ||
| 20 | + formatter: (e) => { | ||
| 21 | + const value = e?.toString().replace(/^0/g, ''); | ||
| 22 | + if (value) { | ||
| 23 | + return value.replace(/^0/g, ''); | ||
| 24 | + } else { | ||
| 25 | + return 0; | ||
| 26 | + } | ||
| 27 | + }, | ||
| 28 | + }, | ||
| 29 | + }, | ||
| 30 | + { | ||
| 31 | + field: ComponentConfigFieldEnum.PASS_WORD, | ||
| 32 | + label: '操作密码', | ||
| 33 | + component: 'InputPassword', | ||
| 34 | + defaultValue: '', | ||
| 35 | + }, | ||
| 36 | + { | ||
| 37 | + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 38 | + label: '显示设备名称', | ||
| 39 | + component: 'Checkbox', | ||
| 40 | + defaultValue: option.showDeviceName, | ||
| 41 | + }, | ||
| 42 | + { | ||
| 43 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, | ||
| 44 | + label: '图标类型', | ||
| 45 | + component: 'RadioGroup', | ||
| 46 | + defaultValue: 'default', | ||
| 47 | + componentProps: ({ formModel }) => { | ||
| 48 | + return { | ||
| 49 | + options: [ | ||
| 50 | + { label: '系统默认', value: 'default' }, | ||
| 51 | + { label: '自定义', value: 'custom' }, | ||
| 52 | + ], | ||
| 53 | + onChange() { | ||
| 54 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 55 | + }, | ||
| 56 | + }; | ||
| 57 | + }, | ||
| 58 | + }, | ||
| 59 | + { | ||
| 10 | field: ComponentConfigFieldEnum.ICON_COLOR, | 60 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 11 | label: '图标颜色', | 61 | label: '图标颜色', |
| 12 | component: 'ColorPicker', | 62 | component: 'ColorPicker', |
| 13 | changeEvent: 'update:value', | 63 | changeEvent: 'update:value', |
| 14 | defaultValue: option.iconColor, | 64 | defaultValue: option.iconColor, |
| 65 | + ifShow: ({ model }) => { | ||
| 66 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 67 | + }, | ||
| 15 | }, | 68 | }, |
| 16 | { | 69 | { |
| 17 | field: ComponentConfigFieldEnum.ICON, | 70 | field: ComponentConfigFieldEnum.ICON, |
| @@ -19,6 +72,9 @@ | @@ -19,6 +72,9 @@ | ||
| 19 | component: 'IconDrawer', | 72 | component: 'IconDrawer', |
| 20 | changeEvent: 'update:value', | 73 | changeEvent: 'update:value', |
| 21 | defaultValue: option.icon, | 74 | defaultValue: option.icon, |
| 75 | + ifShow: ({ model }) => { | ||
| 76 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 77 | + }, | ||
| 22 | componentProps({ formModel }) { | 78 | componentProps({ formModel }) { |
| 23 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | 79 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 24 | return { | 80 | return { |
| @@ -27,34 +83,51 @@ | @@ -27,34 +83,51 @@ | ||
| 27 | }, | 83 | }, |
| 28 | }, | 84 | }, |
| 29 | { | 85 | { |
| 30 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 31 | - label: '显示设备名称', | ||
| 32 | - component: 'Checkbox', | ||
| 33 | - defaultValue: option.showDeviceName, | ||
| 34 | - }, | ||
| 35 | - { | ||
| 36 | - field: ComponentConfigFieldEnum.FONT_SIZE, | ||
| 37 | - label: '文本字体大小', | ||
| 38 | - component: 'InputNumber', | ||
| 39 | - defaultValue: 14, | ||
| 40 | - componentProps: { | ||
| 41 | - min: 0, | ||
| 42 | - max: 100, | ||
| 43 | - formatter: (e) => { | ||
| 44 | - const value = e?.toString().replace(/^0/g, ''); | ||
| 45 | - if (value) { | ||
| 46 | - return value.replace(/^0/g, ''); | ||
| 47 | - } else { | ||
| 48 | - return 0; | ||
| 49 | - } | ||
| 50 | - }, | 86 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, |
| 87 | + label: '图标', | ||
| 88 | + component: 'ApiUpload', | ||
| 89 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 90 | + changeEvent: 'update:fileList', | ||
| 91 | + valueField: 'fileList', | ||
| 92 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 93 | + componentProps: ({ formModel }) => { | ||
| 94 | + return { | ||
| 95 | + listType: 'picture-card', | ||
| 96 | + maxFileLimit: 1, | ||
| 97 | + maxSize: 50 * 1024, | ||
| 98 | + accept: '.svg', | ||
| 99 | + api: async (file: File) => { | ||
| 100 | + try { | ||
| 101 | + const formData = new FormData(); | ||
| 102 | + const { name } = file; | ||
| 103 | + formData.set('file', file); | ||
| 104 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 105 | + return { | ||
| 106 | + uid: fileStaticUri, | ||
| 107 | + name: name || fileName, | ||
| 108 | + url: fileStaticUri, | ||
| 109 | + } as FileItem; | ||
| 110 | + } catch (error) { | ||
| 111 | + return {}; | ||
| 112 | + } | ||
| 113 | + }, | ||
| 114 | + // showUploadList: true, | ||
| 115 | + onDownload() {}, | ||
| 116 | + onPreview: (fileList: FileItem) => { | ||
| 117 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 118 | + }, | ||
| 119 | + | ||
| 120 | + onDelete(url: string) { | ||
| 121 | + formModel.deleteUrl = url!; | ||
| 122 | + }, | ||
| 123 | + }; | ||
| 51 | }, | 124 | }, |
| 52 | }, | 125 | }, |
| 53 | { | 126 | { |
| 54 | - field: ComponentConfigFieldEnum.PASS_WORD, | ||
| 55 | - label: '操作密码', | ||
| 56 | - component: 'InputPassword', | ||
| 57 | - defaultValue: '', | 127 | + field: 'deleteUrl', |
| 128 | + label: '', | ||
| 129 | + component: 'Input', | ||
| 130 | + show: false, | ||
| 58 | }, | 131 | }, |
| 59 | ], | 132 | ], |
| 60 | showActionButtonGroup: false, | 133 | showActionButtonGroup: false, |
| @@ -31,7 +31,7 @@ | @@ -31,7 +31,7 @@ | ||
| 31 | fontSize: persetFontSize, | 31 | fontSize: persetFontSize, |
| 32 | password: persetPassword, | 32 | password: persetPassword, |
| 33 | } = persetOption || {}; | 33 | } = persetOption || {}; |
| 34 | - const { icon, iconColor, fontSize, password } = componentInfo || {}; | 34 | + const { icon, iconColor, fontSize, password, customIcon, defaultCustom } = componentInfo || {}; |
| 35 | 35 | ||
| 36 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | 36 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); |
| 37 | return { | 37 | return { |
| @@ -41,6 +41,8 @@ | @@ -41,6 +41,8 @@ | ||
| 41 | fontSize: fontSize || persetFontSize || 14, | 41 | fontSize: fontSize || persetFontSize || 14, |
| 42 | password: password || persetPassword, | 42 | password: password || persetPassword, |
| 43 | commandType, | 43 | commandType, |
| 44 | + defaultCustom: defaultCustom || 'default', | ||
| 45 | + customIcon: customIcon || [], | ||
| 44 | }; | 46 | }; |
| 45 | }); | 47 | }); |
| 46 | 48 | ||
| @@ -81,10 +83,17 @@ | @@ -81,10 +83,17 @@ | ||
| 81 | <main class="w-full h-full flex justify-around items-center" :style="getScale"> | 83 | <main class="w-full h-full flex justify-around items-center" :style="getScale"> |
| 82 | <div class="flex flex-col justify-center items-center"> | 84 | <div class="flex flex-col justify-center items-center"> |
| 83 | <SvgIcon | 85 | <SvgIcon |
| 84 | - :name="getDesign.icon" | 86 | + v-if="getDesign.defaultCustom !== 'custom'" |
| 87 | + :name="getDesign.icon!" | ||
| 85 | prefix="iconfont" | 88 | prefix="iconfont" |
| 86 | - :style="{ color: getDesign.iconColor }" | ||
| 87 | :size="getRatio ? getRatio * 60 : 60" | 89 | :size="getRatio ? getRatio * 60 : 60" |
| 90 | + :style="{ color: getDesign.iconColor }" | ||
| 91 | + /> | ||
| 92 | + <img | ||
| 93 | + v-else | ||
| 94 | + :src="getDesign.customIcon[0]?.url" | ||
| 95 | + :style="{ width: getRatio ? getRatio * 60 + 'px' : '60px' }" | ||
| 96 | + :alt="getDesign.customIcon[0]?.name" | ||
| 88 | /> | 97 | /> |
| 89 | <span | 98 | <span |
| 90 | class="mt-3 truncate text-gray-500 text-center" | 99 | class="mt-3 truncate text-gray-500 text-center" |
| @@ -3,30 +3,13 @@ | @@ -3,30 +3,13 @@ | ||
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 7 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 8 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 6 | 9 | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 11 | schemas: [ |
| 9 | { | 12 | { |
| 10 | - field: ComponentConfigFieldEnum.ICON_COLOR, | ||
| 11 | - label: '图标颜色', | ||
| 12 | - component: 'ColorPicker', | ||
| 13 | - changeEvent: 'update:value', | ||
| 14 | - defaultValue: option.iconColor, | ||
| 15 | - }, | ||
| 16 | - { | ||
| 17 | - field: ComponentConfigFieldEnum.ICON, | ||
| 18 | - label: '图标', | ||
| 19 | - component: 'IconDrawer', | ||
| 20 | - changeEvent: 'update:value', | ||
| 21 | - defaultValue: option.icon, | ||
| 22 | - componentProps({ formModel }) { | ||
| 23 | - const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | ||
| 24 | - return { | ||
| 25 | - color, | ||
| 26 | - }; | ||
| 27 | - }, | ||
| 28 | - }, | ||
| 29 | - { | ||
| 30 | field: ComponentConfigFieldEnum.FONT_SIZE, | 13 | field: ComponentConfigFieldEnum.FONT_SIZE, |
| 31 | label: '文本字体大小', | 14 | label: '文本字体大小', |
| 32 | component: 'InputNumber', | 15 | component: 'InputNumber', |
| @@ -45,16 +28,106 @@ | @@ -45,16 +28,106 @@ | ||
| 45 | }, | 28 | }, |
| 46 | }, | 29 | }, |
| 47 | { | 30 | { |
| 31 | + field: ComponentConfigFieldEnum.PASS_WORD, | ||
| 32 | + label: '操作密码', | ||
| 33 | + component: 'InputPassword', | ||
| 34 | + defaultValue: '', | ||
| 35 | + }, | ||
| 36 | + { | ||
| 48 | field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | 37 | field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, |
| 49 | label: '显示设备名称', | 38 | label: '显示设备名称', |
| 50 | component: 'Checkbox', | 39 | component: 'Checkbox', |
| 51 | defaultValue: option.showDeviceName, | 40 | defaultValue: option.showDeviceName, |
| 52 | }, | 41 | }, |
| 53 | { | 42 | { |
| 54 | - field: ComponentConfigFieldEnum.PASS_WORD, | ||
| 55 | - label: '操作密码', | ||
| 56 | - component: 'InputPassword', | ||
| 57 | - defaultValue: '', | 43 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, |
| 44 | + label: '图标类型', | ||
| 45 | + component: 'RadioGroup', | ||
| 46 | + defaultValue: 'default', | ||
| 47 | + componentProps: ({ formModel }) => { | ||
| 48 | + return { | ||
| 49 | + options: [ | ||
| 50 | + { label: '系统默认', value: 'default' }, | ||
| 51 | + { label: '自定义', value: 'custom' }, | ||
| 52 | + ], | ||
| 53 | + onChange() { | ||
| 54 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 55 | + }, | ||
| 56 | + }; | ||
| 57 | + }, | ||
| 58 | + }, | ||
| 59 | + { | ||
| 60 | + field: ComponentConfigFieldEnum.ICON_COLOR, | ||
| 61 | + label: '图标颜色', | ||
| 62 | + component: 'ColorPicker', | ||
| 63 | + changeEvent: 'update:value', | ||
| 64 | + defaultValue: option.iconColor, | ||
| 65 | + ifShow: ({ model }) => { | ||
| 66 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 67 | + }, | ||
| 68 | + }, | ||
| 69 | + { | ||
| 70 | + field: ComponentConfigFieldEnum.ICON, | ||
| 71 | + label: '图标', | ||
| 72 | + component: 'IconDrawer', | ||
| 73 | + changeEvent: 'update:value', | ||
| 74 | + defaultValue: option.icon, | ||
| 75 | + ifShow: ({ model }) => { | ||
| 76 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 77 | + }, | ||
| 78 | + componentProps({ formModel }) { | ||
| 79 | + const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | ||
| 80 | + return { | ||
| 81 | + color, | ||
| 82 | + }; | ||
| 83 | + }, | ||
| 84 | + }, | ||
| 85 | + { | ||
| 86 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, | ||
| 87 | + label: '图标', | ||
| 88 | + component: 'ApiUpload', | ||
| 89 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 90 | + changeEvent: 'update:fileList', | ||
| 91 | + valueField: 'fileList', | ||
| 92 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 93 | + componentProps: ({ formModel }) => { | ||
| 94 | + return { | ||
| 95 | + listType: 'picture-card', | ||
| 96 | + maxSize: 50 * 1024, | ||
| 97 | + maxFileLimit: 1, | ||
| 98 | + accept: '.svg', | ||
| 99 | + api: async (file: File) => { | ||
| 100 | + try { | ||
| 101 | + const formData = new FormData(); | ||
| 102 | + const { name } = file; | ||
| 103 | + formData.set('file', file); | ||
| 104 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 105 | + return { | ||
| 106 | + uid: fileStaticUri, | ||
| 107 | + name: name || fileName, | ||
| 108 | + url: fileStaticUri, | ||
| 109 | + } as FileItem; | ||
| 110 | + } catch (error) { | ||
| 111 | + return {}; | ||
| 112 | + } | ||
| 113 | + }, | ||
| 114 | + // showUploadList: true, | ||
| 115 | + onDownload() {}, | ||
| 116 | + onPreview: (fileList: FileItem) => { | ||
| 117 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 118 | + }, | ||
| 119 | + | ||
| 120 | + onDelete(url: string) { | ||
| 121 | + formModel.deleteUrl = url!; | ||
| 122 | + }, | ||
| 123 | + }; | ||
| 124 | + }, | ||
| 125 | + }, | ||
| 126 | + { | ||
| 127 | + field: 'deleteUrl', | ||
| 128 | + label: '', | ||
| 129 | + component: 'Input', | ||
| 130 | + show: false, | ||
| 58 | }, | 131 | }, |
| 59 | ], | 132 | ], |
| 60 | showActionButtonGroup: false, | 133 | showActionButtonGroup: false, |
| @@ -38,8 +38,17 @@ | @@ -38,8 +38,17 @@ | ||
| 38 | } = persetOption || {}; | 38 | } = persetOption || {}; |
| 39 | return { | 39 | return { |
| 40 | dataSource: dataSource.map((item) => { | 40 | dataSource: dataSource.map((item) => { |
| 41 | - const { fontColor, icon, iconColor, unit, showDeviceName, password, fontSize } = | ||
| 42 | - item.componentInfo; | 41 | + const { |
| 42 | + fontColor, | ||
| 43 | + icon, | ||
| 44 | + iconColor, | ||
| 45 | + unit, | ||
| 46 | + showDeviceName, | ||
| 47 | + password, | ||
| 48 | + fontSize, | ||
| 49 | + customIcon, | ||
| 50 | + defaultCustom, | ||
| 51 | + } = item.componentInfo; | ||
| 43 | const { | 52 | const { |
| 44 | attribute, | 53 | attribute, |
| 45 | attributeRename, | 54 | attributeRename, |
| @@ -76,6 +85,8 @@ | @@ -76,6 +85,8 @@ | ||
| 76 | closeCommand, | 85 | closeCommand, |
| 77 | openService, | 86 | openService, |
| 78 | closeService, | 87 | closeService, |
| 88 | + defaultCustom: defaultCustom || 'default', | ||
| 89 | + customIcon: customIcon || [], | ||
| 79 | } as SwitchItemType; | 90 | } as SwitchItemType; |
| 80 | }), | 91 | }), |
| 81 | }; | 92 | }; |
| @@ -128,11 +139,24 @@ | @@ -128,11 +139,24 @@ | ||
| 128 | :key="item.id" | 139 | :key="item.id" |
| 129 | class="flex justify-between items-center w-full px-4" | 140 | class="flex justify-between items-center w-full px-4" |
| 130 | > | 141 | > |
| 131 | - <SvgIcon | 142 | + <!-- <SvgIcon |
| 132 | :name="item.icon!" | 143 | :name="item.icon!" |
| 133 | prefix="iconfont" | 144 | prefix="iconfont" |
| 134 | :size="getRatio ? 30 * getRatio : 30" | 145 | :size="getRatio ? 30 * getRatio : 30" |
| 135 | :style="{ color: item.iconColor }" | 146 | :style="{ color: item.iconColor }" |
| 147 | + /> --> | ||
| 148 | + <SvgIcon | ||
| 149 | + v-if="item.defaultCustom !== 'custom'" | ||
| 150 | + :name="item.icon!" | ||
| 151 | + prefix="iconfont" | ||
| 152 | + :size="getRatio ? getRatio * 30 : 30" | ||
| 153 | + :style="{ color: item.iconColor }" | ||
| 154 | + /> | ||
| 155 | + <img | ||
| 156 | + v-else | ||
| 157 | + :src="item.customIcon[0]?.url" | ||
| 158 | + :style="{ width: getRatio ? getRatio * 30 + 'px' : '30px' }" | ||
| 159 | + :alt="item.customIcon[0]?.name" | ||
| 136 | /> | 160 | /> |
| 137 | <div | 161 | <div |
| 138 | class="text-gray-500 truncate mx-2" | 162 | class="text-gray-500 truncate mx-2" |
| @@ -3,15 +3,68 @@ | @@ -3,15 +3,68 @@ | ||
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 7 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 8 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 6 | 9 | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 11 | schemas: [ |
| 9 | { | 12 | { |
| 13 | + field: ComponentConfigFieldEnum.FONT_SIZE, | ||
| 14 | + label: '文本字体大小', | ||
| 15 | + component: 'InputNumber', | ||
| 16 | + defaultValue: 14, | ||
| 17 | + componentProps: { | ||
| 18 | + min: 0, | ||
| 19 | + max: 100, | ||
| 20 | + formatter: (e) => { | ||
| 21 | + const value = e?.toString().replace(/^0/g, ''); | ||
| 22 | + if (value) { | ||
| 23 | + return value.replace(/^0/g, ''); | ||
| 24 | + } else { | ||
| 25 | + return 0; | ||
| 26 | + } | ||
| 27 | + }, | ||
| 28 | + }, | ||
| 29 | + }, | ||
| 30 | + { | ||
| 31 | + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 32 | + label: '显示设备名称', | ||
| 33 | + component: 'Checkbox', | ||
| 34 | + defaultValue: option.showDeviceName, | ||
| 35 | + }, | ||
| 36 | + { | ||
| 37 | + field: ComponentConfigFieldEnum.SHOW_TIME, | ||
| 38 | + label: '显示时间', | ||
| 39 | + component: 'Checkbox', | ||
| 40 | + defaultValue: option.showTime, | ||
| 41 | + }, | ||
| 42 | + { | ||
| 43 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, | ||
| 44 | + label: '图标类型', | ||
| 45 | + component: 'RadioGroup', | ||
| 46 | + defaultValue: 'default', | ||
| 47 | + componentProps: ({ formModel }) => { | ||
| 48 | + return { | ||
| 49 | + options: [ | ||
| 50 | + { label: '系统默认', value: 'default' }, | ||
| 51 | + { label: '自定义', value: 'custom' }, | ||
| 52 | + ], | ||
| 53 | + onChange() { | ||
| 54 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 55 | + }, | ||
| 56 | + }; | ||
| 57 | + }, | ||
| 58 | + }, | ||
| 59 | + { | ||
| 10 | field: ComponentConfigFieldEnum.ICON, | 60 | field: ComponentConfigFieldEnum.ICON, |
| 11 | label: '开启状态图标', | 61 | label: '开启状态图标', |
| 12 | component: 'IconDrawer', | 62 | component: 'IconDrawer', |
| 13 | changeEvent: 'update:value', | 63 | changeEvent: 'update:value', |
| 14 | defaultValue: option.icon, | 64 | defaultValue: option.icon, |
| 65 | + ifShow: ({ model }) => { | ||
| 66 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 67 | + }, | ||
| 15 | componentProps({ formModel }) { | 68 | componentProps({ formModel }) { |
| 16 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | 69 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 17 | return { | 70 | return { |
| @@ -24,6 +77,9 @@ | @@ -24,6 +77,9 @@ | ||
| 24 | label: '开启图标颜色', | 77 | label: '开启图标颜色', |
| 25 | component: 'ColorPicker', | 78 | component: 'ColorPicker', |
| 26 | changeEvent: 'update:value', | 79 | changeEvent: 'update:value', |
| 80 | + ifShow: ({ model }) => { | ||
| 81 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 82 | + }, | ||
| 27 | defaultValue: option.iconColor, | 83 | defaultValue: option.iconColor, |
| 28 | }, | 84 | }, |
| 29 | { | 85 | { |
| @@ -32,6 +88,9 @@ | @@ -32,6 +88,9 @@ | ||
| 32 | component: 'IconDrawer', | 88 | component: 'IconDrawer', |
| 33 | changeEvent: 'update:value', | 89 | changeEvent: 'update:value', |
| 34 | defaultValue: option.iconClose, | 90 | defaultValue: option.iconClose, |
| 91 | + ifShow: ({ model }) => { | ||
| 92 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 93 | + }, | ||
| 35 | componentProps({ formModel }) { | 94 | componentProps({ formModel }) { |
| 36 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR_CLOSE]; | 95 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR_CLOSE]; |
| 37 | return { | 96 | return { |
| @@ -44,37 +103,84 @@ | @@ -44,37 +103,84 @@ | ||
| 44 | label: '关闭图标颜色', | 103 | label: '关闭图标颜色', |
| 45 | component: 'ColorPicker', | 104 | component: 'ColorPicker', |
| 46 | changeEvent: 'update:value', | 105 | changeEvent: 'update:value', |
| 106 | + ifShow: ({ model }) => { | ||
| 107 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 108 | + }, | ||
| 47 | defaultValue: option.iconColorClose, | 109 | defaultValue: option.iconColorClose, |
| 48 | }, | 110 | }, |
| 49 | { | 111 | { |
| 50 | - field: ComponentConfigFieldEnum.FONT_SIZE, | ||
| 51 | - label: '文本字体大小', | ||
| 52 | - component: 'InputNumber', | ||
| 53 | - defaultValue: 14, | ||
| 54 | - componentProps: { | ||
| 55 | - min: 0, | ||
| 56 | - max: 100, | ||
| 57 | - formatter: (e) => { | ||
| 58 | - const value = e?.toString().replace(/^0/g, ''); | ||
| 59 | - if (value) { | ||
| 60 | - return value.replace(/^0/g, ''); | ||
| 61 | - } else { | ||
| 62 | - return 0; | ||
| 63 | - } | ||
| 64 | - }, | 112 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, |
| 113 | + label: '开启状态图标', | ||
| 114 | + component: 'ApiUpload', | ||
| 115 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 116 | + changeEvent: 'update:fileList', | ||
| 117 | + valueField: 'fileList', | ||
| 118 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 119 | + componentProps: ({}) => { | ||
| 120 | + return { | ||
| 121 | + listType: 'picture-card', | ||
| 122 | + maxSize: 50 * 1024, | ||
| 123 | + maxFileLimit: 1, | ||
| 124 | + accept: '.svg', | ||
| 125 | + api: async (file: File) => { | ||
| 126 | + try { | ||
| 127 | + const formData = new FormData(); | ||
| 128 | + const { name } = file; | ||
| 129 | + formData.set('file', file); | ||
| 130 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 131 | + return { | ||
| 132 | + uid: fileStaticUri, | ||
| 133 | + name: name || fileName, | ||
| 134 | + url: fileStaticUri, | ||
| 135 | + } as FileItem; | ||
| 136 | + } catch (error) { | ||
| 137 | + return {}; | ||
| 138 | + } | ||
| 139 | + }, | ||
| 140 | + // showUploadList: true, | ||
| 141 | + onDownload() {}, | ||
| 142 | + onPreview: (fileList: FileItem) => { | ||
| 143 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 144 | + }, | ||
| 145 | + }; | ||
| 65 | }, | 146 | }, |
| 66 | }, | 147 | }, |
| 67 | { | 148 | { |
| 68 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 69 | - label: '显示设备名称', | ||
| 70 | - component: 'Checkbox', | ||
| 71 | - defaultValue: option.showDeviceName, | ||
| 72 | - }, | ||
| 73 | - { | ||
| 74 | - field: ComponentConfigFieldEnum.SHOW_TIME, | ||
| 75 | - label: '显示时间', | ||
| 76 | - component: 'Checkbox', | ||
| 77 | - defaultValue: option.showTime, | 149 | + field: ComponentConfigFieldEnum.CUSTOM_ICON_CLOSE, |
| 150 | + label: '关闭状态图标', | ||
| 151 | + component: 'ApiUpload', | ||
| 152 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 153 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 154 | + changeEvent: 'update:fileList', | ||
| 155 | + valueField: 'fileList', | ||
| 156 | + componentProps: ({}) => { | ||
| 157 | + return { | ||
| 158 | + listType: 'picture-card', | ||
| 159 | + maxSize: 0 * 1024, | ||
| 160 | + maxFileLimit: 1, | ||
| 161 | + accept: '.svg', | ||
| 162 | + api: async (file: File) => { | ||
| 163 | + try { | ||
| 164 | + const formData = new FormData(); | ||
| 165 | + const { name } = file; | ||
| 166 | + formData.set('file', file); | ||
| 167 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 168 | + return { | ||
| 169 | + uid: fileStaticUri, | ||
| 170 | + name: name || fileName, | ||
| 171 | + url: fileStaticUri, | ||
| 172 | + } as FileItem; | ||
| 173 | + } catch (error) { | ||
| 174 | + return {}; | ||
| 175 | + } | ||
| 176 | + }, | ||
| 177 | + // showUploadList: true, | ||
| 178 | + onDownload() {}, | ||
| 179 | + onPreview: (fileList: FileItem) => { | ||
| 180 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 181 | + }, | ||
| 182 | + }; | ||
| 183 | + }, | ||
| 78 | }, | 184 | }, |
| 79 | ], | 185 | ], |
| 80 | showActionButtonGroup: false, | 186 | showActionButtonGroup: false, |
| @@ -32,8 +32,19 @@ | @@ -32,8 +32,19 @@ | ||
| 32 | 32 | ||
| 33 | const { componentInfo, attributeName, attributeRename } = option; | 33 | const { componentInfo, attributeName, attributeRename } = option; |
| 34 | 34 | ||
| 35 | - const { icon, iconColor, fontColor, unit, iconClose, iconColorClose, showTime, fontSize } = | ||
| 36 | - componentInfo || {}; | 35 | + const { |
| 36 | + icon, | ||
| 37 | + iconColor, | ||
| 38 | + fontColor, | ||
| 39 | + unit, | ||
| 40 | + iconClose, | ||
| 41 | + iconColorClose, | ||
| 42 | + showTime, | ||
| 43 | + fontSize, | ||
| 44 | + customIcon, | ||
| 45 | + customIconClose, | ||
| 46 | + defaultCustom, | ||
| 47 | + } = componentInfo || {}; | ||
| 37 | return { | 48 | return { |
| 38 | iconColor: iconColor || persetIconColor, | 49 | iconColor: iconColor || persetIconColor, |
| 39 | unit: unit ?? perseUnit, | 50 | unit: unit ?? perseUnit, |
| @@ -44,6 +55,9 @@ | @@ -44,6 +55,9 @@ | ||
| 44 | iconColorClose: iconColorClose || persetIconColorClose, | 55 | iconColorClose: iconColorClose || persetIconColorClose, |
| 45 | showTime: showTime ?? persetShowTime, | 56 | showTime: showTime ?? persetShowTime, |
| 46 | fontSize: fontSize || persetFontSize || 14, | 57 | fontSize: fontSize || persetFontSize || 14, |
| 58 | + defaultCustom: defaultCustom || 'default', | ||
| 59 | + customIcon: customIcon || [], | ||
| 60 | + customIconClose: customIconClose || [], | ||
| 47 | }; | 61 | }; |
| 48 | }); | 62 | }); |
| 49 | 63 | ||
| @@ -68,10 +82,17 @@ | @@ -68,10 +82,17 @@ | ||
| 68 | <DeviceName :config="config" /> | 82 | <DeviceName :config="config" /> |
| 69 | <div class="flex flex-1 flex-col justify-center items-center"> | 83 | <div class="flex flex-1 flex-col justify-center items-center"> |
| 70 | <SvgIcon | 84 | <SvgIcon |
| 85 | + v-if="getDesign.defaultCustom !== 'custom'" | ||
| 71 | :name="isOpenClose ? getDesign.icon : getDesign.iconClose" | 86 | :name="isOpenClose ? getDesign.icon : getDesign.iconClose" |
| 72 | prefix="iconfont" | 87 | prefix="iconfont" |
| 73 | :size="getRatio ? getRatio * 70 : 70" | 88 | :size="getRatio ? getRatio * 70 : 70" |
| 74 | - :style="{ color: isOpenClose ? getDesign.iconColor : getDesign.iconColorClose }" | 89 | + :style="{ color: getDesign.iconColor }" |
| 90 | + /> | ||
| 91 | + <img | ||
| 92 | + v-else | ||
| 93 | + :src="isOpenClose ? getDesign.customIcon[0]?.url : getDesign.customIconClose[0]?.url" | ||
| 94 | + :style="{ width: getRatio ? getRatio * 70 + 'px' : '70px' }" | ||
| 95 | + :alt="getDesign.customIcon[0]?.name" | ||
| 75 | /> | 96 | /> |
| 76 | <div | 97 | <div |
| 77 | class="text-gray-500 truncate m-2" | 98 | class="text-gray-500 truncate m-2" |
| @@ -3,6 +3,9 @@ | @@ -3,6 +3,9 @@ | ||
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 7 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 8 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 6 | 9 | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 11 | schemas: [ |
| @@ -61,11 +64,37 @@ | @@ -61,11 +64,37 @@ | ||
| 61 | }, | 64 | }, |
| 62 | }, | 65 | }, |
| 63 | { | 66 | { |
| 67 | + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 68 | + label: '显示设备名称', | ||
| 69 | + component: 'Checkbox', | ||
| 70 | + defaultValue: option.showDeviceName, | ||
| 71 | + }, | ||
| 72 | + { | ||
| 73 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, | ||
| 74 | + label: '图标类型', | ||
| 75 | + component: 'RadioGroup', | ||
| 76 | + defaultValue: 'default', | ||
| 77 | + componentProps: ({ formModel }) => { | ||
| 78 | + return { | ||
| 79 | + options: [ | ||
| 80 | + { label: '系统默认', value: 'default' }, | ||
| 81 | + { label: '自定义', value: 'custom' }, | ||
| 82 | + ], | ||
| 83 | + onChange() { | ||
| 84 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 85 | + }, | ||
| 86 | + }; | ||
| 87 | + }, | ||
| 88 | + }, | ||
| 89 | + { | ||
| 64 | field: ComponentConfigFieldEnum.ICON_COLOR, | 90 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 65 | label: '图标颜色', | 91 | label: '图标颜色', |
| 66 | component: 'ColorPicker', | 92 | component: 'ColorPicker', |
| 67 | changeEvent: 'update:value', | 93 | changeEvent: 'update:value', |
| 68 | defaultValue: option.iconColor, | 94 | defaultValue: option.iconColor, |
| 95 | + ifShow: ({ model }) => { | ||
| 96 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 97 | + }, | ||
| 69 | }, | 98 | }, |
| 70 | { | 99 | { |
| 71 | field: ComponentConfigFieldEnum.ICON, | 100 | field: ComponentConfigFieldEnum.ICON, |
| @@ -73,6 +102,9 @@ | @@ -73,6 +102,9 @@ | ||
| 73 | component: 'IconDrawer', | 102 | component: 'IconDrawer', |
| 74 | changeEvent: 'update:value', | 103 | changeEvent: 'update:value', |
| 75 | defaultValue: option.icon, | 104 | defaultValue: option.icon, |
| 105 | + ifShow: ({ model }) => { | ||
| 106 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 107 | + }, | ||
| 76 | componentProps({ formModel }) { | 108 | componentProps({ formModel }) { |
| 77 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | 109 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 78 | return { | 110 | return { |
| @@ -81,10 +113,50 @@ | @@ -81,10 +113,50 @@ | ||
| 81 | }, | 113 | }, |
| 82 | }, | 114 | }, |
| 83 | { | 115 | { |
| 84 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 85 | - label: '显示设备名称', | ||
| 86 | - component: 'Checkbox', | ||
| 87 | - defaultValue: option.showDeviceName, | 116 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, |
| 117 | + label: '图标', | ||
| 118 | + component: 'ApiUpload', | ||
| 119 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 120 | + changeEvent: 'update:fileList', | ||
| 121 | + valueField: 'fileList', | ||
| 122 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 123 | + componentProps: ({ formModel }) => { | ||
| 124 | + return { | ||
| 125 | + maxSize: 50 * 1024, | ||
| 126 | + listType: 'picture-card', | ||
| 127 | + maxFileLimit: 1, | ||
| 128 | + accept: '.svg', | ||
| 129 | + api: async (file: File) => { | ||
| 130 | + try { | ||
| 131 | + const formData = new FormData(); | ||
| 132 | + const { name } = file; | ||
| 133 | + formData.set('file', file); | ||
| 134 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 135 | + return { | ||
| 136 | + uid: fileStaticUri, | ||
| 137 | + name: name || fileName, | ||
| 138 | + url: fileStaticUri, | ||
| 139 | + } as FileItem; | ||
| 140 | + } catch (error) { | ||
| 141 | + return {}; | ||
| 142 | + } | ||
| 143 | + }, | ||
| 144 | + // showUploadList: true, | ||
| 145 | + onDownload() {}, | ||
| 146 | + onPreview: (fileList: FileItem) => { | ||
| 147 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 148 | + }, | ||
| 149 | + onDelete(url: string) { | ||
| 150 | + formModel.deleteUrl = url!; | ||
| 151 | + }, | ||
| 152 | + }; | ||
| 153 | + }, | ||
| 154 | + }, | ||
| 155 | + { | ||
| 156 | + field: 'deleteUrl', | ||
| 157 | + label: '', | ||
| 158 | + component: 'Input', | ||
| 159 | + show: false, | ||
| 88 | }, | 160 | }, |
| 89 | ], | 161 | ], |
| 90 | showActionButtonGroup: false, | 162 | showActionButtonGroup: false, |
| @@ -31,7 +31,6 @@ | @@ -31,7 +31,6 @@ | ||
| 31 | 31 | ||
| 32 | const getDesign = computed(() => { | 32 | const getDesign = computed(() => { |
| 33 | const { persetOption = {}, option } = props.config; | 33 | const { persetOption = {}, option } = props.config; |
| 34 | - | ||
| 35 | const { | 34 | const { |
| 36 | iconColor: persetIconColor, | 35 | iconColor: persetIconColor, |
| 37 | unit: perseUnit, | 36 | unit: perseUnit, |
| @@ -44,7 +43,8 @@ | @@ -44,7 +43,8 @@ | ||
| 44 | const { componentInfo, attributeRename } = option; | 43 | const { componentInfo, attributeRename } = option; |
| 45 | const { functionName } = unref(getThingModelTsl) || {}; | 44 | const { functionName } = unref(getThingModelTsl) || {}; |
| 46 | 45 | ||
| 47 | - const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {}; | 46 | + const { icon, iconColor, fontColor, unit, valueSize, fontSize, customIcon, defaultCustom } = |
| 47 | + componentInfo || {}; | ||
| 48 | return { | 48 | return { |
| 49 | iconColor: iconColor || persetIconColor, | 49 | iconColor: iconColor || persetIconColor, |
| 50 | unit: unit ?? perseUnit, | 50 | unit: unit ?? perseUnit, |
| @@ -53,6 +53,8 @@ | @@ -53,6 +53,8 @@ | ||
| 53 | attribute: attributeRename || functionName, | 53 | attribute: attributeRename || functionName, |
| 54 | valueSize: valueSize || persetValueSize || 20, | 54 | valueSize: valueSize || persetValueSize || 20, |
| 55 | fontSize: fontSize || persetFontSize || 14, | 55 | fontSize: fontSize || persetFontSize || 14, |
| 56 | + defaultCustom: defaultCustom || 'default', | ||
| 57 | + customIcon: customIcon || [], | ||
| 56 | }; | 58 | }; |
| 57 | }); | 59 | }); |
| 58 | 60 | ||
| @@ -78,11 +80,18 @@ | @@ -78,11 +80,18 @@ | ||
| 78 | <DeviceName :config="config" /> | 80 | <DeviceName :config="config" /> |
| 79 | <div class="flex-1 flex justify-center items-center flex-col w-full"> | 81 | <div class="flex-1 flex justify-center items-center flex-col w-full"> |
| 80 | <SvgIcon | 82 | <SvgIcon |
| 83 | + v-if="getDesign.defaultCustom !== 'custom'" | ||
| 81 | :name="getDesign.icon!" | 84 | :name="getDesign.icon!" |
| 82 | prefix="iconfont" | 85 | prefix="iconfont" |
| 83 | :size="getRatio ? getRatio * 70 : 70" | 86 | :size="getRatio ? getRatio * 70 : 70" |
| 84 | :style="{ color: getDesign.iconColor }" | 87 | :style="{ color: getDesign.iconColor }" |
| 85 | /> | 88 | /> |
| 89 | + <img | ||
| 90 | + v-else | ||
| 91 | + :src="getDesign.customIcon[0]?.url" | ||
| 92 | + :style="{ width: getRatio ? getRatio * 70 + 'px' : '70px' }" | ||
| 93 | + :alt="getDesign.customIcon[0]?.name" | ||
| 94 | + /> | ||
| 86 | <h1 | 95 | <h1 |
| 87 | class="font-bold m-2 truncate w-full text-center" | 96 | class="font-bold m-2 truncate w-full text-center" |
| 88 | :style="{ | 97 | :style="{ |
| @@ -3,6 +3,9 @@ | @@ -3,6 +3,9 @@ | ||
| 3 | import { useForm, BasicForm } from '/@/components/Form'; | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 7 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 8 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 6 | 9 | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 11 | schemas: [ |
| @@ -60,11 +63,37 @@ | @@ -60,11 +63,37 @@ | ||
| 60 | }, | 63 | }, |
| 61 | }, | 64 | }, |
| 62 | { | 65 | { |
| 66 | + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 67 | + label: '显示设备名称', | ||
| 68 | + component: 'Checkbox', | ||
| 69 | + defaultValue: option.showDeviceName, | ||
| 70 | + }, | ||
| 71 | + { | ||
| 72 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, | ||
| 73 | + label: '图标类型', | ||
| 74 | + component: 'RadioGroup', | ||
| 75 | + defaultValue: 'default', | ||
| 76 | + componentProps: ({ formModel }) => { | ||
| 77 | + return { | ||
| 78 | + options: [ | ||
| 79 | + { label: '系统默认', value: 'default' }, | ||
| 80 | + { label: '自定义', value: 'custom' }, | ||
| 81 | + ], | ||
| 82 | + onChange() { | ||
| 83 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 84 | + }, | ||
| 85 | + }; | ||
| 86 | + }, | ||
| 87 | + }, | ||
| 88 | + { | ||
| 63 | field: ComponentConfigFieldEnum.ICON_COLOR, | 89 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 64 | label: '图标颜色', | 90 | label: '图标颜色', |
| 65 | component: 'ColorPicker', | 91 | component: 'ColorPicker', |
| 66 | changeEvent: 'update:value', | 92 | changeEvent: 'update:value', |
| 67 | defaultValue: option.iconColor, | 93 | defaultValue: option.iconColor, |
| 94 | + ifShow: ({ model }) => { | ||
| 95 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 96 | + }, | ||
| 68 | }, | 97 | }, |
| 69 | { | 98 | { |
| 70 | field: ComponentConfigFieldEnum.ICON, | 99 | field: ComponentConfigFieldEnum.ICON, |
| @@ -72,6 +101,9 @@ | @@ -72,6 +101,9 @@ | ||
| 72 | component: 'IconDrawer', | 101 | component: 'IconDrawer', |
| 73 | changeEvent: 'update:value', | 102 | changeEvent: 'update:value', |
| 74 | defaultValue: option.icon, | 103 | defaultValue: option.icon, |
| 104 | + ifShow: ({ model }) => { | ||
| 105 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 106 | + }, | ||
| 75 | componentProps({ formModel }) { | 107 | componentProps({ formModel }) { |
| 76 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | 108 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 77 | return { | 109 | return { |
| @@ -80,10 +112,50 @@ | @@ -80,10 +112,50 @@ | ||
| 80 | }, | 112 | }, |
| 81 | }, | 113 | }, |
| 82 | { | 114 | { |
| 83 | - field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, | ||
| 84 | - label: '显示设备名称', | ||
| 85 | - component: 'Checkbox', | ||
| 86 | - defaultValue: option.showDeviceName, | 115 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, |
| 116 | + label: '图标', | ||
| 117 | + component: 'ApiUpload', | ||
| 118 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 119 | + changeEvent: 'update:fileList', | ||
| 120 | + valueField: 'fileList', | ||
| 121 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 122 | + componentProps: ({ formModel }) => { | ||
| 123 | + return { | ||
| 124 | + listType: 'picture-card', | ||
| 125 | + maxSize: 50 * 1024, | ||
| 126 | + maxFileLimit: 1, | ||
| 127 | + accept: '.svg', | ||
| 128 | + api: async (file: File) => { | ||
| 129 | + try { | ||
| 130 | + const formData = new FormData(); | ||
| 131 | + const { name } = file; | ||
| 132 | + formData.set('file', file); | ||
| 133 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 134 | + return { | ||
| 135 | + uid: fileStaticUri, | ||
| 136 | + name: name || fileName, | ||
| 137 | + url: fileStaticUri, | ||
| 138 | + } as FileItem; | ||
| 139 | + } catch (error) { | ||
| 140 | + return {}; | ||
| 141 | + } | ||
| 142 | + }, | ||
| 143 | + // showUploadList: true, | ||
| 144 | + onDownload() {}, | ||
| 145 | + onPreview: (fileList: FileItem) => { | ||
| 146 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 147 | + }, | ||
| 148 | + onDelete(url: string) { | ||
| 149 | + formModel.deleteUrl = url!; | ||
| 150 | + }, | ||
| 151 | + }; | ||
| 152 | + }, | ||
| 153 | + }, | ||
| 154 | + { | ||
| 155 | + field: 'deleteUrl', | ||
| 156 | + label: '', | ||
| 157 | + component: 'Input', | ||
| 158 | + show: false, | ||
| 87 | }, | 159 | }, |
| 88 | ], | 160 | ], |
| 89 | showActionButtonGroup: false, | 161 | showActionButtonGroup: false, |
| @@ -40,7 +40,8 @@ | @@ -40,7 +40,8 @@ | ||
| 40 | 40 | ||
| 41 | const { componentInfo, attribute, attributeRename } = option; | 41 | const { componentInfo, attribute, attributeRename } = option; |
| 42 | 42 | ||
| 43 | - const { icon, iconColor, fontColor, unit, valueSize, fontSize } = componentInfo || {}; | 43 | + const { icon, iconColor, fontColor, unit, valueSize, fontSize, customIcon, defaultCustom } = |
| 44 | + componentInfo || {}; | ||
| 44 | return { | 45 | return { |
| 45 | iconColor: iconColor || persetIconColor, | 46 | iconColor: iconColor || persetIconColor, |
| 46 | unit: unit ?? perseUnit, | 47 | unit: unit ?? perseUnit, |
| @@ -49,6 +50,8 @@ | @@ -49,6 +50,8 @@ | ||
| 49 | attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, | 50 | attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, |
| 50 | valueSize: valueSize || persetValueSize || 20, | 51 | valueSize: valueSize || persetValueSize || 20, |
| 51 | fontSize: fontSize || persetFontSize || 14, | 52 | fontSize: fontSize || persetFontSize || 14, |
| 53 | + defaultCustom: defaultCustom || 'default', | ||
| 54 | + customIcon: customIcon || [], | ||
| 52 | }; | 55 | }; |
| 53 | }); | 56 | }); |
| 54 | 57 | ||
| @@ -73,11 +76,18 @@ | @@ -73,11 +76,18 @@ | ||
| 73 | <DeviceName :config="config" /> | 76 | <DeviceName :config="config" /> |
| 74 | <div :style="getScale" class="flex-1 flex justify-center items-center flex-col w-full"> | 77 | <div :style="getScale" class="flex-1 flex justify-center items-center flex-col w-full"> |
| 75 | <SvgIcon | 78 | <SvgIcon |
| 79 | + v-if="getDesign.defaultCustom !== 'custom'" | ||
| 76 | :name="getDesign.icon!" | 80 | :name="getDesign.icon!" |
| 77 | prefix="iconfont" | 81 | prefix="iconfont" |
| 78 | :size="getRatio ? getRatio * 70 : 70" | 82 | :size="getRatio ? getRatio * 70 : 70" |
| 79 | :style="{ color: getDesign.iconColor }" | 83 | :style="{ color: getDesign.iconColor }" |
| 80 | /> | 84 | /> |
| 85 | + <img | ||
| 86 | + v-else | ||
| 87 | + :src="getDesign.customIcon[0]?.url" | ||
| 88 | + :style="{ width: getRatio ? getRatio * 70 + 'px' : '70px' }" | ||
| 89 | + :alt="getDesign.customIcon[0]?.name" | ||
| 90 | + /> | ||
| 81 | <h1 | 91 | <h1 |
| 82 | class="my-4 font-bold !my-2 truncate w-full text-center" | 92 | class="my-4 font-bold !my-2 truncate w-full text-center" |
| 83 | :style="{ | 93 | :style="{ |
| @@ -4,6 +4,10 @@ | @@ -4,6 +4,10 @@ | ||
| 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | import { option } from './config'; | 5 | import { option } from './config'; |
| 6 | 6 | ||
| 7 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 8 | + import { createImgPreview } from '/@/components/Preview'; | ||
| 9 | + import { upload } from '/@/api/oss/ossFileUploader'; | ||
| 10 | + | ||
| 7 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ | 11 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | schemas: [ | 12 | schemas: [ |
| 9 | { | 13 | { |
| @@ -60,11 +64,31 @@ | @@ -60,11 +64,31 @@ | ||
| 60 | }, | 64 | }, |
| 61 | }, | 65 | }, |
| 62 | { | 66 | { |
| 67 | + field: ComponentConfigFieldEnum.DEFAULT_CUSTOM, | ||
| 68 | + label: '图标类型', | ||
| 69 | + component: 'RadioGroup', | ||
| 70 | + defaultValue: 'default', | ||
| 71 | + componentProps: ({ formModel }) => { | ||
| 72 | + return { | ||
| 73 | + options: [ | ||
| 74 | + { label: '系统默认', value: 'default' }, | ||
| 75 | + { label: '自定义', value: 'custom' }, | ||
| 76 | + ], | ||
| 77 | + onChange() { | ||
| 78 | + formModel[ComponentConfigFieldEnum.CUSTOM_ICON] = []; | ||
| 79 | + }, | ||
| 80 | + }; | ||
| 81 | + }, | ||
| 82 | + }, | ||
| 83 | + { | ||
| 63 | field: ComponentConfigFieldEnum.ICON_COLOR, | 84 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 64 | label: '图标颜色', | 85 | label: '图标颜色', |
| 65 | component: 'ColorPicker', | 86 | component: 'ColorPicker', |
| 66 | changeEvent: 'update:value', | 87 | changeEvent: 'update:value', |
| 67 | defaultValue: option.iconColor, | 88 | defaultValue: option.iconColor, |
| 89 | + ifShow: ({ model }) => { | ||
| 90 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 91 | + }, | ||
| 68 | }, | 92 | }, |
| 69 | { | 93 | { |
| 70 | field: ComponentConfigFieldEnum.ICON, | 94 | field: ComponentConfigFieldEnum.ICON, |
| @@ -73,6 +97,9 @@ | @@ -73,6 +97,9 @@ | ||
| 73 | changeEvent: 'update:value', | 97 | changeEvent: 'update:value', |
| 74 | valueField: 'value', | 98 | valueField: 'value', |
| 75 | defaultValue: option.icon, | 99 | defaultValue: option.icon, |
| 100 | + ifShow: ({ model }) => { | ||
| 101 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | ||
| 102 | + }, | ||
| 76 | componentProps({ formModel }) { | 103 | componentProps({ formModel }) { |
| 77 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; | 104 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 78 | return { | 105 | return { |
| @@ -80,6 +107,51 @@ | @@ -80,6 +107,51 @@ | ||
| 80 | }; | 107 | }; |
| 81 | }, | 108 | }, |
| 82 | }, | 109 | }, |
| 110 | + { | ||
| 111 | + field: ComponentConfigFieldEnum.CUSTOM_ICON, | ||
| 112 | + label: '图标', | ||
| 113 | + component: 'ApiUpload', | ||
| 114 | + ifShow: ({ model }) => model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] === 'custom', | ||
| 115 | + changeEvent: 'update:fileList', | ||
| 116 | + valueField: 'fileList', | ||
| 117 | + helpMessage: ['支持.svg格式,建议尺寸为32*32px,大小不超过50kb '], | ||
| 118 | + componentProps: ({ formModel }) => { | ||
| 119 | + return { | ||
| 120 | + listType: 'picture-card', | ||
| 121 | + maxSize: 50 * 1024, | ||
| 122 | + maxFileLimit: 1, | ||
| 123 | + accept: '.svg', | ||
| 124 | + api: async (file: File) => { | ||
| 125 | + try { | ||
| 126 | + const formData = new FormData(); | ||
| 127 | + const { name } = file; | ||
| 128 | + formData.set('file', file); | ||
| 129 | + const { fileStaticUri, fileName } = await upload(formData); | ||
| 130 | + return { | ||
| 131 | + uid: fileStaticUri, | ||
| 132 | + name: name || fileName, | ||
| 133 | + url: fileStaticUri, | ||
| 134 | + } as FileItem; | ||
| 135 | + } catch (error) { | ||
| 136 | + return {}; | ||
| 137 | + } | ||
| 138 | + }, | ||
| 139 | + onDownload() {}, | ||
| 140 | + onPreview: (fileList: FileItem) => { | ||
| 141 | + createImgPreview({ imageList: [fileList.url!] }); | ||
| 142 | + }, | ||
| 143 | + onDelete(url: string) { | ||
| 144 | + formModel.deleteUrl = url!; | ||
| 145 | + }, | ||
| 146 | + }; | ||
| 147 | + }, | ||
| 148 | + }, | ||
| 149 | + { | ||
| 150 | + field: 'deleteUrl', | ||
| 151 | + label: '', | ||
| 152 | + component: 'Input', | ||
| 153 | + show: false, | ||
| 154 | + }, | ||
| 83 | ], | 155 | ], |
| 84 | showActionButtonGroup: false, | 156 | showActionButtonGroup: false, |
| 85 | labelWidth: 120, | 157 | labelWidth: 120, |
| @@ -35,7 +35,8 @@ | @@ -35,7 +35,8 @@ | ||
| 35 | 35 | ||
| 36 | return { | 36 | return { |
| 37 | dataSource: dataSource.map((item) => { | 37 | dataSource: dataSource.map((item) => { |
| 38 | - const { fontColor, icon, iconColor, unit, valueSize, fontSize } = item.componentInfo; | 38 | + const { fontColor, icon, iconColor, unit, valueSize, fontSize, customIcon, defaultCustom } = |
| 39 | + item.componentInfo; | ||
| 39 | const { attribute, attributeRename, deviceName, deviceRename, deviceId, deviceProfileId } = | 40 | const { attribute, attributeRename, deviceName, deviceRename, deviceId, deviceProfileId } = |
| 40 | item; | 41 | item; |
| 41 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); | 42 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); |
| @@ -50,6 +51,8 @@ | @@ -50,6 +51,8 @@ | ||
| 50 | id: deviceId, | 51 | id: deviceId, |
| 51 | valueSize: valueSize || persetValueSize || 20, | 52 | valueSize: valueSize || persetValueSize || 20, |
| 52 | fontSize: fontSize || persetFontSize || 14, | 53 | fontSize: fontSize || persetFontSize || 14, |
| 54 | + defaultCustom: defaultCustom || 'default', | ||
| 55 | + customIcon: customIcon || [], | ||
| 53 | }; | 56 | }; |
| 54 | }), | 57 | }), |
| 55 | }; | 58 | }; |
| @@ -67,6 +70,8 @@ | @@ -67,6 +70,8 @@ | ||
| 67 | fontColor: '#357CFB', | 70 | fontColor: '#357CFB', |
| 68 | fontSize: 16, | 71 | fontSize: 16, |
| 69 | valueSize: 16, | 72 | valueSize: 16, |
| 73 | + defaultCustom: 'default', | ||
| 74 | + customIcon: [], | ||
| 70 | }, | 75 | }, |
| 71 | { | 76 | { |
| 72 | id: buildUUID(), | 77 | id: buildUUID(), |
| @@ -79,6 +84,8 @@ | @@ -79,6 +84,8 @@ | ||
| 79 | fontColor: '#FFA000', | 84 | fontColor: '#FFA000', |
| 80 | fontSize: 16, | 85 | fontSize: 16, |
| 81 | valueSize: 16, | 86 | valueSize: 16, |
| 87 | + defaultCustom: 'default', | ||
| 88 | + customIcon: [], | ||
| 82 | }, | 89 | }, |
| 83 | ]); | 90 | ]); |
| 84 | 91 | ||
| @@ -114,11 +121,24 @@ | @@ -114,11 +121,24 @@ | ||
| 114 | class="flex justify-between items-center mt-2" | 121 | class="flex justify-between items-center mt-2" |
| 115 | > | 122 | > |
| 116 | <div class="flex items-center"> | 123 | <div class="flex items-center"> |
| 117 | - <SvgIcon | 124 | + <!-- <SvgIcon |
| 118 | :name="item.icon!" | 125 | :name="item.icon!" |
| 119 | prefix="iconfont" | 126 | prefix="iconfont" |
| 120 | :size="getRatio ? 30 * getRatio : 30" | 127 | :size="getRatio ? 30 * getRatio : 30" |
| 121 | :style="{ color: item.iconColor }" | 128 | :style="{ color: item.iconColor }" |
| 129 | + /> --> | ||
| 130 | + <SvgIcon | ||
| 131 | + v-if="item.defaultCustom !== 'custom'" | ||
| 132 | + :name="item.icon!" | ||
| 133 | + prefix="iconfont" | ||
| 134 | + :size="getRatio ? getRatio * 30 : 30" | ||
| 135 | + :style="{ color: item.iconColor }" | ||
| 136 | + /> | ||
| 137 | + <img | ||
| 138 | + v-else | ||
| 139 | + :src="item.customIcon[0]?.url" | ||
| 140 | + :style="{ width: getRatio ? getRatio * 30 + 'px' : '30px' }" | ||
| 141 | + :alt="item.customIcon[0]?.name" | ||
| 122 | /> | 142 | /> |
| 123 | <div | 143 | <div |
| 124 | class="text-gray-500 ml-6" | 144 | class="text-gray-500 ml-6" |
| @@ -36,4 +36,8 @@ export enum ComponentConfigFieldEnum { | @@ -36,4 +36,8 @@ export enum ComponentConfigFieldEnum { | ||
| 36 | MIN_NUMBER = 'minNumber', | 36 | MIN_NUMBER = 'minNumber', |
| 37 | MAX_NUMBER = 'maxNumber', | 37 | MAX_NUMBER = 'maxNumber', |
| 38 | PASS_WORD = 'password', //操作密码 | 38 | PASS_WORD = 'password', //操作密码 |
| 39 | + DEFAULT_CUSTOM = 'defaultCustom', | ||
| 40 | + DEFAULT_CUSTOM_CLOSE = 'defaultCustomClose', | ||
| 41 | + CUSTOM_ICON = 'customIcon', | ||
| 42 | + CUSTOM_ICON_CLOSE = 'customIconClose', | ||
| 39 | } | 43 | } |
| @@ -20,6 +20,8 @@ | @@ -20,6 +20,8 @@ | ||
| 20 | import { useGetComponentConfig } from '../../../packages/hook/useGetComponetConfig'; | 20 | import { useGetComponentConfig } from '../../../packages/hook/useGetComponetConfig'; |
| 21 | import { isBoolean } from '/@/utils/is'; | 21 | import { isBoolean } from '/@/utils/is'; |
| 22 | import { useApp } from '../../hooks/useApp'; | 22 | import { useApp } from '../../hooks/useApp'; |
| 23 | + import { FileItem } from '/@/components/Form/src/components/ApiUpload.vue'; | ||
| 24 | + import { deleteFilePath } from '/@/api/oss/ossFileUploader'; | ||
| 23 | 25 | ||
| 24 | const props = defineProps<{ | 26 | const props = defineProps<{ |
| 25 | sourceInfo: WidgetDataType; | 27 | sourceInfo: WidgetDataType; |
| @@ -74,8 +76,56 @@ | @@ -74,8 +76,56 @@ | ||
| 74 | emit('update', toRaw(props.sourceInfo)); | 76 | emit('update', toRaw(props.sourceInfo)); |
| 75 | } | 77 | } |
| 76 | 78 | ||
| 79 | + const countElementOccurrences = (arr) => { | ||
| 80 | + const countMap = {}; | ||
| 81 | + | ||
| 82 | + arr.forEach((element) => { | ||
| 83 | + if (countMap[element]) { | ||
| 84 | + countMap[element]++; | ||
| 85 | + } else { | ||
| 86 | + countMap[element] = 1; | ||
| 87 | + } | ||
| 88 | + }); | ||
| 89 | + | ||
| 90 | + return countMap; | ||
| 91 | + }; | ||
| 92 | + | ||
| 77 | async function handleDelete() { | 93 | async function handleDelete() { |
| 78 | try { | 94 | try { |
| 95 | + const { componentData: oldDataSource } = props.rawDataSource; | ||
| 96 | + const customIconUrls = ref<any>([]); | ||
| 97 | + oldDataSource?.forEach((item: any) => { | ||
| 98 | + item.dataSource?.forEach((dataSource) => { | ||
| 99 | + if (dataSource.componentInfo?.customIcon) { | ||
| 100 | + dataSource.componentInfo?.customIcon.forEach((icon: FileItem) => { | ||
| 101 | + customIconUrls.value.push(icon.url); | ||
| 102 | + }); | ||
| 103 | + } | ||
| 104 | + }); | ||
| 105 | + }); | ||
| 106 | + | ||
| 107 | + const { dataSource: deleteDataSource } = props.sourceInfo; | ||
| 108 | + const dataSourceDeleteUrl = deleteDataSource.map( | ||
| 109 | + (item) => item.componentInfo.customIcon?.[0].url | ||
| 110 | + ); | ||
| 111 | + | ||
| 112 | + if (dataSourceDeleteUrl?.length) { | ||
| 113 | + // 判断外部所有组件是否有dataSourceDeleteUrl使用中的url | ||
| 114 | + const deletePromise = unref(customIconUrls)?.filter((item) => | ||
| 115 | + dataSourceDeleteUrl?.includes(item) | ||
| 116 | + ); | ||
| 117 | + | ||
| 118 | + const deleteUrlInfo = countElementOccurrences(deletePromise); | ||
| 119 | + const deleteUrl = deletePromise?.filter((item) => deleteUrlInfo?.[item] == 1); | ||
| 120 | + Promise.all( | ||
| 121 | + deleteUrl.map((item) => { | ||
| 122 | + deleteFilePath(item); | ||
| 123 | + }) | ||
| 124 | + ); | ||
| 125 | + } | ||
| 126 | + } catch (err) {} | ||
| 127 | + | ||
| 128 | + try { | ||
| 79 | await deleteDataComponent({ dataBoardId: unref(boardId), ids: [props.sourceInfo.id] }); | 129 | await deleteDataComponent({ dataBoardId: unref(boardId), ids: [props.sourceInfo.id] }); |
| 80 | createMessage.success('删除成功'); | 130 | createMessage.success('删除成功'); |
| 81 | emit('ok'); | 131 | emit('ok'); |