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 | +}; |