Commit 67d1de3831df2c6d6446ccfdcb6e177bf6b4a6c6
Committed by
xp.Huang
1 parent
b1aeeabc
fix: 看板组件图标能使用自定义图标
Showing
18 changed files
with
788 additions
and
102 deletions
| ... | ... | @@ -71,9 +71,12 @@ |
| 71 | 71 | return false; |
| 72 | 72 | } |
| 73 | 73 | } |
| 74 | - | |
| 75 | 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 | 80 | return false; |
| 78 | 81 | } |
| 79 | 82 | handleUpload(file); | ... | ... |
| ... | ... | @@ -30,7 +30,7 @@ |
| 30 | 30 | auth: 'api:yt:sceneLinkage:update', |
| 31 | 31 | icon: 'clarity:note-edit-line', |
| 32 | 32 | onClick: handleEdit.bind(null, record), |
| 33 | - ifShow: record.status !== 1 && record.isEdge !== 1, | |
| 33 | + ifShow: record.status !== 1, | |
| 34 | 34 | }, |
| 35 | 35 | ]" |
| 36 | 36 | :drop-down-actions="[ |
| ... | ... | @@ -39,7 +39,7 @@ |
| 39 | 39 | auth: 'api:yt:sceneLinkage:delete', |
| 40 | 40 | icon: 'ant-design:delete-outlined', |
| 41 | 41 | color: 'error', |
| 42 | - ifShow: record.status !== 1 && record.isEdge !== 1, | |
| 42 | + ifShow: record.status !== 1, | |
| 43 | 43 | popConfirm: { |
| 44 | 44 | title: '是否确认删除', |
| 45 | 45 | confirm: handleDeleteOrBatchDelete.bind(null, record), |
| ... | ... | @@ -56,7 +56,6 @@ |
| 56 | 56 | :loading="record.pendingStatus" |
| 57 | 57 | checkedChildren="启用" |
| 58 | 58 | unCheckedChildren="禁用" |
| 59 | - :disabled="record.isEdge === 1" | |
| 60 | 59 | @change="(checked:boolean)=>statusChange(checked,record)" |
| 61 | 60 | /> |
| 62 | 61 | </Authority> | ... | ... |
| ... | ... | @@ -56,7 +56,7 @@ |
| 56 | 56 | </script> |
| 57 | 57 | |
| 58 | 58 | <template> |
| 59 | - <BasicModal @register="register" title="组件设置" @ok="handleOk"> | |
| 59 | + <BasicModal @register="register" title="组件设置" @ok="handleOk" :width="700"> | |
| 60 | 60 | <!-- --> |
| 61 | 61 | <component ref="settingFormEl" :is="getSettingComponent" /> |
| 62 | 62 | </BasicModal> | ... | ... |
| ... | ... | @@ -29,6 +29,8 @@ |
| 29 | 29 | import { MessageAlert } from './components/MessageAlert'; |
| 30 | 30 | import { createSelectWidgetKeysContext, createSelectWidgetModeContext } from './useContext'; |
| 31 | 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 | 35 | const props = defineProps<{ |
| 34 | 36 | layout: Layout[]; |
| ... | ... | @@ -227,6 +229,20 @@ |
| 227 | 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 | 246 | const handleSubmit = async () => { |
| 231 | 247 | const validateResult = await validate(); |
| 232 | 248 | if (validateResult && !validateResult.flag) { |
| ... | ... | @@ -241,6 +257,59 @@ |
| 241 | 257 | } |
| 242 | 258 | } |
| 243 | 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 | 313 | try { |
| 245 | 314 | loading.value = true; |
| 246 | 315 | unref(currentMode) === DataActionModeEnum.UPDATE | ... | ... |
| ... | ... | @@ -3,15 +3,68 @@ |
| 3 | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 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 | 60 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 11 | 61 | label: '图标颜色', |
| 12 | 62 | component: 'ColorPicker', |
| 13 | 63 | changeEvent: 'update:value', |
| 14 | 64 | defaultValue: option.iconColor, |
| 65 | + ifShow: ({ model }) => { | |
| 66 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 67 | + }, | |
| 15 | 68 | }, |
| 16 | 69 | { |
| 17 | 70 | field: ComponentConfigFieldEnum.ICON, |
| ... | ... | @@ -19,6 +72,9 @@ |
| 19 | 72 | component: 'IconDrawer', |
| 20 | 73 | changeEvent: 'update:value', |
| 21 | 74 | defaultValue: option.icon, |
| 75 | + ifShow: ({ model }) => { | |
| 76 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 77 | + }, | |
| 22 | 78 | componentProps({ formModel }) { |
| 23 | 79 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 24 | 80 | return { |
| ... | ... | @@ -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 | 133 | showActionButtonGroup: false, | ... | ... |
| ... | ... | @@ -31,7 +31,7 @@ |
| 31 | 31 | fontSize: persetFontSize, |
| 32 | 32 | password: persetPassword, |
| 33 | 33 | } = persetOption || {}; |
| 34 | - const { icon, iconColor, fontSize, password } = componentInfo || {}; | |
| 34 | + const { icon, iconColor, fontSize, password, customIcon, defaultCustom } = componentInfo || {}; | |
| 35 | 35 | |
| 36 | 36 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); |
| 37 | 37 | return { |
| ... | ... | @@ -41,6 +41,8 @@ |
| 41 | 41 | fontSize: fontSize || persetFontSize || 14, |
| 42 | 42 | password: password || persetPassword, |
| 43 | 43 | commandType, |
| 44 | + defaultCustom: defaultCustom || 'default', | |
| 45 | + customIcon: customIcon || [], | |
| 44 | 46 | }; |
| 45 | 47 | }); |
| 46 | 48 | |
| ... | ... | @@ -81,10 +83,17 @@ |
| 81 | 83 | <main class="w-full h-full flex justify-around items-center" :style="getScale"> |
| 82 | 84 | <div class="flex flex-col justify-center items-center"> |
| 83 | 85 | <SvgIcon |
| 84 | - :name="getDesign.icon" | |
| 86 | + v-if="getDesign.defaultCustom !== 'custom'" | |
| 87 | + :name="getDesign.icon!" | |
| 85 | 88 | prefix="iconfont" |
| 86 | - :style="{ color: getDesign.iconColor }" | |
| 87 | 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 | 98 | <span |
| 90 | 99 | class="mt-3 truncate text-gray-500 text-center" | ... | ... |
| ... | ... | @@ -3,30 +3,13 @@ |
| 3 | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 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 | 13 | field: ComponentConfigFieldEnum.FONT_SIZE, |
| 31 | 14 | label: '文本字体大小', |
| 32 | 15 | component: 'InputNumber', |
| ... | ... | @@ -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 | 37 | field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME, |
| 49 | 38 | label: '显示设备名称', |
| 50 | 39 | component: 'Checkbox', |
| 51 | 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 | 133 | showActionButtonGroup: false, | ... | ... |
| ... | ... | @@ -38,8 +38,17 @@ |
| 38 | 38 | } = persetOption || {}; |
| 39 | 39 | return { |
| 40 | 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 | 52 | const { |
| 44 | 53 | attribute, |
| 45 | 54 | attributeRename, |
| ... | ... | @@ -76,6 +85,8 @@ |
| 76 | 85 | closeCommand, |
| 77 | 86 | openService, |
| 78 | 87 | closeService, |
| 88 | + defaultCustom: defaultCustom || 'default', | |
| 89 | + customIcon: customIcon || [], | |
| 79 | 90 | } as SwitchItemType; |
| 80 | 91 | }), |
| 81 | 92 | }; |
| ... | ... | @@ -128,11 +139,24 @@ |
| 128 | 139 | :key="item.id" |
| 129 | 140 | class="flex justify-between items-center w-full px-4" |
| 130 | 141 | > |
| 131 | - <SvgIcon | |
| 142 | + <!-- <SvgIcon | |
| 132 | 143 | :name="item.icon!" |
| 133 | 144 | prefix="iconfont" |
| 134 | 145 | :size="getRatio ? 30 * getRatio : 30" |
| 135 | 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 | 161 | <div |
| 138 | 162 | class="text-gray-500 truncate mx-2" | ... | ... |
| ... | ... | @@ -3,15 +3,68 @@ |
| 3 | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 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 | 60 | field: ComponentConfigFieldEnum.ICON, |
| 11 | 61 | label: '开启状态图标', |
| 12 | 62 | component: 'IconDrawer', |
| 13 | 63 | changeEvent: 'update:value', |
| 14 | 64 | defaultValue: option.icon, |
| 65 | + ifShow: ({ model }) => { | |
| 66 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 67 | + }, | |
| 15 | 68 | componentProps({ formModel }) { |
| 16 | 69 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 17 | 70 | return { |
| ... | ... | @@ -24,6 +77,9 @@ |
| 24 | 77 | label: '开启图标颜色', |
| 25 | 78 | component: 'ColorPicker', |
| 26 | 79 | changeEvent: 'update:value', |
| 80 | + ifShow: ({ model }) => { | |
| 81 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 82 | + }, | |
| 27 | 83 | defaultValue: option.iconColor, |
| 28 | 84 | }, |
| 29 | 85 | { |
| ... | ... | @@ -32,6 +88,9 @@ |
| 32 | 88 | component: 'IconDrawer', |
| 33 | 89 | changeEvent: 'update:value', |
| 34 | 90 | defaultValue: option.iconClose, |
| 91 | + ifShow: ({ model }) => { | |
| 92 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 93 | + }, | |
| 35 | 94 | componentProps({ formModel }) { |
| 36 | 95 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR_CLOSE]; |
| 37 | 96 | return { |
| ... | ... | @@ -44,37 +103,84 @@ |
| 44 | 103 | label: '关闭图标颜色', |
| 45 | 104 | component: 'ColorPicker', |
| 46 | 105 | changeEvent: 'update:value', |
| 106 | + ifShow: ({ model }) => { | |
| 107 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 108 | + }, | |
| 47 | 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 | 186 | showActionButtonGroup: false, | ... | ... |
| ... | ... | @@ -32,8 +32,19 @@ |
| 32 | 32 | |
| 33 | 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 | 48 | return { |
| 38 | 49 | iconColor: iconColor || persetIconColor, |
| 39 | 50 | unit: unit ?? perseUnit, |
| ... | ... | @@ -44,6 +55,9 @@ |
| 44 | 55 | iconColorClose: iconColorClose || persetIconColorClose, |
| 45 | 56 | showTime: showTime ?? persetShowTime, |
| 46 | 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 | 82 | <DeviceName :config="config" /> |
| 69 | 83 | <div class="flex flex-1 flex-col justify-center items-center"> |
| 70 | 84 | <SvgIcon |
| 85 | + v-if="getDesign.defaultCustom !== 'custom'" | |
| 71 | 86 | :name="isOpenClose ? getDesign.icon : getDesign.iconClose" |
| 72 | 87 | prefix="iconfont" |
| 73 | 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 | 97 | <div |
| 77 | 98 | class="text-gray-500 truncate m-2" | ... | ... |
| ... | ... | @@ -3,6 +3,9 @@ |
| 3 | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 11 | schemas: [ |
| ... | ... | @@ -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 | 90 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 65 | 91 | label: '图标颜色', |
| 66 | 92 | component: 'ColorPicker', |
| 67 | 93 | changeEvent: 'update:value', |
| 68 | 94 | defaultValue: option.iconColor, |
| 95 | + ifShow: ({ model }) => { | |
| 96 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 97 | + }, | |
| 69 | 98 | }, |
| 70 | 99 | { |
| 71 | 100 | field: ComponentConfigFieldEnum.ICON, |
| ... | ... | @@ -73,6 +102,9 @@ |
| 73 | 102 | component: 'IconDrawer', |
| 74 | 103 | changeEvent: 'update:value', |
| 75 | 104 | defaultValue: option.icon, |
| 105 | + ifShow: ({ model }) => { | |
| 106 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 107 | + }, | |
| 76 | 108 | componentProps({ formModel }) { |
| 77 | 109 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 78 | 110 | return { |
| ... | ... | @@ -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 | 162 | showActionButtonGroup: false, | ... | ... |
| ... | ... | @@ -31,7 +31,6 @@ |
| 31 | 31 | |
| 32 | 32 | const getDesign = computed(() => { |
| 33 | 33 | const { persetOption = {}, option } = props.config; |
| 34 | - | |
| 35 | 34 | const { |
| 36 | 35 | iconColor: persetIconColor, |
| 37 | 36 | unit: perseUnit, |
| ... | ... | @@ -44,7 +43,8 @@ |
| 44 | 43 | const { componentInfo, attributeRename } = option; |
| 45 | 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 | 48 | return { |
| 49 | 49 | iconColor: iconColor || persetIconColor, |
| 50 | 50 | unit: unit ?? perseUnit, |
| ... | ... | @@ -53,6 +53,8 @@ |
| 53 | 53 | attribute: attributeRename || functionName, |
| 54 | 54 | valueSize: valueSize || persetValueSize || 20, |
| 55 | 55 | fontSize: fontSize || persetFontSize || 14, |
| 56 | + defaultCustom: defaultCustom || 'default', | |
| 57 | + customIcon: customIcon || [], | |
| 56 | 58 | }; |
| 57 | 59 | }); |
| 58 | 60 | |
| ... | ... | @@ -78,11 +80,18 @@ |
| 78 | 80 | <DeviceName :config="config" /> |
| 79 | 81 | <div class="flex-1 flex justify-center items-center flex-col w-full"> |
| 80 | 82 | <SvgIcon |
| 83 | + v-if="getDesign.defaultCustom !== 'custom'" | |
| 81 | 84 | :name="getDesign.icon!" |
| 82 | 85 | prefix="iconfont" |
| 83 | 86 | :size="getRatio ? getRatio * 70 : 70" |
| 84 | 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 | 95 | <h1 |
| 87 | 96 | class="font-bold m-2 truncate w-full text-center" |
| 88 | 97 | :style="{ | ... | ... |
| ... | ... | @@ -3,6 +3,9 @@ |
| 3 | 3 | import { useForm, BasicForm } from '/@/components/Form'; |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 10 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 11 | schemas: [ |
| ... | ... | @@ -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 | 89 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 64 | 90 | label: '图标颜色', |
| 65 | 91 | component: 'ColorPicker', |
| 66 | 92 | changeEvent: 'update:value', |
| 67 | 93 | defaultValue: option.iconColor, |
| 94 | + ifShow: ({ model }) => { | |
| 95 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 96 | + }, | |
| 68 | 97 | }, |
| 69 | 98 | { |
| 70 | 99 | field: ComponentConfigFieldEnum.ICON, |
| ... | ... | @@ -72,6 +101,9 @@ |
| 72 | 101 | component: 'IconDrawer', |
| 73 | 102 | changeEvent: 'update:value', |
| 74 | 103 | defaultValue: option.icon, |
| 104 | + ifShow: ({ model }) => { | |
| 105 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 106 | + }, | |
| 75 | 107 | componentProps({ formModel }) { |
| 76 | 108 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 77 | 109 | return { |
| ... | ... | @@ -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 | 161 | showActionButtonGroup: false, | ... | ... |
| ... | ... | @@ -40,7 +40,8 @@ |
| 40 | 40 | |
| 41 | 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 | 45 | return { |
| 45 | 46 | iconColor: iconColor || persetIconColor, |
| 46 | 47 | unit: unit ?? perseUnit, |
| ... | ... | @@ -49,6 +50,8 @@ |
| 49 | 50 | attribute: attributeRename || unref(getThingModelTsl)?.functionName || attribute, |
| 50 | 51 | valueSize: valueSize || persetValueSize || 20, |
| 51 | 52 | fontSize: fontSize || persetFontSize || 14, |
| 53 | + defaultCustom: defaultCustom || 'default', | |
| 54 | + customIcon: customIcon || [], | |
| 52 | 55 | }; |
| 53 | 56 | }); |
| 54 | 57 | |
| ... | ... | @@ -73,11 +76,18 @@ |
| 73 | 76 | <DeviceName :config="config" /> |
| 74 | 77 | <div :style="getScale" class="flex-1 flex justify-center items-center flex-col w-full"> |
| 75 | 78 | <SvgIcon |
| 79 | + v-if="getDesign.defaultCustom !== 'custom'" | |
| 76 | 80 | :name="getDesign.icon!" |
| 77 | 81 | prefix="iconfont" |
| 78 | 82 | :size="getRatio ? getRatio * 70 : 70" |
| 79 | 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 | 91 | <h1 |
| 82 | 92 | class="my-4 font-bold !my-2 truncate w-full text-center" |
| 83 | 93 | :style="{ | ... | ... |
| ... | ... | @@ -4,6 +4,10 @@ |
| 4 | 4 | import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type'; |
| 5 | 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 | 11 | const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({ |
| 8 | 12 | schemas: [ |
| 9 | 13 | { |
| ... | ... | @@ -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 | 84 | field: ComponentConfigFieldEnum.ICON_COLOR, |
| 64 | 85 | label: '图标颜色', |
| 65 | 86 | component: 'ColorPicker', |
| 66 | 87 | changeEvent: 'update:value', |
| 67 | 88 | defaultValue: option.iconColor, |
| 89 | + ifShow: ({ model }) => { | |
| 90 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 91 | + }, | |
| 68 | 92 | }, |
| 69 | 93 | { |
| 70 | 94 | field: ComponentConfigFieldEnum.ICON, |
| ... | ... | @@ -73,6 +97,9 @@ |
| 73 | 97 | changeEvent: 'update:value', |
| 74 | 98 | valueField: 'value', |
| 75 | 99 | defaultValue: option.icon, |
| 100 | + ifShow: ({ model }) => { | |
| 101 | + return model[ComponentConfigFieldEnum.DEFAULT_CUSTOM] !== 'custom'; | |
| 102 | + }, | |
| 76 | 103 | componentProps({ formModel }) { |
| 77 | 104 | const color = formModel[ComponentConfigFieldEnum.ICON_COLOR]; |
| 78 | 105 | return { |
| ... | ... | @@ -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 | 156 | showActionButtonGroup: false, |
| 85 | 157 | labelWidth: 120, | ... | ... |
| ... | ... | @@ -35,7 +35,8 @@ |
| 35 | 35 | |
| 36 | 36 | return { |
| 37 | 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 | 40 | const { attribute, attributeRename, deviceName, deviceRename, deviceId, deviceProfileId } = |
| 40 | 41 | item; |
| 41 | 42 | const tsl = getDeviceProfileTslByIdWithIdentifier?.(deviceProfileId, attribute); |
| ... | ... | @@ -50,6 +51,8 @@ |
| 50 | 51 | id: deviceId, |
| 51 | 52 | valueSize: valueSize || persetValueSize || 20, |
| 52 | 53 | fontSize: fontSize || persetFontSize || 14, |
| 54 | + defaultCustom: defaultCustom || 'default', | |
| 55 | + customIcon: customIcon || [], | |
| 53 | 56 | }; |
| 54 | 57 | }), |
| 55 | 58 | }; |
| ... | ... | @@ -67,6 +70,8 @@ |
| 67 | 70 | fontColor: '#357CFB', |
| 68 | 71 | fontSize: 16, |
| 69 | 72 | valueSize: 16, |
| 73 | + defaultCustom: 'default', | |
| 74 | + customIcon: [], | |
| 70 | 75 | }, |
| 71 | 76 | { |
| 72 | 77 | id: buildUUID(), |
| ... | ... | @@ -79,6 +84,8 @@ |
| 79 | 84 | fontColor: '#FFA000', |
| 80 | 85 | fontSize: 16, |
| 81 | 86 | valueSize: 16, |
| 87 | + defaultCustom: 'default', | |
| 88 | + customIcon: [], | |
| 82 | 89 | }, |
| 83 | 90 | ]); |
| 84 | 91 | |
| ... | ... | @@ -114,11 +121,24 @@ |
| 114 | 121 | class="flex justify-between items-center mt-2" |
| 115 | 122 | > |
| 116 | 123 | <div class="flex items-center"> |
| 117 | - <SvgIcon | |
| 124 | + <!-- <SvgIcon | |
| 118 | 125 | :name="item.icon!" |
| 119 | 126 | prefix="iconfont" |
| 120 | 127 | :size="getRatio ? 30 * getRatio : 30" |
| 121 | 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 | 143 | <div |
| 124 | 144 | class="text-gray-500 ml-6" | ... | ... |
| ... | ... | @@ -36,4 +36,8 @@ export enum ComponentConfigFieldEnum { |
| 36 | 36 | MIN_NUMBER = 'minNumber', |
| 37 | 37 | MAX_NUMBER = 'maxNumber', |
| 38 | 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 | 20 | import { useGetComponentConfig } from '../../../packages/hook/useGetComponetConfig'; |
| 21 | 21 | import { isBoolean } from '/@/utils/is'; |
| 22 | 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 | 26 | const props = defineProps<{ |
| 25 | 27 | sourceInfo: WidgetDataType; |
| ... | ... | @@ -74,8 +76,56 @@ |
| 74 | 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 | 93 | async function handleDelete() { |
| 78 | 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 | 129 | await deleteDataComponent({ dataBoardId: unref(boardId), ids: [props.sourceInfo.id] }); |
| 80 | 130 | createMessage.success('删除成功'); |
| 81 | 131 | emit('ok'); | ... | ... |