Commit 23345caceb9ee9437fe85377c5af6a153d339d8b
Merge branch 'ww' into 'main'
feat: add operation ota page && dynamic set loading effect See merge request huang/yun-teng-iot-front!360
Showing
15 changed files
with
675 additions
and
131 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, | 
| @@ -238,7 +238,7 @@ | @@ -238,7 +238,7 @@ | ||
| 238 | class="h-full flex flex-col justify-center items-center" | 238 | class="h-full flex flex-col justify-center items-center" | 
| 239 | v-if="!cameraList.length" | 239 | v-if="!cameraList.length" | 
| 240 | /> | 240 | /> | 
| 241 | - <Row :gutter="16" class="h-full mx-0"> | 241 | + <Row :gutter="16" class="h-full mx-0 content-start"> | 
| 242 | <Col | 242 | <Col | 
| 243 | v-for="item in cameraList" | 243 | v-for="item in cameraList" | 
| 244 | :key="item.id" | 244 | :key="item.id" | 
| 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 | +}; | 
| @@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
| 12 | 12 | ||
| 13 | const isEdit = ref(false); | 13 | const isEdit = ref(false); | 
| 14 | const recordId = ref<Nullable<string>>(null); | 14 | const recordId = ref<Nullable<string>>(null); | 
| 15 | + const loading = ref(false); | ||
| 15 | 16 | ||
| 16 | const [registerModal, { changeLoading, closeModal }] = useModalInner( | 17 | const [registerModal, { changeLoading, closeModal }] = useModalInner( | 
| 17 | (record: DataBoardRecord & { isEdit: boolean }) => { | 18 | (record: DataBoardRecord & { isEdit: boolean }) => { | 
| @@ -33,6 +34,7 @@ | @@ -33,6 +34,7 @@ | ||
| 33 | await method.validate(); | 34 | await method.validate(); | 
| 34 | try { | 35 | try { | 
| 35 | const value = method.getFieldsValue() as AddDataBoardParams; | 36 | const value = method.getFieldsValue() as AddDataBoardParams; | 
| 37 | + loading.value = true; | ||
| 36 | changeLoading(true); | 38 | changeLoading(true); | 
| 37 | await addDataBoard(value); | 39 | await addDataBoard(value); | 
| 38 | createMessage.success('创建成功'); | 40 | createMessage.success('创建成功'); | 
| @@ -42,12 +44,14 @@ | @@ -42,12 +44,14 @@ | ||
| 42 | createMessage.error('创建失败'); | 44 | createMessage.error('创建失败'); | 
| 43 | } finally { | 45 | } finally { | 
| 44 | changeLoading(false); | 46 | changeLoading(false); | 
| 47 | + loading.value = false; | ||
| 45 | } | 48 | } | 
| 46 | }; | 49 | }; | 
| 47 | 50 | ||
| 48 | const handleEditPanel = async () => { | 51 | const handleEditPanel = async () => { | 
| 49 | await method.validate(); | 52 | await method.validate(); | 
| 50 | try { | 53 | try { | 
| 54 | + loading.value = true; | ||
| 51 | const value = method.getFieldsValue() as UpdateDataBoardParams; | 55 | const value = method.getFieldsValue() as UpdateDataBoardParams; | 
| 52 | value.id = unref(recordId) as string; | 56 | value.id = unref(recordId) as string; | 
| 53 | changeLoading(true); | 57 | changeLoading(true); | 
| @@ -59,6 +63,7 @@ | @@ -59,6 +63,7 @@ | ||
| 59 | createMessage.error('编辑失败'); | 63 | createMessage.error('编辑失败'); | 
| 60 | } finally { | 64 | } finally { | 
| 61 | changeLoading(false); | 65 | changeLoading(false); | 
| 66 | + loading.value = false; | ||
| 62 | } | 67 | } | 
| 63 | }; | 68 | }; | 
| 64 | 69 | ||
| @@ -75,6 +80,7 @@ | @@ -75,6 +80,7 @@ | ||
| 75 | :title="isEdit ? '编辑看板' : '创建看板'" | 80 | :title="isEdit ? '编辑看板' : '创建看板'" | 
| 76 | @register="registerModal" | 81 | @register="registerModal" | 
| 77 | @ok="handleGetValue" | 82 | @ok="handleGetValue" | 
| 83 | + :okButtonProps="{ loading }" | ||
| 78 | > | 84 | > | 
| 79 | <BasicForm @register="registerForm" /> | 85 | <BasicForm @register="registerForm" /> | 
| 80 | </BasicModal> | 86 | </BasicModal> | 
| @@ -25,6 +25,7 @@ | @@ -25,6 +25,7 @@ | ||
| 25 | const emit = defineEmits(['update', 'create', 'register']); | 25 | const emit = defineEmits(['update', 'create', 'register']); | 
| 26 | const ROUTE = useRoute(); | 26 | const ROUTE = useRoute(); | 
| 27 | 27 | ||
| 28 | + const loading = ref(false); | ||
| 28 | const { createMessage } = useMessage(); | 29 | const { createMessage } = useMessage(); | 
| 29 | 30 | ||
| 30 | const boardId = computed(() => { | 31 | const boardId = computed(() => { | 
| @@ -81,6 +82,7 @@ | @@ -81,6 +82,7 @@ | ||
| 81 | } | 82 | } | 
| 82 | const layout = calcLayoutInfo(unref(props.layout)); | 83 | const layout = calcLayoutInfo(unref(props.layout)); | 
| 83 | changeOkLoading(true); | 84 | changeOkLoading(true); | 
| 85 | + loading.value = true; | ||
| 84 | await addDataComponent({ | 86 | await addDataComponent({ | 
| 85 | boardId: unref(boardId), | 87 | boardId: unref(boardId), | 
| 86 | record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value, layout }, | 88 | record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value, layout }, | 
| @@ -93,12 +95,14 @@ | @@ -93,12 +95,14 @@ | ||
| 93 | // createMessage.error('创建失败'); | 95 | // createMessage.error('创建失败'); | 
| 94 | } finally { | 96 | } finally { | 
| 95 | changeOkLoading(false); | 97 | changeOkLoading(false); | 
| 98 | + loading.value = false; | ||
| 96 | } | 99 | } | 
| 97 | }; | 100 | }; | 
| 98 | 101 | ||
| 99 | const handleUpdateComponent = async (value: Recordable) => { | 102 | const handleUpdateComponent = async (value: Recordable) => { | 
| 100 | try { | 103 | try { | 
| 101 | changeOkLoading(true); | 104 | changeOkLoading(true); | 
| 105 | + loading.value = true; | ||
| 102 | const res = await updateDataComponent({ | 106 | const res = await updateDataComponent({ | 
| 103 | boardId: unref(boardId), | 107 | boardId: unref(boardId), | 
| 104 | record: { | 108 | record: { | 
| @@ -116,6 +120,7 @@ | @@ -116,6 +120,7 @@ | ||
| 116 | // createMessage.error('修改失败'); | 120 | // createMessage.error('修改失败'); | 
| 117 | } finally { | 121 | } finally { | 
| 118 | changeOkLoading(false); | 122 | changeOkLoading(false); | 
| 123 | + loading.value = false; | ||
| 119 | } | 124 | } | 
| 120 | }; | 125 | }; | 
| 121 | 126 | ||
| @@ -133,6 +138,7 @@ | @@ -133,6 +138,7 @@ | ||
| 133 | :destroy-on-close="true" | 138 | :destroy-on-close="true" | 
| 134 | @ok="handleSubmit" | 139 | @ok="handleSubmit" | 
| 135 | @cancel="resetForm" | 140 | @cancel="resetForm" | 
| 141 | + :ok-button-props="{ loading }" | ||
| 136 | > | 142 | > | 
| 137 | <section> | 143 | <section> | 
| 138 | <Tabs type="card"> | 144 | <Tabs type="card"> |