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 | 22 | theme = htmlRoot = null; |
23 | 23 | } |
24 | 24 | })(); |
25 | - | |
26 | 25 | </script> |
27 | 26 | <div id="app"> |
28 | 27 | <style> |
... | ... | @@ -152,17 +151,17 @@ |
152 | 151 | } |
153 | 152 | } |
154 | 153 | </style> |
155 | - <div class="app-loading"> | |
154 | + <div id="first-screen-loading" class="app-loading"> | |
156 | 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 | 157 | <div class="app-loading-dots"> |
159 | 158 | <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> |
160 | 159 | </div> |
161 | - <div class="app-loading-title"><%= title %></div> | |
160 | + <!-- <div class="app-loading-title"><%= title %></div> --> | |
162 | 161 | </div> |
163 | 162 | </div> |
164 | 163 | </div> |
165 | - | |
164 | + | |
166 | 165 | <script type="module" src="/src/main.ts"></script> |
167 | 166 | </body> |
168 | 167 | </html> | ... | ... |
1 | 1 | import { defHttp } from '/@/utils/http/axios'; |
2 | -import { FileUploadResponse } from './model/index'; | |
2 | +import { FileUploadResponse, Platform } from './model/index'; | |
3 | 3 | enum API { |
4 | 4 | SELECT_DETAIL = '/enterprise/get', |
5 | 5 | UPDATE_DETAIL = '/enterprise/update', |
... | ... | @@ -67,7 +67,7 @@ export const bgUpload = (file) => { |
67 | 67 | |
68 | 68 | // 获取平台定制详情 |
69 | 69 | export const getPlatForm = () => { |
70 | - return defHttp.get({ | |
70 | + return defHttp.get<Platform>({ | |
71 | 71 | url: API.SELECT_PLATFORM, |
72 | 72 | }); |
73 | 73 | }; | ... | ... |
... | ... | @@ -5,3 +5,17 @@ export interface FileUploadResponse { |
5 | 5 | size: number; |
6 | 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 | 10 | export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue'; |
11 | 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; |
12 | 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 | 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 | 14 | import { setupI18n } from '/@/locales/setupI18n'; |
15 | 15 | import { registerGlobComp } from '/@/components/registerGlobComp'; |
16 | 16 | import '/@/assets/iconfont/iconfont'; |
17 | +import { usePlatform } from './views/system/customize/hook/usePlatformInfo'; | |
17 | 18 | |
18 | 19 | if (import.meta.env.DEV) { |
19 | 20 | import('ant-design-vue/dist/antd.less'); |
... | ... | @@ -23,6 +24,8 @@ async function bootstrap() { |
23 | 24 | // Configure store |
24 | 25 | setupStore(app); |
25 | 26 | |
27 | + await usePlatform(); | |
28 | + | |
26 | 29 | // Initialize internal system configuration |
27 | 30 | initAppConfigStore(); |
28 | 31 | |
... | ... | @@ -47,6 +50,7 @@ async function bootstrap() { |
47 | 50 | // Mount when the route is ready |
48 | 51 | // https://next.router.vuejs.org/api/#isready |
49 | 52 | await router.isReady(); |
53 | + | |
50 | 54 | app.mount('#app', true); |
51 | 55 | } |
52 | 56 | ... | ... |
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 | 6 | <a-button type="primary" @click="handleAdd"> 新增场景联动 </a-button> |
7 | 7 | </Authority> |
8 | 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 | 15 | <a-button color="error" :disabled="hasBatchDelete"> 批量删除 </a-button> |
11 | 16 | </Popconfirm> |
12 | 17 | </Authority> |
13 | 18 | </template> |
14 | 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 | 48 | </template> |
42 | 49 | |
43 | 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 | 58 | </template> |
47 | 59 | </BasicTable> |
48 | 60 | <SceneLinkAgeDrawer @register="registerDrawer" @success="handleSuccess" /> |
49 | 61 | </div> |
50 | 62 | </template> |
51 | 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 | 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 | 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 | 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 | +}; | ... | ... |