Commit cf4ef9c76b556ad4872e878abd61b8c05806152f
1 parent
154ef642
feat: add operation ota page && dynamic set loading effect
Showing
13 changed files
with
662 additions
and
130 deletions
| @@ -22,7 +22,6 @@ | @@ -22,7 +22,6 @@ | ||
| 22 | theme = htmlRoot = null; | 22 | theme = htmlRoot = null; |
| 23 | } | 23 | } |
| 24 | })(); | 24 | })(); |
| 25 | - | ||
| 26 | </script> | 25 | </script> |
| 27 | <div id="app"> | 26 | <div id="app"> |
| 28 | <style> | 27 | <style> |
| @@ -152,17 +151,17 @@ | @@ -152,17 +151,17 @@ | ||
| 152 | } | 151 | } |
| 153 | } | 152 | } |
| 154 | </style> | 153 | </style> |
| 155 | - <div class="app-loading"> | 154 | + <div id="first-screen-loading" class="app-loading"> |
| 156 | <div class="app-loading-wrap"> | 155 | <div class="app-loading-wrap"> |
| 157 | - <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" /> | 156 | + <!-- <img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" /> --> |
| 158 | <div class="app-loading-dots"> | 157 | <div class="app-loading-dots"> |
| 159 | <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> | 158 | <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> |
| 160 | </div> | 159 | </div> |
| 161 | - <div class="app-loading-title"><%= title %></div> | 160 | + <!-- <div class="app-loading-title"><%= title %></div> --> |
| 162 | </div> | 161 | </div> |
| 163 | </div> | 162 | </div> |
| 164 | </div> | 163 | </div> |
| 165 | - | 164 | + |
| 166 | <script type="module" src="/src/main.ts"></script> | 165 | <script type="module" src="/src/main.ts"></script> |
| 167 | </body> | 166 | </body> |
| 168 | </html> | 167 | </html> |
| 1 | import { defHttp } from '/@/utils/http/axios'; | 1 | import { defHttp } from '/@/utils/http/axios'; |
| 2 | -import { FileUploadResponse } from './model/index'; | 2 | +import { FileUploadResponse, Platform } from './model/index'; |
| 3 | enum API { | 3 | enum API { |
| 4 | SELECT_DETAIL = '/enterprise/get', | 4 | SELECT_DETAIL = '/enterprise/get', |
| 5 | UPDATE_DETAIL = '/enterprise/update', | 5 | UPDATE_DETAIL = '/enterprise/update', |
| @@ -67,7 +67,7 @@ export const bgUpload = (file) => { | @@ -67,7 +67,7 @@ export const bgUpload = (file) => { | ||
| 67 | 67 | ||
| 68 | // 获取平台定制详情 | 68 | // 获取平台定制详情 |
| 69 | export const getPlatForm = () => { | 69 | export const getPlatForm = () => { |
| 70 | - return defHttp.get({ | 70 | + return defHttp.get<Platform>({ |
| 71 | url: API.SELECT_PLATFORM, | 71 | url: API.SELECT_PLATFORM, |
| 72 | }); | 72 | }); |
| 73 | }; | 73 | }; |
| @@ -5,3 +5,17 @@ export interface FileUploadResponse { | @@ -5,3 +5,17 @@ export interface FileUploadResponse { | ||
| 5 | size: number; | 5 | size: number; |
| 6 | fileStaticUri: string; | 6 | fileStaticUri: string; |
| 7 | } | 7 | } |
| 8 | + | ||
| 9 | +export interface Platform { | ||
| 10 | + id: string; | ||
| 11 | + creator: string; | ||
| 12 | + createTime: string; | ||
| 13 | + updater: string; | ||
| 14 | + updateTime: string; | ||
| 15 | + name: string; | ||
| 16 | + logo: string; | ||
| 17 | + background: string; | ||
| 18 | + copyright: string; | ||
| 19 | + presentedOurselves: string; | ||
| 20 | + domain: string; | ||
| 21 | +} |
| @@ -10,6 +10,7 @@ export { default as ApiSelect } from './src/components/ApiSelect.vue'; | @@ -10,6 +10,7 @@ export { default as ApiSelect } from './src/components/ApiSelect.vue'; | ||
| 10 | export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue'; | 10 | export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue'; |
| 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
| 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; | 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; |
| 13 | +export { default as ApiUpload } from './src/components/ApiUpload.vue'; | ||
| 13 | 14 | ||
| 14 | //注册自定义组件 | 15 | //注册自定义组件 |
| 15 | export { | 16 | export { |
| 1 | +<script lang="ts"> | ||
| 2 | + export default { | ||
| 3 | + inheritAttrs: false, | ||
| 4 | + }; | ||
| 5 | +</script> | ||
| 6 | +<script lang="ts" setup> | ||
| 7 | + import { UploadDragger, Spin } from 'ant-design-vue'; | ||
| 8 | + import { InboxOutlined } from '@ant-design/icons-vue'; | ||
| 9 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 10 | + import { isBoolean, isFunction } from '/@/utils/is'; | ||
| 11 | + import { computed, ref, unref } from 'vue'; | ||
| 12 | + import { cloneDeep } from 'lodash-es'; | ||
| 13 | + interface FileItem { | ||
| 14 | + uid: string; | ||
| 15 | + name?: string; | ||
| 16 | + status?: string; | ||
| 17 | + response?: string; | ||
| 18 | + url?: string; | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + const emit = defineEmits(['update:fileList']); | ||
| 22 | + | ||
| 23 | + const { createMessage } = useMessage(); | ||
| 24 | + | ||
| 25 | + const loading = ref(false); | ||
| 26 | + | ||
| 27 | + const componentDisabled = ref(false); | ||
| 28 | + | ||
| 29 | + const setLoading = (spin: boolean) => { | ||
| 30 | + loading.value = spin; | ||
| 31 | + }; | ||
| 32 | + | ||
| 33 | + const props = withDefaults( | ||
| 34 | + defineProps<{ | ||
| 35 | + fileList?: FileItem[]; | ||
| 36 | + accept?: string; | ||
| 37 | + maxSize?: number; | ||
| 38 | + disabled?: boolean; | ||
| 39 | + listType?: string; | ||
| 40 | + multiple?: boolean; | ||
| 41 | + showUploadList?: boolean | { showPreviewIcon?: boolean; showRemoveIcon?: boolean }; | ||
| 42 | + transformFile?: (file: File) => string | Blob | Promise<string | Blob | File>; | ||
| 43 | + api: (file: string | Blob | Promise<string | Blob | File>) => Promise<FileItem>; | ||
| 44 | + }>(), | ||
| 45 | + { | ||
| 46 | + fileList: () => [], | ||
| 47 | + maxSize: 5 * 1024 * 1024, | ||
| 48 | + showUploadList: () => ({ showPreviewIcon: true, showRemoveIcon: true }), | ||
| 49 | + } | ||
| 50 | + ); | ||
| 51 | + | ||
| 52 | + const handleBeforeUpload = (file: File) => { | ||
| 53 | + if (file.size > props.maxSize) { | ||
| 54 | + createMessage.warning(`文件大小超过${Math.floor(props.maxSize / 1024)}mb`); | ||
| 55 | + return false; | ||
| 56 | + } | ||
| 57 | + handleUpload(file); | ||
| 58 | + return false; | ||
| 59 | + }; | ||
| 60 | + | ||
| 61 | + const getDisabled = computed(() => { | ||
| 62 | + const { disabled } = props; | ||
| 63 | + if (isBoolean(disabled)) { | ||
| 64 | + return disabled ? componentDisabled.value : disabled; | ||
| 65 | + } | ||
| 66 | + return componentDisabled.value; | ||
| 67 | + }); | ||
| 68 | + | ||
| 69 | + const handleUpload = async (file: File | string | Blob | Promise<string | Blob | File>) => { | ||
| 70 | + try { | ||
| 71 | + setLoading(true); | ||
| 72 | + componentDisabled.value = true; | ||
| 73 | + console.log({ componentDisabled: unref(componentDisabled), getDisabled: unref(getDisabled) }); | ||
| 74 | + const { transformFile, api } = props; | ||
| 75 | + if (transformFile && isFunction(transformFile)) file = await transformFile(file as File); | ||
| 76 | + if (api && isFunction(api)) { | ||
| 77 | + const data = await api(file); | ||
| 78 | + emit('update:fileList', cloneDeep([...props.fileList, data])); | ||
| 79 | + } | ||
| 80 | + } catch (error) { | ||
| 81 | + window.console.error(error); | ||
| 82 | + } finally { | ||
| 83 | + setLoading(false); | ||
| 84 | + componentDisabled.value = false; | ||
| 85 | + } | ||
| 86 | + }; | ||
| 87 | + | ||
| 88 | + const handleRemove = (file: FileItem) => { | ||
| 89 | + const _fileList = cloneDeep(props.fileList); | ||
| 90 | + const index = _fileList.findIndex((item) => item.uid === file.uid); | ||
| 91 | + ~index && _fileList.splice(index, 1); | ||
| 92 | + emit('update:fileList', _fileList); | ||
| 93 | + }; | ||
| 94 | + | ||
| 95 | + const handlePreview = (file: FileItem) => { | ||
| 96 | + console.log('preview', file); | ||
| 97 | + }; | ||
| 98 | + | ||
| 99 | + const handleDownload = (file: FileItem) => { | ||
| 100 | + console.log('download', file); | ||
| 101 | + }; | ||
| 102 | +</script> | ||
| 103 | + | ||
| 104 | +<template> | ||
| 105 | + <UploadDragger | ||
| 106 | + :file-list="props.fileList" | ||
| 107 | + :disabled="getDisabled" | ||
| 108 | + :before-upload="handleBeforeUpload" | ||
| 109 | + @preview="handlePreview" | ||
| 110 | + @download="handleDownload" | ||
| 111 | + :remove="handleRemove" | ||
| 112 | + > | ||
| 113 | + <Spin :spinning="loading"> | ||
| 114 | + {{ getDisabled }} | ||
| 115 | + <div class="w-full h-full flex flex-col justify-center content-center"> | ||
| 116 | + <InboxOutlined class="text-[3rem] !text-blue-500" /> | ||
| 117 | + <div class="m-2 text-gray-400">点击上传或拖拽上传</div> | ||
| 118 | + </div> | ||
| 119 | + </Spin> | ||
| 120 | + </UploadDragger> | ||
| 121 | +</template> |
| @@ -14,6 +14,7 @@ import { setupGlobDirectives } from '/@/directives'; | @@ -14,6 +14,7 @@ import { setupGlobDirectives } from '/@/directives'; | ||
| 14 | import { setupI18n } from '/@/locales/setupI18n'; | 14 | import { setupI18n } from '/@/locales/setupI18n'; |
| 15 | import { registerGlobComp } from '/@/components/registerGlobComp'; | 15 | import { registerGlobComp } from '/@/components/registerGlobComp'; |
| 16 | import '/@/assets/iconfont/iconfont'; | 16 | import '/@/assets/iconfont/iconfont'; |
| 17 | +import { usePlatform } from './views/system/customize/hook/usePlatformInfo'; | ||
| 17 | 18 | ||
| 18 | if (import.meta.env.DEV) { | 19 | if (import.meta.env.DEV) { |
| 19 | import('ant-design-vue/dist/antd.less'); | 20 | import('ant-design-vue/dist/antd.less'); |
| @@ -23,6 +24,8 @@ async function bootstrap() { | @@ -23,6 +24,8 @@ async function bootstrap() { | ||
| 23 | // Configure store | 24 | // Configure store |
| 24 | setupStore(app); | 25 | setupStore(app); |
| 25 | 26 | ||
| 27 | + await usePlatform(); | ||
| 28 | + | ||
| 26 | // Initialize internal system configuration | 29 | // Initialize internal system configuration |
| 27 | initAppConfigStore(); | 30 | initAppConfigStore(); |
| 28 | 31 | ||
| @@ -47,6 +50,7 @@ async function bootstrap() { | @@ -47,6 +50,7 @@ async function bootstrap() { | ||
| 47 | // Mount when the route is ready | 50 | // Mount when the route is ready |
| 48 | // https://next.router.vuejs.org/api/#isready | 51 | // https://next.router.vuejs.org/api/#isready |
| 49 | await router.isReady(); | 52 | await router.isReady(); |
| 53 | + | ||
| 50 | app.mount('#app', true); | 54 | app.mount('#app', true); |
| 51 | } | 55 | } |
| 52 | 56 |
| @@ -39,7 +39,7 @@ | @@ -39,7 +39,7 @@ | ||
| 39 | width: '200px', | 39 | width: '200px', |
| 40 | height: '200px', | 40 | height: '200px', |
| 41 | color: '#409eff', | 41 | color: '#409eff', |
| 42 | - muted: false, //静音 | 42 | + muted: true, //静音 |
| 43 | webFullScreen: false, | 43 | webFullScreen: false, |
| 44 | autoPlay: true, //自动播放 | 44 | autoPlay: true, //自动播放 |
| 45 | currentTime: 0, | 45 | currentTime: 0, |
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
| 3 | + import { BasicForm, useForm } from '/@/components/Form'; | ||
| 4 | + import { formSchema } from '../config/packageDetail.config'; | ||
| 5 | + defineEmits(['register']); | ||
| 6 | + | ||
| 7 | + const [registerModal] = useModalInner((_record: Recordable) => {}); | ||
| 8 | + | ||
| 9 | + const [registerForm] = useForm({ | ||
| 10 | + schemas: formSchema, | ||
| 11 | + showActionButtonGroup: false, | ||
| 12 | + // labelCol: { span: 8 }, | ||
| 13 | + labelWidth: 100, | ||
| 14 | + wrapperCol: { span: 16 }, | ||
| 15 | + }); | ||
| 16 | +</script> | ||
| 17 | + | ||
| 18 | +<template> | ||
| 19 | + <BasicModal title="包管理" @register="registerModal"> | ||
| 20 | + <BasicForm @register="registerForm" /> | ||
| 21 | + </BasicModal> | ||
| 22 | +</template> |
src/views/operation/ota/config/config.ts
0 → 100644
| 1 | +import { BasicColumn, FormSchema } from '/@/components/Table'; | ||
| 2 | + | ||
| 3 | +export const columns: BasicColumn[] = [ | ||
| 4 | + { | ||
| 5 | + title: '创建时间', | ||
| 6 | + dataIndex: 'createTime', | ||
| 7 | + width: 120, | ||
| 8 | + }, | ||
| 9 | + { | ||
| 10 | + title: '标题', | ||
| 11 | + dataIndex: 'title', | ||
| 12 | + width: 120, | ||
| 13 | + }, | ||
| 14 | + { | ||
| 15 | + title: '版本', | ||
| 16 | + dataIndex: 'version', | ||
| 17 | + width: 120, | ||
| 18 | + }, | ||
| 19 | + { | ||
| 20 | + title: '版本标签', | ||
| 21 | + dataIndex: 'versionLabel', | ||
| 22 | + width: 120, | ||
| 23 | + }, | ||
| 24 | + { | ||
| 25 | + title: '包类型', | ||
| 26 | + dataIndex: 'pkgType', | ||
| 27 | + width: 120, | ||
| 28 | + }, | ||
| 29 | + { | ||
| 30 | + title: '直接URL', | ||
| 31 | + dataIndex: 'url', | ||
| 32 | + width: 120, | ||
| 33 | + }, | ||
| 34 | + { | ||
| 35 | + title: '文件大小', | ||
| 36 | + dataIndex: 'fileSize', | ||
| 37 | + width: 120, | ||
| 38 | + }, | ||
| 39 | + { | ||
| 40 | + title: '校验和', | ||
| 41 | + dataIndex: 'vaildateTotal', | ||
| 42 | + width: 120, | ||
| 43 | + }, | ||
| 44 | +]; | ||
| 45 | + | ||
| 46 | +export const searchFormSchema: FormSchema[] = [ | ||
| 47 | + { | ||
| 48 | + field: 'name', | ||
| 49 | + label: '标题', | ||
| 50 | + component: 'Input', | ||
| 51 | + colProps: { span: 8 }, | ||
| 52 | + }, | ||
| 53 | +]; |
| 1 | +import { FormSchema } from '/@/components/Form'; | ||
| 2 | + | ||
| 3 | +export enum PackageField { | ||
| 4 | + TITLE = 'title', | ||
| 5 | + VERSION = 'version', | ||
| 6 | + VERSION_LABEL = 'versionLabel', | ||
| 7 | + DEVICE_CONFIGURATION = 'deviceConfiguration', | ||
| 8 | + PACKAGE_TYPE = 'packageType', | ||
| 9 | + PACKAGE_UPDATE_TYPE = 'PackageUpdateType', | ||
| 10 | + PACKAGE_BINARY_FILE = 'packageBinaryFile', | ||
| 11 | + PACKAGE_EXTERNAL_URL = 'packageEexternalUrl', | ||
| 12 | + CHECK_SUM_WAY = 'checkSumWay', | ||
| 13 | + ALG = 'alg', | ||
| 14 | + CHECK_SUM = 'checkSum', | ||
| 15 | + DESCRIPTION = 'description', | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +export enum PackageUpdateType { | ||
| 19 | + BINARY_FILE = 'binaryFile', | ||
| 20 | + EXTERNAL_URL = 'externalUrl', | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +export enum PackageType { | ||
| 24 | + FIRMWARE = 'firmware', | ||
| 25 | + SOFTWARE = 'software', | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +export enum CheckSumWay { | ||
| 29 | + AUTO = 'auto', | ||
| 30 | + MANUAL = 'manual', | ||
| 31 | +} | ||
| 32 | + | ||
| 33 | +export enum ALG { | ||
| 34 | + MD5 = 'md5', | ||
| 35 | + SHA_256 = 'sha-256', | ||
| 36 | + SHA_384 = 'sha-384', | ||
| 37 | + SHA_512 = 'sha-512', | ||
| 38 | + CRC_32 = 'crc-32', | ||
| 39 | + MURMUR3_32 = 'murmur3-32', | ||
| 40 | + MURMUR3_128 = 'murmur3-128', | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +export const formSchema: FormSchema[] = [ | ||
| 44 | + { | ||
| 45 | + field: PackageField.TITLE, | ||
| 46 | + label: '标题', | ||
| 47 | + component: 'Input', | ||
| 48 | + rules: [{ required: true, message: '标题为必填项' }], | ||
| 49 | + componentProps: { | ||
| 50 | + placeholder: '请输入标题', | ||
| 51 | + }, | ||
| 52 | + }, | ||
| 53 | + { | ||
| 54 | + field: PackageField.VERSION, | ||
| 55 | + label: '版本', | ||
| 56 | + component: 'Input', | ||
| 57 | + rules: [{ required: true, message: '版本为必填项' }], | ||
| 58 | + componentProps: { | ||
| 59 | + placeholder: '请输入版本', | ||
| 60 | + }, | ||
| 61 | + }, | ||
| 62 | + { | ||
| 63 | + field: PackageField.VERSION_LABEL, | ||
| 64 | + label: '版本标签', | ||
| 65 | + component: 'Input', | ||
| 66 | + helpMessage: ['自定义标签应与您设备报告的软件包版本相匹配'], | ||
| 67 | + componentProps: { | ||
| 68 | + placeholder: '请输入版本标签', | ||
| 69 | + }, | ||
| 70 | + }, | ||
| 71 | + { | ||
| 72 | + field: PackageField.DEVICE_CONFIGURATION, | ||
| 73 | + label: '设备配置', | ||
| 74 | + component: 'Select', | ||
| 75 | + helpMessage: ['上传的包仅适用于具有所选配置文件的设备'], | ||
| 76 | + defaultValue: 'default', | ||
| 77 | + rules: [{ required: true, message: '设备配置为必填项' }], | ||
| 78 | + componentProps: () => { | ||
| 79 | + return { | ||
| 80 | + options: [{ label: 'default', value: 'default' }], | ||
| 81 | + placeholder: '请选择设备配置', | ||
| 82 | + }; | ||
| 83 | + }, | ||
| 84 | + }, | ||
| 85 | + { | ||
| 86 | + field: PackageField.PACKAGE_TYPE, | ||
| 87 | + label: '包类型', | ||
| 88 | + component: 'Select', | ||
| 89 | + helpMessage: ['上传包后,您将无法修改标题、版本、设备配置文件和包类型'], | ||
| 90 | + defaultValue: PackageType.FIRMWARE, | ||
| 91 | + rules: [{ required: true, message: '包类型为必填项' }], | ||
| 92 | + componentProps: () => { | ||
| 93 | + return { | ||
| 94 | + options: [ | ||
| 95 | + { label: '固件', value: PackageType.FIRMWARE }, | ||
| 96 | + { label: '软件', value: PackageType.SOFTWARE }, | ||
| 97 | + ], | ||
| 98 | + placeholder: '请选择设备配置', | ||
| 99 | + }; | ||
| 100 | + }, | ||
| 101 | + }, | ||
| 102 | + { | ||
| 103 | + field: PackageField.PACKAGE_UPDATE_TYPE, | ||
| 104 | + label: '上传方式', | ||
| 105 | + component: 'RadioGroup', | ||
| 106 | + defaultValue: PackageUpdateType.BINARY_FILE, | ||
| 107 | + componentProps: () => { | ||
| 108 | + return { | ||
| 109 | + options: [ | ||
| 110 | + { label: '上传二进制文件', value: PackageUpdateType.BINARY_FILE }, | ||
| 111 | + { label: '使用外部URL', value: PackageUpdateType.EXTERNAL_URL }, | ||
| 112 | + ], | ||
| 113 | + }; | ||
| 114 | + }, | ||
| 115 | + }, | ||
| 116 | + { | ||
| 117 | + field: PackageField.PACKAGE_BINARY_FILE, | ||
| 118 | + label: '二进制文件', | ||
| 119 | + ifShow: ({ model }) => { | ||
| 120 | + return model[PackageField.PACKAGE_UPDATE_TYPE] === PackageUpdateType.BINARY_FILE; | ||
| 121 | + }, | ||
| 122 | + component: 'Upload', | ||
| 123 | + componentProps: { | ||
| 124 | + api: () => { | ||
| 125 | + return {}; | ||
| 126 | + }, | ||
| 127 | + }, | ||
| 128 | + }, | ||
| 129 | + { | ||
| 130 | + field: PackageField.PACKAGE_EXTERNAL_URL, | ||
| 131 | + label: '外部URL', | ||
| 132 | + component: 'Input', | ||
| 133 | + ifShow: ({ model }) => { | ||
| 134 | + return model[PackageField.PACKAGE_UPDATE_TYPE] === PackageUpdateType.EXTERNAL_URL; | ||
| 135 | + }, | ||
| 136 | + rules: [{ required: true, message: '外部URL为必填项' }], | ||
| 137 | + componentProps: { | ||
| 138 | + placeholder: '请输入外部URL', | ||
| 139 | + }, | ||
| 140 | + }, | ||
| 141 | + { | ||
| 142 | + field: PackageField.CHECK_SUM_WAY, | ||
| 143 | + label: '校验和方式', | ||
| 144 | + component: 'RadioGroup', | ||
| 145 | + defaultValue: CheckSumWay.AUTO, | ||
| 146 | + componentProps: () => { | ||
| 147 | + return { | ||
| 148 | + options: [ | ||
| 149 | + { label: '自动生成', value: CheckSumWay.AUTO }, | ||
| 150 | + { label: '手动生成', value: CheckSumWay.MANUAL }, | ||
| 151 | + ], | ||
| 152 | + }; | ||
| 153 | + }, | ||
| 154 | + }, | ||
| 155 | + { | ||
| 156 | + field: PackageField.ALG, | ||
| 157 | + label: '校验和算法', | ||
| 158 | + component: 'Select', | ||
| 159 | + ifShow: ({ model }) => { | ||
| 160 | + return model[PackageField.CHECK_SUM_WAY] === CheckSumWay.MANUAL; | ||
| 161 | + }, | ||
| 162 | + componentProps: { | ||
| 163 | + placeholder: '请选择校验和算法', | ||
| 164 | + options: Object.keys(ALG).map((key) => { | ||
| 165 | + return { | ||
| 166 | + label: String(ALG[key]).toUpperCase(), | ||
| 167 | + value: ALG[key], | ||
| 168 | + }; | ||
| 169 | + }), | ||
| 170 | + }, | ||
| 171 | + }, | ||
| 172 | + { | ||
| 173 | + field: PackageField.CHECK_SUM, | ||
| 174 | + label: '校验和', | ||
| 175 | + component: 'Input', | ||
| 176 | + ifShow: ({ model }) => { | ||
| 177 | + return model[PackageField.CHECK_SUM_WAY] === CheckSumWay.MANUAL; | ||
| 178 | + }, | ||
| 179 | + helpMessage: ['如果校验和为空,会自动生成'], | ||
| 180 | + componentProps: { | ||
| 181 | + placeholder: '请输入校验和', | ||
| 182 | + }, | ||
| 183 | + }, | ||
| 184 | + { | ||
| 185 | + field: PackageField.DESCRIPTION, | ||
| 186 | + label: '描述', | ||
| 187 | + component: 'InputTextArea', | ||
| 188 | + componentProps: { | ||
| 189 | + placeholder: '请输入描述', | ||
| 190 | + }, | ||
| 191 | + }, | ||
| 192 | +]; |
src/views/operation/ota/index.vue
0 → 100644
| 1 | +<script lang="ts" setup> | ||
| 2 | + import { Button } from 'ant-design-vue'; | ||
| 3 | + import { columns, searchFormSchema } from './config/config'; | ||
| 4 | + import { PageWrapper } from '/@/components/Page'; | ||
| 5 | + import { BasicTable, useTable } from '/@/components/Table'; | ||
| 6 | + import PackageDetailModal from './components/PackageDetailModal.vue'; | ||
| 7 | + import { useModal } from '/@/components/Modal'; | ||
| 8 | + // import { ApiUpload } from '/@/components/Form'; | ||
| 9 | + // import { computed, ref, unref } from 'vue'; | ||
| 10 | + | ||
| 11 | + const [register] = useTable({ | ||
| 12 | + columns, | ||
| 13 | + title: '包仓库', | ||
| 14 | + formConfig: { | ||
| 15 | + labelWidth: 120, | ||
| 16 | + schemas: searchFormSchema, | ||
| 17 | + }, | ||
| 18 | + useSearchForm: true, | ||
| 19 | + showTableSetting: true, | ||
| 20 | + }); | ||
| 21 | + | ||
| 22 | + const [registerModal, { openModal }] = useModal(); | ||
| 23 | + | ||
| 24 | + const handleCreatePackage = () => { | ||
| 25 | + openModal(true); | ||
| 26 | + }; | ||
| 27 | + | ||
| 28 | + // const fileList = ref([]); | ||
| 29 | + // const handleUpload = async (file: File) => { | ||
| 30 | + // console.log(file); | ||
| 31 | + // return new Promise((resolve) => { | ||
| 32 | + // setTimeout(() => { | ||
| 33 | + // resolve({ | ||
| 34 | + // uid: file.uid, | ||
| 35 | + // type: file.type, | ||
| 36 | + // name: file.name, | ||
| 37 | + // linkProps: { download: 'http://www.baidu.cn' }, | ||
| 38 | + // }); | ||
| 39 | + // }, 3000); | ||
| 40 | + // }); | ||
| 41 | + // }; | ||
| 42 | +</script> | ||
| 43 | + | ||
| 44 | +<template> | ||
| 45 | + <PageWrapper dense contentFullHeight contentClass="flex flex-col"> | ||
| 46 | + <!-- <div class="w-40 h-40"> | ||
| 47 | + <ApiUpload v-model:file-list="fileList" :api="handleUpload" /> | ||
| 48 | + </div> --> | ||
| 49 | + <BasicTable @register="register"> | ||
| 50 | + <template #toolbar> | ||
| 51 | + <Button @click="handleCreatePackage" type="primary">新增包</Button> | ||
| 52 | + </template> | ||
| 53 | + </BasicTable> | ||
| 54 | + <PackageDetailModal @register="registerModal" /> | ||
| 55 | + </PageWrapper> | ||
| 56 | +</template> |
| @@ -6,149 +6,161 @@ | @@ -6,149 +6,161 @@ | ||
| 6 | <a-button type="primary" @click="handleAdd"> 新增场景联动 </a-button> | 6 | <a-button type="primary" @click="handleAdd"> 新增场景联动 </a-button> |
| 7 | </Authority> | 7 | </Authority> |
| 8 | <Authority value="api:yt:sceneLinkage:delete"> | 8 | <Authority value="api:yt:sceneLinkage:delete"> |
| 9 | - <Popconfirm title="您确定要批量删除数据" ok-text="确定" cancel-text="取消" @confirm="handleDeleteOrBatchDelete(null)"> | 9 | + <Popconfirm |
| 10 | + title="您确定要批量删除数据" | ||
| 11 | + ok-text="确定" | ||
| 12 | + cancel-text="取消" | ||
| 13 | + @confirm="handleDeleteOrBatchDelete(null)" | ||
| 14 | + > | ||
| 10 | <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> | 15 | <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> |
| 11 | </Popconfirm> | 16 | </Popconfirm> |
| 12 | </Authority> | 17 | </Authority> |
| 13 | </template> | 18 | </template> |
| 14 | <template #action="{ record }"> | 19 | <template #action="{ record }"> |
| 15 | - <TableAction :actions="[ | ||
| 16 | - { | ||
| 17 | - label: '查看', | ||
| 18 | - auth: 'api:yt:sceneLinkage:get', | ||
| 19 | - icon: 'ant-design:eye-outlined', | ||
| 20 | - onClick: handleView.bind(null, record), | ||
| 21 | - }, | ||
| 22 | - { | ||
| 23 | - label: '编辑', | ||
| 24 | - auth: 'api:yt:sceneLinkage:update', | ||
| 25 | - icon: 'clarity:note-edit-line', | ||
| 26 | - onClick: handleEdit.bind(null, record), | ||
| 27 | - ifShow: record.creator === userId && record.status !== 1, | ||
| 28 | - }, | ||
| 29 | - { | ||
| 30 | - label: '删除', | ||
| 31 | - auth: 'api:yt:sceneLinkage:delete', | ||
| 32 | - icon: 'ant-design:delete-outlined', | ||
| 33 | - color: 'error', | ||
| 34 | - ifShow: record.creator === userId && record.status !== 1, | ||
| 35 | - popConfirm: { | ||
| 36 | - title: '是否确认删除', | ||
| 37 | - confirm: handleDeleteOrBatchDelete.bind(null, record), | 20 | + <TableAction |
| 21 | + :actions="[ | ||
| 22 | + { | ||
| 23 | + label: '查看', | ||
| 24 | + auth: 'api:yt:sceneLinkage:get', | ||
| 25 | + icon: 'ant-design:eye-outlined', | ||
| 26 | + onClick: handleView.bind(null, record), | ||
| 38 | }, | 27 | }, |
| 39 | - }, | ||
| 40 | - ]" /> | 28 | + { |
| 29 | + label: '编辑', | ||
| 30 | + auth: 'api:yt:sceneLinkage:update', | ||
| 31 | + icon: 'clarity:note-edit-line', | ||
| 32 | + onClick: handleEdit.bind(null, record), | ||
| 33 | + ifShow: record.creator === userId && record.status !== 1, | ||
| 34 | + }, | ||
| 35 | + { | ||
| 36 | + label: '删除', | ||
| 37 | + auth: 'api:yt:sceneLinkage:delete', | ||
| 38 | + icon: 'ant-design:delete-outlined', | ||
| 39 | + color: 'error', | ||
| 40 | + ifShow: record.creator === userId && record.status !== 1, | ||
| 41 | + popConfirm: { | ||
| 42 | + title: '是否确认删除', | ||
| 43 | + confirm: handleDeleteOrBatchDelete.bind(null, record), | ||
| 44 | + }, | ||
| 45 | + }, | ||
| 46 | + ]" | ||
| 47 | + /> | ||
| 41 | </template> | 48 | </template> |
| 42 | 49 | ||
| 43 | <template #status="{ record }"> | 50 | <template #status="{ record }"> |
| 44 | - <Switch :checked="record.status === 1" :loading="record.pendingStatus" checkedChildren="启用" | ||
| 45 | - unCheckedChildren="禁用" @change="(checked:boolean)=>statusChange(checked,record)" /> | 51 | + <Switch |
| 52 | + :checked="record.status === 1" | ||
| 53 | + :loading="record.pendingStatus" | ||
| 54 | + checkedChildren="启用" | ||
| 55 | + unCheckedChildren="禁用" | ||
| 56 | + @change="(checked:boolean)=>statusChange(checked,record)" | ||
| 57 | + /> | ||
| 46 | </template> | 58 | </template> |
| 47 | </BasicTable> | 59 | </BasicTable> |
| 48 | <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" /> | 60 | <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" /> |
| 49 | </div> | 61 | </div> |
| 50 | </template> | 62 | </template> |
| 51 | <script lang="ts" setup> | 63 | <script lang="ts" setup> |
| 52 | -import { nextTick } from 'vue'; | ||
| 53 | -import { BasicTable, useTable, TableAction } from '/@/components/Table'; | ||
| 54 | -import { useDrawer } from '/@/components/Drawer'; | ||
| 55 | -import { | ||
| 56 | - screenLinkPageGetApi, | ||
| 57 | - screenLinkPageDeleteApi, | ||
| 58 | - screenLinkPagePutApi, | ||
| 59 | -} from '/@/api/ruleengine/ruleengineApi'; | ||
| 60 | -import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; | ||
| 61 | -import { Switch, Popconfirm } from 'ant-design-vue'; | ||
| 62 | -import { columns, searchFormSchema } from './config/config.data.ts'; | ||
| 63 | -import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | ||
| 64 | -import { getAuthCache } from '/@/utils/auth'; | ||
| 65 | -import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue'; | ||
| 66 | -import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 67 | -import { Authority } from '/@/components/Authority'; | ||
| 68 | - | ||
| 69 | -const userInfo: any = getAuthCache(USER_INFO_KEY); | ||
| 70 | -const userId = userInfo.userId; | 64 | + import { nextTick } from 'vue'; |
| 65 | + import { BasicTable, useTable, TableAction } from '/@/components/Table'; | ||
| 66 | + import { useDrawer } from '/@/components/Drawer'; | ||
| 67 | + import { | ||
| 68 | + screenLinkPageGetApi, | ||
| 69 | + screenLinkPageDeleteApi, | ||
| 70 | + screenLinkPagePutApi, | ||
| 71 | + } from '/@/api/ruleengine/ruleengineApi'; | ||
| 72 | + import { useBatchDelete } from '/@/hooks/web/useBatchDelete'; | ||
| 73 | + import { Switch, Popconfirm } from 'ant-design-vue'; | ||
| 74 | + import { columns, searchFormSchema } from './config/config.data'; | ||
| 75 | + import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | ||
| 76 | + import { getAuthCache } from '/@/utils/auth'; | ||
| 77 | + import SceneLinkAgeDrawer from './SceneLinkAgeDrawer.vue'; | ||
| 78 | + import { useMessage } from '/@/hooks/web/useMessage'; | ||
| 79 | + import { Authority } from '/@/components/Authority'; | ||
| 71 | 80 | ||
| 72 | -const [registerDrawer, { openDrawer }] = useDrawer(); | ||
| 73 | -const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({ | ||
| 74 | - title: '场景联动列表', | ||
| 75 | - api: screenLinkPageGetApi, | ||
| 76 | - columns, | ||
| 77 | - formConfig: { | ||
| 78 | - labelWidth: 120, | ||
| 79 | - schemas: searchFormSchema, | ||
| 80 | - }, | ||
| 81 | - useSearchForm: true, | ||
| 82 | - showTableSetting: true, | ||
| 83 | - bordered: true, | ||
| 84 | - showIndexColumn: false, | ||
| 85 | - actionColumn: { | ||
| 86 | - width: 200, | ||
| 87 | - title: '操作', | ||
| 88 | - dataIndex: 'action', | ||
| 89 | - slots: { customRender: 'action' }, | ||
| 90 | - fixed: 'right', | ||
| 91 | - }, | ||
| 92 | -}); | ||
| 93 | -const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } = | ||
| 94 | - useBatchDelete(screenLinkPageDeleteApi, handleSuccess, setProps); | ||
| 95 | -selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { | ||
| 96 | - // Demo:status为1的选择框禁用 | ||
| 97 | - if (record.status === 1) { | ||
| 98 | - return { disabled: true }; | ||
| 99 | - } else { | ||
| 100 | - return { disabled: false }; | ||
| 101 | - } | ||
| 102 | -}; | ||
| 103 | -nextTick(() => { | ||
| 104 | - setProps(selectionOptions); | ||
| 105 | -}); | ||
| 106 | - | ||
| 107 | -function handleAdd() { | ||
| 108 | - window.localStorage.setItem('isViewDisabledBtn', 'noView') | ||
| 109 | - openDrawer(true, { | ||
| 110 | - isUpdate: false, | ||
| 111 | - }); | ||
| 112 | -} | 81 | + const userInfo: any = getAuthCache(USER_INFO_KEY); |
| 82 | + const userId = userInfo.userId; | ||
| 113 | 83 | ||
| 114 | -function handleEdit(record: Recordable) { | ||
| 115 | - window.localStorage.setItem('isViewDisabledBtn', 'noView') | ||
| 116 | - openDrawer(true, { | ||
| 117 | - record, | ||
| 118 | - isUpdate: true, | 84 | + const [registerDrawer, { openDrawer }] = useDrawer(); |
| 85 | + const [registerTable, { reload, setProps, setSelectedRowKeys }] = useTable({ | ||
| 86 | + title: '场景联动列表', | ||
| 87 | + api: screenLinkPageGetApi, | ||
| 88 | + columns, | ||
| 89 | + formConfig: { | ||
| 90 | + labelWidth: 120, | ||
| 91 | + schemas: searchFormSchema, | ||
| 92 | + }, | ||
| 93 | + useSearchForm: true, | ||
| 94 | + showTableSetting: true, | ||
| 95 | + bordered: true, | ||
| 96 | + showIndexColumn: false, | ||
| 97 | + actionColumn: { | ||
| 98 | + width: 200, | ||
| 99 | + title: '操作', | ||
| 100 | + dataIndex: 'action', | ||
| 101 | + slots: { customRender: 'action' }, | ||
| 102 | + fixed: 'right', | ||
| 103 | + }, | ||
| 119 | }); | 104 | }); |
| 120 | -} | ||
| 121 | -function handleView(record: Recordable) { | ||
| 122 | - window.localStorage.setItem('isViewDisabledBtn', 'isView') | ||
| 123 | - openDrawer(true, { | ||
| 124 | - record, | ||
| 125 | - isUpdate: 3, | ||
| 126 | - }); | ||
| 127 | -} | ||
| 128 | -function handleSuccess() { | ||
| 129 | - reload(); | ||
| 130 | -} | ||
| 131 | - | ||
| 132 | -const statusChange = async (checked, record) => { | ||
| 133 | - setProps({ | ||
| 134 | - loading: true, | ||
| 135 | - }); | ||
| 136 | - setSelectedRowKeys([]); | ||
| 137 | - resetSelectedRowKeys(); | ||
| 138 | - const newStatus = checked ? 1 : 0; | ||
| 139 | - const { createMessage } = useMessage(); | ||
| 140 | - try { | ||
| 141 | - await screenLinkPagePutApi({ id: record.id, status: newStatus }); | ||
| 142 | - if (newStatus) { | ||
| 143 | - createMessage.success(`启用成功`); | 105 | + const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } = |
| 106 | + useBatchDelete(screenLinkPageDeleteApi, handleSuccess, setProps); | ||
| 107 | + selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => { | ||
| 108 | + // Demo:status为1的选择框禁用 | ||
| 109 | + if (record.status === 1) { | ||
| 110 | + return { disabled: true }; | ||
| 144 | } else { | 111 | } else { |
| 145 | - createMessage.success('禁用成功'); | 112 | + return { disabled: false }; |
| 146 | } | 113 | } |
| 147 | - } finally { | ||
| 148 | - setProps({ | ||
| 149 | - loading: false, | 114 | + }; |
| 115 | + nextTick(() => { | ||
| 116 | + setProps(selectionOptions); | ||
| 117 | + }); | ||
| 118 | + | ||
| 119 | + function handleAdd() { | ||
| 120 | + window.localStorage.setItem('isViewDisabledBtn', 'noView'); | ||
| 121 | + openDrawer(true, { | ||
| 122 | + isUpdate: false, | ||
| 123 | + }); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + function handleEdit(record: Recordable) { | ||
| 127 | + window.localStorage.setItem('isViewDisabledBtn', 'noView'); | ||
| 128 | + openDrawer(true, { | ||
| 129 | + record, | ||
| 130 | + isUpdate: true, | ||
| 131 | + }); | ||
| 132 | + } | ||
| 133 | + function handleView(record: Recordable) { | ||
| 134 | + window.localStorage.setItem('isViewDisabledBtn', 'isView'); | ||
| 135 | + openDrawer(true, { | ||
| 136 | + record, | ||
| 137 | + isUpdate: 3, | ||
| 150 | }); | 138 | }); |
| 139 | + } | ||
| 140 | + function handleSuccess() { | ||
| 151 | reload(); | 141 | reload(); |
| 152 | } | 142 | } |
| 153 | -}; | 143 | + |
| 144 | + const statusChange = async (checked, record) => { | ||
| 145 | + setProps({ | ||
| 146 | + loading: true, | ||
| 147 | + }); | ||
| 148 | + setSelectedRowKeys([]); | ||
| 149 | + resetSelectedRowKeys(); | ||
| 150 | + const newStatus = checked ? 1 : 0; | ||
| 151 | + const { createMessage } = useMessage(); | ||
| 152 | + try { | ||
| 153 | + await screenLinkPagePutApi({ id: record.id, status: newStatus }); | ||
| 154 | + if (newStatus) { | ||
| 155 | + createMessage.success(`启用成功`); | ||
| 156 | + } else { | ||
| 157 | + createMessage.success('禁用成功'); | ||
| 158 | + } | ||
| 159 | + } finally { | ||
| 160 | + setProps({ | ||
| 161 | + loading: false, | ||
| 162 | + }); | ||
| 163 | + reload(); | ||
| 164 | + } | ||
| 165 | + }; | ||
| 154 | </script> | 166 | </script> |
| 1 | +import { getPlatForm } from '/@/api/oem'; | ||
| 2 | + | ||
| 3 | +enum DefaultPlatform { | ||
| 4 | + LOGO = '/resource/img/logo.png', | ||
| 5 | + // TITLE = 'ThingsKit', | ||
| 6 | + ICO = '/favicon.ico', | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +export const usePlatform = async () => { | ||
| 10 | + const platformInfo = await getPlatForm(); | ||
| 11 | + | ||
| 12 | + const createLoadingEffect = () => { | ||
| 13 | + const wrap = document.createElement('div'); | ||
| 14 | + wrap.setAttribute('class', 'app-loading-wrap'); | ||
| 15 | + const img = document.createElement('img'); | ||
| 16 | + img.setAttribute('src', platformInfo.logo || DefaultPlatform.LOGO); | ||
| 17 | + img.setAttribute('class', 'app-loading-logo'); | ||
| 18 | + img.setAttribute('alt', 'Logo'); | ||
| 19 | + const dots = document.createElement('div'); | ||
| 20 | + dots.setAttribute('class', 'app-loading-dots'); | ||
| 21 | + const dotWrap = document.createElement('span'); | ||
| 22 | + dotWrap.setAttribute('class', 'dot dot-spin'); | ||
| 23 | + for (let i = 0; i < 4; i++) { | ||
| 24 | + dotWrap.appendChild(document.createElement('i')); | ||
| 25 | + } | ||
| 26 | + const title = document.createElement('div'); | ||
| 27 | + title.setAttribute('class', 'app-loading-title'); | ||
| 28 | + const textNode = document.createTextNode( | ||
| 29 | + platformInfo.name || import.meta.env.VITE_GLOB_APP_TITLE | ||
| 30 | + ); | ||
| 31 | + title.appendChild(textNode); | ||
| 32 | + wrap.appendChild(img); | ||
| 33 | + dots.appendChild(dotWrap); | ||
| 34 | + wrap.appendChild(dots); | ||
| 35 | + wrap.appendChild(title); | ||
| 36 | + return wrap; | ||
| 37 | + }; | ||
| 38 | + | ||
| 39 | + const replaceSiteIco = () => { | ||
| 40 | + const linkEl = document.querySelectorAll('link[rel*="icon"]'); | ||
| 41 | + linkEl.forEach((item) => { | ||
| 42 | + item.setAttribute('href', platformInfo.logo || DefaultPlatform.ICO); | ||
| 43 | + }); | ||
| 44 | + }; | ||
| 45 | + | ||
| 46 | + const replaceLoadingEffect = () => { | ||
| 47 | + const loadingWrapper = document.getElementById('first-screen-loading'); | ||
| 48 | + loadingWrapper!.innerHTML = ''; | ||
| 49 | + loadingWrapper?.appendChild(createLoadingEffect()); | ||
| 50 | + }; | ||
| 51 | + | ||
| 52 | + const bootstrap = () => { | ||
| 53 | + replaceSiteIco(); | ||
| 54 | + replaceLoadingEffect(); | ||
| 55 | + }; | ||
| 56 | + | ||
| 57 | + bootstrap(); | ||
| 58 | +}; |