@skyfox2000/webui 1.2.7 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/modules/_plugin-vue_export-helper-CHgC5LLL.js +9 -0
- package/lib/assets/modules/file-upload-BYIvmkmy.js +204 -0
- package/lib/assets/modules/form-excel-BUX2QW11.js +235 -0
- package/lib/assets/modules/{index-BZvwPKou.js → index-DcRkhkn2.js} +1 -1
- package/lib/assets/modules/index-jl5Ie2tZ.js +112 -0
- package/lib/assets/modules/{menuTabs-izaFx0kk.js → menuTabs-DY1XBq16.js} +134 -139
- package/lib/assets/modules/{toolIcon-66dclHit.js → toolIcon-BDIOj_3l.js} +1 -1
- package/lib/assets/modules/uploadList-BPG5whz7.js +369 -0
- package/lib/assets/modules/{index-CKMDsqve.js → uploadList-D2Ux4h1N.js} +553 -513
- package/lib/components/common/index.d.ts +2 -0
- package/lib/components/common/loading/index.vue.d.ts +3 -0
- package/lib/components/content/dialog/index.vue.d.ts +1 -0
- package/lib/components/content/drawer/index.vue.d.ts +2 -0
- package/lib/components/content/form/formItem.vue.d.ts +2 -0
- package/lib/components/form/autoComplete/index.vue.d.ts +41 -4
- package/lib/components/form/cascader/index.vue.d.ts +13 -2
- package/lib/components/form/checkbox/index.vue.d.ts +13 -2
- package/lib/components/form/index.d.ts +1 -0
- package/lib/components/form/propEditor/index.vue.d.ts +10 -1
- package/lib/components/form/radio/index.vue.d.ts +13 -2
- package/lib/components/form/select/index.vue.d.ts +13 -2
- package/lib/components/form/upload/uploadList.vue.d.ts +14 -3
- package/lib/components/index.d.ts +2 -2
- package/lib/es/AceEditor/index.js +3 -3
- package/lib/es/BasicLayout/index.js +15 -15
- package/lib/es/Error403/index.js +1 -1
- package/lib/es/Error404/index.js +1 -1
- package/lib/es/ExcelForm/index.js +4 -4
- package/lib/es/UploadForm/index.js +60 -87
- package/lib/index.d.ts +3 -3
- package/lib/typings/form.d.ts +18 -2
- package/lib/typings/option.d.ts +16 -2
- package/lib/typings/page.d.ts +1 -0
- package/lib/utils/file-upload.d.ts +4 -2
- package/lib/utils/form-excel.d.ts +3 -0
- package/lib/utils/main-openapis.d.ts +0 -1
- package/lib/webui.css +1 -1
- package/lib/webui.es.js +1088 -1064
- package/package.json +1 -1
- package/scripts/userInput.py +2 -0
- package/src/components/common/index.ts +3 -0
- package/src/components/common/loading/index.vue +11 -0
- package/src/components/content/dialog/index.vue +15 -8
- package/src/components/content/dialog/uploadForm.vue +38 -79
- package/src/components/content/drawer/index.vue +26 -6
- package/src/components/content/form/formItem.vue +21 -5
- package/src/components/content/form/index.vue +4 -2
- package/src/components/content/search/searchItem.vue +1 -1
- package/src/components/form/autoComplete/index.vue +12 -4
- package/src/components/form/cascader/index.vue +1 -1
- package/src/components/form/datePicker/index.vue +1 -1
- package/src/components/form/index.ts +1 -0
- package/src/components/form/input/inputIcon.vue +1 -1
- package/src/components/form/propEditor/index.vue +113 -33
- package/src/components/form/select/index.vue +27 -17
- package/src/components/form/upload/uploadList.vue +174 -34
- package/src/components/index.ts +13 -1
- package/src/directives/permission.ts +11 -11
- package/src/index.ts +4 -10
- package/src/stores/appInfo.ts +6 -6
- package/src/typings/form.d.ts +18 -2
- package/src/typings/option.d.ts +16 -2
- package/src/typings/page.d.ts +1 -0
- package/src/utils/download.ts +1 -1
- package/src/utils/eventbus.ts +1 -1
- package/src/utils/file-upload.ts +75 -14
- package/src/utils/form-excel.ts +50 -1
- package/src/utils/form-validate.ts +18 -1
- package/src/utils/form.ts +3 -1
- package/src/utils/options.ts +38 -12
- package/lib/assets/modules/file-upload-D4bA7go8.js +0 -179
- package/lib/assets/modules/form-excel-DL2_SNiS.js +0 -211
- package/lib/assets/modules/index-D16E7UbH.js +0 -111
- package/lib/assets/modules/uploadList-Dw6eRrJT.js +0 -210
package/src/index.ts
CHANGED
|
@@ -155,7 +155,7 @@ export {
|
|
|
155
155
|
} from '@/utils/form-validate';
|
|
156
156
|
|
|
157
157
|
// form-excel 工具
|
|
158
|
-
export { validateExcel, checkExcelDuplicates, processExcelFile } from '@/utils/form-excel';
|
|
158
|
+
export { validateExcel, checkExcelDuplicates, processExcelFile, appendExcelData } from '@/utils/form-excel';
|
|
159
159
|
export type { ExcelMarkCell, ExcelMarkInfo } from '@/utils/form-excel';
|
|
160
160
|
|
|
161
161
|
// table 工具
|
|
@@ -185,15 +185,7 @@ export { path, AsyncUploader } from '@/utils/file-upload';
|
|
|
185
185
|
export { EventPrefix } from '@/utils/eventbus';
|
|
186
186
|
export { default as eventBus } from '@/utils/eventbus';
|
|
187
187
|
|
|
188
|
-
export {
|
|
189
|
-
getHostInfo,
|
|
190
|
-
getAppInfo,
|
|
191
|
-
userLogin,
|
|
192
|
-
userLogout,
|
|
193
|
-
getToken,
|
|
194
|
-
getUserInfo,
|
|
195
|
-
mainAppPush,
|
|
196
|
-
} from '@/utils/main-openapis';
|
|
188
|
+
export { getHostInfo, getAppInfo, userLogin, userLogout, getToken, getUserInfo } from '@/utils/main-openapis';
|
|
197
189
|
|
|
198
190
|
import router from '@/router';
|
|
199
191
|
export { router as AppRouter };
|
|
@@ -215,6 +207,7 @@ export {
|
|
|
215
207
|
LayoutIcon,
|
|
216
208
|
ProjectIcon,
|
|
217
209
|
ToolIcon,
|
|
210
|
+
Loading,
|
|
218
211
|
Dialog,
|
|
219
212
|
Drawer,
|
|
220
213
|
Form,
|
|
@@ -235,6 +228,7 @@ export {
|
|
|
235
228
|
InputPassword,
|
|
236
229
|
InputNumber,
|
|
237
230
|
PropEditor,
|
|
231
|
+
type PropConfigItem,
|
|
238
232
|
Radio,
|
|
239
233
|
RadioStatus,
|
|
240
234
|
RangePicker,
|
package/src/stores/appInfo.ts
CHANGED
|
@@ -52,8 +52,8 @@ const checkRoutePermission = (route: RouteRecord): boolean => {
|
|
|
52
52
|
|
|
53
53
|
if (EnvConfig.VITE_PERMISSION_MODE === 'role') {
|
|
54
54
|
// 仅判断角色权限
|
|
55
|
-
if (!isEmpty(roles)
|
|
56
|
-
return userInfoStore.hasRole(roles);
|
|
55
|
+
if (!isEmpty(roles)) {
|
|
56
|
+
return userInfoStore.hasRole(roles!);
|
|
57
57
|
}
|
|
58
58
|
// 如果没有配置角色,默认允许访问
|
|
59
59
|
return true;
|
|
@@ -63,8 +63,8 @@ const checkRoutePermission = (route: RouteRecord): boolean => {
|
|
|
63
63
|
let hasPermitPermission = false;
|
|
64
64
|
|
|
65
65
|
// 检查角色权限
|
|
66
|
-
if (!isEmpty(roles)
|
|
67
|
-
hasRolePermission = userInfoStore.hasRole(roles);
|
|
66
|
+
if (!isEmpty(roles)) {
|
|
67
|
+
hasRolePermission = userInfoStore.hasRole(roles!);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// 如果是管理员,只需要检查角色权限
|
|
@@ -73,8 +73,8 @@ const checkRoutePermission = (route: RouteRecord): boolean => {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// 检查功能权限
|
|
76
|
-
if (!isEmpty(permission)
|
|
77
|
-
hasPermitPermission = userInfoStore.hasPermit(route.path, permission);
|
|
76
|
+
if (!isEmpty(permission)) {
|
|
77
|
+
hasPermitPermission = userInfoStore.hasPermit(route.path, permission!);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// 任意一个有权限即可
|
package/src/typings/form.d.ts
CHANGED
|
@@ -99,6 +99,22 @@ export type EditorControl<T> = EditorControlOption & {
|
|
|
99
99
|
ruleValidator?: Validator;
|
|
100
100
|
/**
|
|
101
101
|
* 验证结果
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const ruleResults: Record<string, ValidateError[]> = {};
|
|
105
|
+
* fieldNames.forEach((fieldName) => {
|
|
106
|
+
* const rule = formRules[fieldName];
|
|
107
|
+
* if (rule) {
|
|
108
|
+
* ruleResults[fieldName] = [
|
|
109
|
+
* {
|
|
110
|
+
* field: fieldName,
|
|
111
|
+
* fieldValue: fieldValue,
|
|
112
|
+
* message: `数据重复`,
|
|
113
|
+
* },
|
|
114
|
+
* ];
|
|
115
|
+
* }
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
102
118
|
*/
|
|
103
119
|
ruleResults: Ref<Record<string, ValidateError[]> | undefined>;
|
|
104
120
|
/**
|
|
@@ -156,10 +172,10 @@ export interface InputFactoryItems<T> {
|
|
|
156
172
|
/**
|
|
157
173
|
* 输入项reload事件处理
|
|
158
174
|
* @param event 事件名
|
|
159
|
-
* @param
|
|
175
|
+
* @param params 参数
|
|
160
176
|
* @returns
|
|
161
177
|
*/
|
|
162
|
-
reloadHandler?: (event: string,
|
|
178
|
+
reloadHandler?: (event: string, params: Record<string, AnyData> | AnyData[]) => void;
|
|
163
179
|
/**
|
|
164
180
|
* 输入项事件
|
|
165
181
|
* @param evt 事件
|
package/src/typings/option.d.ts
CHANGED
|
@@ -43,6 +43,10 @@ export interface OptionProps {
|
|
|
43
43
|
* 接口定义
|
|
44
44
|
*/
|
|
45
45
|
url?: IUrlInfo;
|
|
46
|
+
/**
|
|
47
|
+
* 重载接口,默认false
|
|
48
|
+
*/
|
|
49
|
+
reload?: boolean;
|
|
46
50
|
/**
|
|
47
51
|
* 接口自定义QOD参数
|
|
48
52
|
*/
|
|
@@ -73,8 +77,9 @@ export interface OptionProps {
|
|
|
73
77
|
* - eventBus.emit(changeEvent, { Q参数 } | 数据[])
|
|
74
78
|
* - 数据组仅用于联动显示
|
|
75
79
|
* - 其它情况使用Query参数
|
|
80
|
+
* - 第一个参数为事件名,第二个参数为事件参数模板,支持模板 ${selectedValues}
|
|
76
81
|
*/
|
|
77
|
-
changeEvent?: string | string[];
|
|
82
|
+
changeEvent?: [string | string[], string];
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
import type { PropType } from 'vue';
|
|
@@ -124,6 +129,14 @@ export const OptionCommProps = {
|
|
|
124
129
|
type: Object as PropType<IUrlInfo>,
|
|
125
130
|
required: false,
|
|
126
131
|
},
|
|
132
|
+
/**
|
|
133
|
+
* 重载接口,默认false
|
|
134
|
+
*/
|
|
135
|
+
reload: {
|
|
136
|
+
type: Boolean as PropType<boolean>,
|
|
137
|
+
required: false,
|
|
138
|
+
default: false,
|
|
139
|
+
},
|
|
127
140
|
/**
|
|
128
141
|
* 接口自定义QOD参数
|
|
129
142
|
*/
|
|
@@ -169,9 +182,10 @@ export const OptionCommProps = {
|
|
|
169
182
|
* - eventBus.emit(changeEvent, { Q参数 } | 数据[])
|
|
170
183
|
* - 数据组仅用于联动显示
|
|
171
184
|
* - 其它情况使用Query参数
|
|
185
|
+
* - 第一个参数为事件名,第二个参数为事件参数模板,支持模板 ${selectedValues}
|
|
172
186
|
*/
|
|
173
187
|
changeEvent: {
|
|
174
|
-
type:
|
|
188
|
+
type: Array as PropType<[string | string[], string]>,
|
|
175
189
|
required: false,
|
|
176
190
|
},
|
|
177
191
|
};
|
package/src/typings/page.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export enum ProviderKeys {
|
|
|
17
17
|
'EditorControl' = 'Page.EditorControl',
|
|
18
18
|
'GridControl' = 'Page.GridControl',
|
|
19
19
|
'LabelText' = 'FormItem.LabelText',
|
|
20
|
+
'LabelWidth' = 'FormItem.LabelWidth',
|
|
20
21
|
'RuleKey' = 'FormItem.RuleKey',
|
|
21
22
|
'ErrInfo' = 'FormItem.ErrInfo',
|
|
22
23
|
}
|
package/src/utils/download.ts
CHANGED
|
@@ -51,7 +51,7 @@ export const donwloadFromMinio = <T>(url: IUrlInfo, params?: ReqParams, pageCtrl
|
|
|
51
51
|
const minioFile: MinioFile = result.data as unknown as MinioFile;
|
|
52
52
|
// 提取内容和文件名
|
|
53
53
|
const base64String = minioFile.Content!;
|
|
54
|
-
const fileName = minioFile.
|
|
54
|
+
const fileName = minioFile.Key.split('/').pop()!;
|
|
55
55
|
|
|
56
56
|
// 解码 Base64 字符串
|
|
57
57
|
const byteCharacters = atob(base64String);
|
package/src/utils/eventbus.ts
CHANGED
|
@@ -25,7 +25,7 @@ class EventBus {
|
|
|
25
25
|
*/
|
|
26
26
|
on(event: string, listener: (...args: any[]) => void) {
|
|
27
27
|
if (!this.isValidPrefix(event)) {
|
|
28
|
-
console.error(`事件前缀不正确: ${event}
|
|
28
|
+
console.error(`事件前缀不正确: ${event}, 事件名必须以 ${Object.values(EventPrefix).join(', ')} 开头`);
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
package/src/utils/file-upload.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { hostUrl, IUrlInfo } from '@skyfox2000/fapi';
|
|
|
3
3
|
import { getToken } from '@/utils/main-openapis';
|
|
4
4
|
import { UploadFile, UploadStatus } from '@/typings/upload.d';
|
|
5
5
|
import dayjs from 'dayjs';
|
|
6
|
+
import message from 'vue-m-message';
|
|
7
|
+
import { Ref } from 'vue';
|
|
6
8
|
|
|
7
9
|
export class path {
|
|
8
10
|
/**
|
|
@@ -44,18 +46,70 @@ export class AsyncUploader {
|
|
|
44
46
|
* @param urlInfo 文件上传的 API 配置(IUrlInfo 对象)
|
|
45
47
|
* @param maxConcurrent 最大允许并发上传的文件数量
|
|
46
48
|
*/
|
|
47
|
-
constructor(urlInfo: IUrlInfo, maxConcurrent: number) {
|
|
49
|
+
constructor(urlInfo: IUrlInfo, maxConcurrent: number = 3) {
|
|
48
50
|
this.urlInfo = urlInfo;
|
|
49
51
|
this.maxConcurrent = maxConcurrent;
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
/**
|
|
55
|
+
* 执行上传
|
|
56
|
+
* @param fileList 文件列表
|
|
57
|
+
* @param loading 加载状态
|
|
58
|
+
* @param continueOnError 错误时是否继续上传
|
|
59
|
+
* @param onComplete 上传完成回调
|
|
60
|
+
* @param onProgress 上传进度回调
|
|
61
|
+
* @returns 上传结果
|
|
62
|
+
*/
|
|
63
|
+
public async doUpload(
|
|
64
|
+
fileList: UploadFile[],
|
|
65
|
+
loading: Ref<boolean>,
|
|
66
|
+
continueOnError: boolean,
|
|
67
|
+
onComplete?: (result: boolean, files: UploadFile[]) => void,
|
|
68
|
+
onProgress?: (file: UploadFile) => void,
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
if (!fileList.length) return;
|
|
71
|
+
|
|
72
|
+
if (fileList.length === 0) {
|
|
73
|
+
message.warning('请选择上传的文件!');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
loading.value = true;
|
|
78
|
+
|
|
79
|
+
// 开始上传文件
|
|
80
|
+
await this.uploadFiles(fileList, onProgress, (files) => {
|
|
81
|
+
let result = false;
|
|
82
|
+
let err_count = 0;
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
if (file.status === UploadStatus.Error) {
|
|
85
|
+
err_count++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!err_count) {
|
|
89
|
+
message.success('全部文件上传成功!');
|
|
90
|
+
result = true;
|
|
91
|
+
} else if (err_count < files.length) {
|
|
92
|
+
if (continueOnError) {
|
|
93
|
+
message.error('上传结束,部分文件上传失败!');
|
|
94
|
+
message.warning('保存上传成功的文件!');
|
|
95
|
+
result = true;
|
|
96
|
+
} else {
|
|
97
|
+
message.error('上传结束,部分文件上传失败,取消保存!');
|
|
98
|
+
}
|
|
99
|
+
} else message.error('上传结束,所有文件上传失败!');
|
|
100
|
+
|
|
101
|
+
loading.value = false;
|
|
102
|
+
onComplete?.(result, files);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
52
106
|
/**
|
|
53
107
|
* 上传多个文件,控制并发数量
|
|
54
108
|
* @param files 文件列表(File[] 或 FileList)
|
|
55
109
|
* @param onProgress 上传进度回调
|
|
56
110
|
* @param onComplete 上传完成回调
|
|
57
111
|
*/
|
|
58
|
-
|
|
112
|
+
private async uploadFiles(
|
|
59
113
|
fileList: UploadFile[],
|
|
60
114
|
onProgress?: (file: UploadFile) => void,
|
|
61
115
|
onComplete?: (files: UploadFile[]) => void,
|
|
@@ -93,7 +147,6 @@ export class AsyncUploader {
|
|
|
93
147
|
|
|
94
148
|
// 等待所有上传任务完成
|
|
95
149
|
await Promise.all(activeUploads);
|
|
96
|
-
onComplete?.(fileList);
|
|
97
150
|
} catch (error) {
|
|
98
151
|
console.error('上传失败:', error);
|
|
99
152
|
fileList.forEach((file) => {
|
|
@@ -101,6 +154,7 @@ export class AsyncUploader {
|
|
|
101
154
|
file.error = error instanceof Error ? error : new Error('上传失败');
|
|
102
155
|
onProgress?.(file);
|
|
103
156
|
});
|
|
157
|
+
} finally {
|
|
104
158
|
onComplete?.(fileList);
|
|
105
159
|
}
|
|
106
160
|
}
|
|
@@ -119,19 +173,11 @@ export class AsyncUploader {
|
|
|
119
173
|
onProgress?: (file: UploadFile) => void,
|
|
120
174
|
): Promise<void> {
|
|
121
175
|
try {
|
|
122
|
-
// 更新文件状态为"正在上传"
|
|
123
|
-
file.status = UploadStatus.Uploading;
|
|
124
|
-
|
|
125
176
|
await this.uploadFile(file, this.abortController!.signal, (percent) => {
|
|
126
177
|
file.percent = percent;
|
|
127
178
|
onProgress?.(file);
|
|
128
179
|
});
|
|
129
|
-
|
|
130
|
-
// 更新文件状态为"上传成功"
|
|
131
|
-
file.status = UploadStatus.Success;
|
|
132
180
|
} catch (error) {
|
|
133
|
-
// 更新文件状态为"上传失败"
|
|
134
|
-
file.status = UploadStatus.Error;
|
|
135
181
|
file.error = error instanceof Error ? error : new Error('上传失败');
|
|
136
182
|
console.error(file.error);
|
|
137
183
|
} finally {
|
|
@@ -156,6 +202,9 @@ export class AsyncUploader {
|
|
|
156
202
|
signal: AbortSignal,
|
|
157
203
|
onProgress: (percent: number) => void,
|
|
158
204
|
): Promise<UploadFile> {
|
|
205
|
+
// 更新文件状态为"正在上传"
|
|
206
|
+
file.status = UploadStatus.Uploading;
|
|
207
|
+
|
|
159
208
|
// 以 Promise 化的方式处理上传请求
|
|
160
209
|
return new Promise((resolve, reject) => {
|
|
161
210
|
// 创建表单数据
|
|
@@ -172,7 +221,7 @@ export class AsyncUploader {
|
|
|
172
221
|
// 创建 XMLHttpRequest 对象
|
|
173
222
|
const xhr = new XMLHttpRequest();
|
|
174
223
|
const url = hostUrl(this.urlInfo);
|
|
175
|
-
if (url === false) return Promise.resolve(
|
|
224
|
+
if (url === false) return Promise.resolve(file);
|
|
176
225
|
xhr.open('POST', url, true);
|
|
177
226
|
|
|
178
227
|
// 设置请求头
|
|
@@ -222,22 +271,32 @@ export class AsyncUploader {
|
|
|
222
271
|
file.minioFile = {
|
|
223
272
|
ETag: res.data.ETag,
|
|
224
273
|
Bucket: res.data.Bucket,
|
|
274
|
+
FileName: file.fileName,
|
|
225
275
|
Key: res.data.Key,
|
|
226
276
|
Size: res.data.Size,
|
|
277
|
+
Type: file.type,
|
|
227
278
|
UpdateTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
|
228
|
-
Status: UploadStatus.
|
|
279
|
+
Status: UploadStatus.Success,
|
|
229
280
|
};
|
|
281
|
+
// 更新文件状态为"上传成功"
|
|
282
|
+
file.status = UploadStatus.Success;
|
|
230
283
|
resolve(file); // 上传成功,返回解析后的 JSON 数据
|
|
231
284
|
} else {
|
|
232
285
|
// 如果 status 不是 success,则视为失败,提取 msg 作为错误信息
|
|
233
286
|
const error = res.msg;
|
|
287
|
+
// 更新文件状态为"上传失败"
|
|
288
|
+
file.status = UploadStatus.Error;
|
|
234
289
|
reject(new Error(error));
|
|
235
290
|
}
|
|
236
291
|
} catch (error) {
|
|
292
|
+
// 更新文件状态为"上传失败"
|
|
293
|
+
file.status = UploadStatus.Error;
|
|
237
294
|
// 如果 JSON 格式无效,抛出错误
|
|
238
295
|
reject(new Error('无法解析返回的 JSON 数据: ' + error));
|
|
239
296
|
}
|
|
240
297
|
} else {
|
|
298
|
+
// 更新文件状态为"上传失败"
|
|
299
|
+
file.status = UploadStatus.Error;
|
|
241
300
|
// 根据 HTTP 状态码判断上传失败的原因
|
|
242
301
|
reject(new Error(`上传失败,状态码:${xhr.status}`));
|
|
243
302
|
}
|
|
@@ -245,6 +304,8 @@ export class AsyncUploader {
|
|
|
245
304
|
|
|
246
305
|
// 监听网络错误事件
|
|
247
306
|
xhr.addEventListener('error', () => {
|
|
307
|
+
// 更新文件状态为"上传失败"
|
|
308
|
+
file.status = UploadStatus.Error;
|
|
248
309
|
reject(new Error('上传失败,网络异常'));
|
|
249
310
|
});
|
|
250
311
|
|
|
@@ -268,7 +329,6 @@ export class AsyncUploader {
|
|
|
268
329
|
}
|
|
269
330
|
}
|
|
270
331
|
}
|
|
271
|
-
|
|
272
332
|
// // 使用示例
|
|
273
333
|
// (async () => {
|
|
274
334
|
// // 创建上传器实例,指定 API 配置和最大并发数
|
|
@@ -302,3 +362,4 @@ export class AsyncUploader {
|
|
|
302
362
|
// console.error('上传错误:', error);
|
|
303
363
|
// }
|
|
304
364
|
// })();
|
|
365
|
+
|
package/src/utils/form-excel.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { isEmpty } from './isEmpty';
|
|
|
6
6
|
import message from 'vue-m-message';
|
|
7
7
|
import { ValidateRule } from '@/typings/form';
|
|
8
8
|
import { validMessages } from './form-validate';
|
|
9
|
+
import { UploadFile } from '@/typings/upload';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Excel数据处理需要标记的单元格
|
|
@@ -44,7 +45,10 @@ export const processExcelFile = async (excelBuffer: ArrayBuffer) => {
|
|
|
44
45
|
|
|
45
46
|
// 提取表头和数据
|
|
46
47
|
const headers: string[] = [];
|
|
48
|
+
// 表头对象数据行
|
|
47
49
|
const excelData: Record<string, any>[] = [];
|
|
50
|
+
// 纯数据行
|
|
51
|
+
const excelRows: any[][] = [];
|
|
48
52
|
|
|
49
53
|
// 提取表头(第一行)
|
|
50
54
|
worksheet.getRow(1).eachCell((cell) => {
|
|
@@ -86,6 +90,7 @@ export const processExcelFile = async (excelBuffer: ArrayBuffer) => {
|
|
|
86
90
|
if (rowNumber > 1) {
|
|
87
91
|
// 跳过表头
|
|
88
92
|
const rowData: Record<string, any> = {};
|
|
93
|
+
const rowDataArray: any[] = [];
|
|
89
94
|
headers.forEach((header, idx) => {
|
|
90
95
|
if (header) {
|
|
91
96
|
const cell = row.getCell(idx + 1);
|
|
@@ -116,13 +121,15 @@ export const processExcelFile = async (excelBuffer: ArrayBuffer) => {
|
|
|
116
121
|
} else {
|
|
117
122
|
rowData[header] = null;
|
|
118
123
|
}
|
|
124
|
+
rowDataArray.push(rowData[header]);
|
|
119
125
|
}
|
|
120
126
|
});
|
|
121
127
|
excelData.push(rowData);
|
|
128
|
+
excelRows.push(rowDataArray);
|
|
122
129
|
}
|
|
123
130
|
});
|
|
124
131
|
|
|
125
|
-
return { workbook, worksheet, headers, excelData };
|
|
132
|
+
return { workbook, worksheet, headers, excelData, excelRows };
|
|
126
133
|
};
|
|
127
134
|
|
|
128
135
|
/**
|
|
@@ -521,3 +528,45 @@ export const checkExcelDuplicates = async (
|
|
|
521
528
|
|
|
522
529
|
return { hasError: false }; // 没有重复数据
|
|
523
530
|
};
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* 将Excel数据追加到fileData中
|
|
534
|
+
* @param fileInfo 文件信息
|
|
535
|
+
* @param fileData 文件数据
|
|
536
|
+
* @param key 文件数据对象的键
|
|
537
|
+
* @param fields 需要追加的字段
|
|
538
|
+
* @returns
|
|
539
|
+
*/
|
|
540
|
+
export const appendExcelData = async (fileInfo: UploadFile, fileData: Record<string, any>, fields?: string[]) => {
|
|
541
|
+
const file = fileInfo.originFileObj as File;
|
|
542
|
+
if (file) {
|
|
543
|
+
// 使用ArrayBuffer读取文件
|
|
544
|
+
const buffer = await file.arrayBuffer();
|
|
545
|
+
// 获取Excel数据,并转换成指定数据对象结构
|
|
546
|
+
const excelFileData = await processExcelFile(buffer);
|
|
547
|
+
if (!excelFileData) {
|
|
548
|
+
message.error('上传的文件不是Excel文件');
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
const { headers, excelRows, excelData } = excelFileData;
|
|
552
|
+
if (fields && fields.length > 0) {
|
|
553
|
+
fields.forEach((field) => {
|
|
554
|
+
switch (field) {
|
|
555
|
+
case 'Headers':
|
|
556
|
+
fileData.Headers = headers;
|
|
557
|
+
break;
|
|
558
|
+
case 'RawRows':
|
|
559
|
+
fileData.RawRows = excelRows;
|
|
560
|
+
break;
|
|
561
|
+
case 'Records':
|
|
562
|
+
fileData.Records = excelData;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
} else {
|
|
567
|
+
fileData.Headers = headers;
|
|
568
|
+
fileData.RawRows = excelRows;
|
|
569
|
+
fileData.Records = excelData;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
};
|
|
@@ -2,7 +2,7 @@ import { inject, toRaw, ref, provide, watch, Ref } from 'vue';
|
|
|
2
2
|
import Validator from 'async-validator';
|
|
3
3
|
import { EditorControl, InputFactoryItems, ValidateError, ValidateRule } from '@/typings/form.d';
|
|
4
4
|
import { ProviderKeys } from '@/typings/page.d';
|
|
5
|
-
import { AnyData} from '@skyfox2000/fapi';
|
|
5
|
+
import { AnyData } from '@skyfox2000/fapi';
|
|
6
6
|
import { isEmpty } from './isEmpty';
|
|
7
7
|
|
|
8
8
|
export let validMessages: Validator;
|
|
@@ -171,6 +171,23 @@ export const getRuleTexts = (rules?: Record<string, ValidateRule>) => {
|
|
|
171
171
|
/**
|
|
172
172
|
* 表单数据验证
|
|
173
173
|
* @param editorCtrl 表单控制对象
|
|
174
|
+
* @example 规则示例
|
|
175
|
+
* {
|
|
176
|
+
* Name: {
|
|
177
|
+
* required: true,
|
|
178
|
+
* message: '请输入站点名称',
|
|
179
|
+
* },
|
|
180
|
+
* Files: {
|
|
181
|
+
* type: 'object',
|
|
182
|
+
* fields: {
|
|
183
|
+
* dev_list: {
|
|
184
|
+
* type: 'array',
|
|
185
|
+
* min: 1,
|
|
186
|
+
* message: '请上传端口配置表',
|
|
187
|
+
* },
|
|
188
|
+
* },
|
|
189
|
+
* },
|
|
190
|
+
* }
|
|
174
191
|
*/
|
|
175
192
|
export const formValidate = async <T>(editorCtrl: EditorControl<T>) => {
|
|
176
193
|
const formData: Record<string, AnyData> = editorCtrl.formData.value as Record<string, AnyData>;
|
package/src/utils/form.ts
CHANGED
|
@@ -29,6 +29,7 @@ export const onFormSaveAs = <T>(editorCtrl: EditorControl<T>) => {
|
|
|
29
29
|
* @param pageCtrl 页面控制对象
|
|
30
30
|
*/
|
|
31
31
|
export const onFormClose = <T>(editorCtrl: EditorControl<T>) => {
|
|
32
|
+
resetForm(editorCtrl);
|
|
32
33
|
editorCtrl.visible.value = false;
|
|
33
34
|
};
|
|
34
35
|
|
|
@@ -174,7 +175,8 @@ export const openNewForm = <T>(editorCtrl: EditorControl<T>) => {
|
|
|
174
175
|
*/
|
|
175
176
|
export const setFormData = <T>(editorCtrl: EditorControl<T>, formData?: T) => {
|
|
176
177
|
resetForm(editorCtrl);
|
|
177
|
-
|
|
178
|
+
const newFormData = { ...editorCtrl.formData.value, ...formData };
|
|
179
|
+
editorCtrl.formData.value = JSON.parse(JSON.stringify(newFormData)) as T;
|
|
178
180
|
};
|
|
179
181
|
|
|
180
182
|
/**
|
package/src/utils/options.ts
CHANGED
|
@@ -37,11 +37,12 @@ export const loadOption = (
|
|
|
37
37
|
) => {
|
|
38
38
|
if (inputFactory && props.reloadEvent) {
|
|
39
39
|
/// 接收事件,联动显示或者重新加载数据
|
|
40
|
-
inputFactory.reloadHandler = (_event: string,
|
|
41
|
-
optionEventHandler(url!, props,
|
|
40
|
+
inputFactory.reloadHandler = (_event: string, params: Record<string, AnyData> | AnyData[]) => {
|
|
41
|
+
optionEventHandler(url!, props, params, options);
|
|
42
42
|
};
|
|
43
43
|
eventBus.on(props.reloadEvent, inputFactory.reloadHandler);
|
|
44
44
|
}
|
|
45
|
+
|
|
45
46
|
if (props.data) {
|
|
46
47
|
watch(
|
|
47
48
|
() => props.data,
|
|
@@ -106,13 +107,15 @@ const updateOptions = (
|
|
|
106
107
|
const optionEventHandler = (
|
|
107
108
|
url: IUrlInfo,
|
|
108
109
|
props: OptionProps,
|
|
109
|
-
|
|
110
|
+
params_data: Record<string, AnyData> | AnyData[],
|
|
110
111
|
options: Ref<OptionItemProps[]>,
|
|
111
112
|
) => {
|
|
112
|
-
if (Array.isArray(
|
|
113
|
-
|
|
113
|
+
if (Array.isArray(params_data)) {
|
|
114
|
+
// 数据组仅用于联动显示
|
|
115
|
+
updateOptions(props, params_data, options, true);
|
|
114
116
|
} else {
|
|
115
|
-
|
|
117
|
+
// 其它情况使用Query参数
|
|
118
|
+
doQueryOptions(url, props, params_data, options);
|
|
116
119
|
}
|
|
117
120
|
};
|
|
118
121
|
|
|
@@ -134,7 +137,7 @@ export const unloadOption = (props: OptionProps, inputFactory?: InputFactoryItem
|
|
|
134
137
|
* @param options 实际选择项
|
|
135
138
|
*/
|
|
136
139
|
const doQueryOptions = (url: IUrlInfo, props: OptionProps, params: ReqParams, options: Ref<OptionItemProps[]>) => {
|
|
137
|
-
const newParams: ReqParams = combineParams(url.params, params,
|
|
140
|
+
const newParams: ReqParams = combineParams(url.params, props.params, params);
|
|
138
141
|
queryOptions(url, props.fieldMap, newParams).then((results) => {
|
|
139
142
|
// 使用url,由request负责转换,无需再次map转换
|
|
140
143
|
const data = results as OptionItemProps[];
|
|
@@ -295,12 +298,35 @@ export const onOptionChanged = (
|
|
|
295
298
|
}
|
|
296
299
|
|
|
297
300
|
if (props.changeEvent) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
301
|
+
const [eventNames, eventParams] = props.changeEvent;
|
|
302
|
+
if (eventParams) {
|
|
303
|
+
if (Array.isArray(eventNames)) {
|
|
304
|
+
eventNames.every((eventName: string) => {
|
|
305
|
+
const eventParamsValue = parseFieldTemplate(eventParams, { selectedValues: values });
|
|
306
|
+
try {
|
|
307
|
+
const eventJsonValue = JSON.parse(eventParamsValue);
|
|
308
|
+
eventBus.emit(eventName, eventJsonValue, values, selectedValues);
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error('eventParamsValue: ', eventParamsValue, error);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
} else {
|
|
314
|
+
const eventParamsValue = parseFieldTemplate(eventParams, { selectedValues: values });
|
|
315
|
+
try {
|
|
316
|
+
const eventJsonValue = JSON.parse(eventParamsValue);
|
|
317
|
+
eventBus.emit(eventNames, eventJsonValue, values, selectedValues);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error('eventParamsValue: ', eventParamsValue, error);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
302
322
|
} else {
|
|
303
|
-
|
|
323
|
+
if (Array.isArray(eventNames)) {
|
|
324
|
+
eventNames.every((eventName: string) => {
|
|
325
|
+
eventBus.emit(eventName, selectedValues, values);
|
|
326
|
+
});
|
|
327
|
+
} else {
|
|
328
|
+
eventBus.emit(eventNames, selectedValues, values);
|
|
329
|
+
}
|
|
304
330
|
}
|
|
305
331
|
}
|
|
306
332
|
// 触发 inputFactory 的 change 事件
|