@skyfox2000/webui 1.4.17 → 1.4.19
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/package.json
CHANGED
|
@@ -12,6 +12,10 @@ const props = defineProps<{
|
|
|
12
12
|
* 配置项
|
|
13
13
|
*/
|
|
14
14
|
selectList?: PropConfigItem[];
|
|
15
|
+
/**
|
|
16
|
+
* 忽略项
|
|
17
|
+
*/
|
|
18
|
+
ignoreList?: string[];
|
|
15
19
|
/**
|
|
16
20
|
* 配置名宽度%
|
|
17
21
|
*/
|
|
@@ -56,11 +60,13 @@ const initConfigList = () => {
|
|
|
56
60
|
} else {
|
|
57
61
|
// 没有selectList的情况:基于value动态生成配置项
|
|
58
62
|
if (props.value && Object.keys(props.value).length > 0) {
|
|
59
|
-
configList.value = Object.entries(props.value)
|
|
60
|
-
|
|
61
|
-
field,
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
configList.value = Object.entries(props.value)
|
|
64
|
+
.filter(([field]) => !props.ignoreList || !props.ignoreList.includes(field))
|
|
65
|
+
.map(([field, value]) => ({
|
|
66
|
+
id: Date.now() + Math.random(),
|
|
67
|
+
field,
|
|
68
|
+
value,
|
|
69
|
+
}));
|
|
64
70
|
} else {
|
|
65
71
|
// value为空时,初始化为空数组
|
|
66
72
|
configList.value = [];
|
|
@@ -81,7 +87,7 @@ watch(
|
|
|
81
87
|
|
|
82
88
|
const updateConfig = () => {
|
|
83
89
|
let newConfig: Record<string, string>;
|
|
84
|
-
|
|
90
|
+
|
|
85
91
|
if (props.selectList && props.selectList.length > 0) {
|
|
86
92
|
// 有selectList时,只更新selectList中定义的字段,保留其他字段不变
|
|
87
93
|
newConfig = { ...props.value };
|
|
@@ -91,7 +97,7 @@ const updateConfig = () => {
|
|
|
91
97
|
}
|
|
92
98
|
});
|
|
93
99
|
} else {
|
|
94
|
-
// 没有selectList
|
|
100
|
+
// 没有selectList时,更新所有字段,包括ignoreList中的字段
|
|
95
101
|
newConfig = configList.value.reduce(
|
|
96
102
|
(acc: Record<string, string>, item: PropConfigItem) => {
|
|
97
103
|
if (item.field) {
|
|
@@ -101,8 +107,17 @@ const updateConfig = () => {
|
|
|
101
107
|
},
|
|
102
108
|
{} as Record<string, string>,
|
|
103
109
|
);
|
|
110
|
+
|
|
111
|
+
// 保留ignoreList中的字段
|
|
112
|
+
if (props.ignoreList && props.ignoreList.length > 0) {
|
|
113
|
+
props.ignoreList.forEach((field) => {
|
|
114
|
+
if (props.value[field] !== undefined) {
|
|
115
|
+
newConfig[field] = props.value[field];
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
104
119
|
}
|
|
105
|
-
|
|
120
|
+
|
|
106
121
|
isInnerChange = true;
|
|
107
122
|
emit('update:value', newConfig);
|
|
108
123
|
};
|
|
@@ -124,15 +139,9 @@ const handleInputChange = () => {
|
|
|
124
139
|
<div class="flex flex-col gap-2">
|
|
125
140
|
<div v-for="item in configList" :key="item.id" class="flex items-center gap-2">
|
|
126
141
|
<div :class="[fieldWidth ? `w-[${fieldWidth}%]` : 'w-[33%]']">
|
|
127
|
-
<Input
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
:title="item.text || item.field"
|
|
131
|
-
class="w-full"
|
|
132
|
-
:placeholder="item.text || labelHolder || '配置名'"
|
|
133
|
-
@input="handleInputChange"
|
|
134
|
-
:disabled="!addMore"
|
|
135
|
-
/>
|
|
142
|
+
<Input v-if="!selectList || selectList.length === 0" v-model:value="item.field"
|
|
143
|
+
:title="item.text || item.field" class="w-full" :placeholder="item.text || labelHolder || '配置名'"
|
|
144
|
+
@input="handleInputChange" :disabled="!addMore" />
|
|
136
145
|
<div v-else>
|
|
137
146
|
<Input v-model:value="item.text" :title="item.text" :disabled="true" class="w-[100%]" />
|
|
138
147
|
<Input type="hidden" v-model:value="item.field" />
|
|
@@ -140,20 +149,13 @@ const handleInputChange = () => {
|
|
|
140
149
|
</div>
|
|
141
150
|
<div class="w-[3%]">=</div>
|
|
142
151
|
<div :class="[fieldWidth ? `w-[${97 - fieldWidth}%]` : 'w-[64%]']">
|
|
143
|
-
<Input
|
|
144
|
-
|
|
145
|
-
:placeholder="valueHolder || '请输入' + item.text || '请输入配置值'"
|
|
146
|
-
@input="handleInputChange"
|
|
147
|
-
:title="item.value"
|
|
148
|
-
/>
|
|
152
|
+
<Input v-model:value="item.value" :placeholder="valueHolder || '请输入' + item.text || '请输入配置值'"
|
|
153
|
+
@input="handleInputChange" :title="item.value" />
|
|
149
154
|
</div>
|
|
150
155
|
</div>
|
|
151
|
-
<Button
|
|
152
|
-
v-if="addMore"
|
|
153
|
-
@click="addNewLine"
|
|
156
|
+
<Button v-if="addMore" @click="addNewLine"
|
|
154
157
|
class="mt-1 w-[80px] !text-[12px] text-[#666] bg-[#e6f7ff] border-[#b3e0ff] hover:bg-[#b3e0ff] hover:border-[#8abeff]"
|
|
155
|
-
size="small"
|
|
156
|
-
>
|
|
158
|
+
size="small">
|
|
157
159
|
新增配置行
|
|
158
160
|
</Button>
|
|
159
161
|
</div>
|
|
@@ -11,13 +11,92 @@ import { LoginExpiredError, useUserInfo } from '@/stores/userInfo';
|
|
|
11
11
|
// 表格列类型定义 (适配 ant-design-vue)
|
|
12
12
|
export interface TableColumn {
|
|
13
13
|
title: string;
|
|
14
|
-
dataIndex?: string;
|
|
14
|
+
dataIndex?: string | string[];
|
|
15
15
|
key?: string;
|
|
16
16
|
visible?: boolean; // 是否显示列
|
|
17
17
|
export?: boolean; // 是否导出列
|
|
18
18
|
customRender?: (options: { text: any; record: Record<string, any>; index: number; column: TableColumn }) => any;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* 将 dataIndex 转换为字符串形式
|
|
23
|
+
* 如果是数组则转换为点号连接的字符串,否则直接返回原值或空字符串
|
|
24
|
+
* @param dataIndex 列的 dataIndex
|
|
25
|
+
*/
|
|
26
|
+
const toString = (dataIndex: string | string[] | undefined): string => {
|
|
27
|
+
if (!dataIndex) return '';
|
|
28
|
+
if (Array.isArray(dataIndex)) {
|
|
29
|
+
return dataIndex.join('.');
|
|
30
|
+
}
|
|
31
|
+
return dataIndex;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 准备 CSV 字段配置
|
|
36
|
+
* @param columns 需要导出的列配置
|
|
37
|
+
*/
|
|
38
|
+
const getFields = (columns: TableColumn[]) => {
|
|
39
|
+
return columns.map((col) => ({
|
|
40
|
+
label: col.title,
|
|
41
|
+
value: toString(col.dataIndex) || col.key || '',
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 处理数据行,应用自定义渲染
|
|
47
|
+
* @param data 原始数据
|
|
48
|
+
* @param columns 列配置
|
|
49
|
+
*/
|
|
50
|
+
const processData = <T extends Record<string, any>>(data: T[], columns: TableColumn[]) => {
|
|
51
|
+
return data.map((row, index) => {
|
|
52
|
+
const newRow: Record<string, any> = { ...row };
|
|
53
|
+
|
|
54
|
+
columns.forEach((col) => {
|
|
55
|
+
const field = toString(col.dataIndex) || col.key;
|
|
56
|
+
if (!field) return;
|
|
57
|
+
|
|
58
|
+
// 应用自定义渲染
|
|
59
|
+
if (col.customRender) {
|
|
60
|
+
newRow[field] = col.customRender({
|
|
61
|
+
text: row[field],
|
|
62
|
+
record: row,
|
|
63
|
+
index: index,
|
|
64
|
+
column: col,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
return newRow;
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 生成并下载 CSV 文件
|
|
74
|
+
* @param fileName 文件名
|
|
75
|
+
* @param fields CSV 字段配置
|
|
76
|
+
* @param data 处理后的数据
|
|
77
|
+
*/
|
|
78
|
+
const downloadCSV = async <T extends Record<string, any>>(
|
|
79
|
+
fileName: string,
|
|
80
|
+
fields: Array<{ label: string; value: string }>,
|
|
81
|
+
data: T[],
|
|
82
|
+
) => {
|
|
83
|
+
try {
|
|
84
|
+
const mod = await import('@json2csv/plainjs');
|
|
85
|
+
const JSON2CSVParser = mod.Parser;
|
|
86
|
+
|
|
87
|
+
// 生成 CSV 内容
|
|
88
|
+
const parser = new JSON2CSVParser({ fields });
|
|
89
|
+
const csvContent = parser.parse(data);
|
|
90
|
+
|
|
91
|
+
// 创建并下载文件
|
|
92
|
+
const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv' });
|
|
93
|
+
downloadBlob(blob, fileName);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error('导出失败:', error);
|
|
96
|
+
message.error('文件导出失败,请稍后重试');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
21
100
|
/**
|
|
22
101
|
* 导出选中行数据为 CSV 文件
|
|
23
102
|
* @param fileName 文件名(支持 {YYYY}、{YY}、{MM}、{DD}、{HH}、{mm}、{ss} 时间格式)
|
|
@@ -30,8 +109,6 @@ export const exportSelectedRows = async <T extends Record<string, any>>(
|
|
|
30
109
|
selectRows: T[],
|
|
31
110
|
) => {
|
|
32
111
|
try {
|
|
33
|
-
const mod = await import('@json2csv/plainjs');
|
|
34
|
-
const JSON2CSVParser = mod.Parser;
|
|
35
112
|
// 1. 处理文件名中的日期格式
|
|
36
113
|
const processedFileName = formatFileName(fileName);
|
|
37
114
|
|
|
@@ -39,42 +116,15 @@ export const exportSelectedRows = async <T extends Record<string, any>>(
|
|
|
39
116
|
const exportColumns = columns.filter((col) => col.visible !== false && col.export !== false);
|
|
40
117
|
|
|
41
118
|
// 3. 准备 CSV 字段配置
|
|
42
|
-
const fields = exportColumns
|
|
43
|
-
label: col.title,
|
|
44
|
-
value: col.dataIndex || col.key || '',
|
|
45
|
-
}));
|
|
119
|
+
const fields = getFields(exportColumns);
|
|
46
120
|
|
|
47
121
|
// 4. 处理数据行
|
|
48
|
-
const processedData = selectRows
|
|
49
|
-
const newRow: Record<string, any> = { ...row };
|
|
50
|
-
|
|
51
|
-
exportColumns.forEach((col) => {
|
|
52
|
-
const field = col.dataIndex || col.key;
|
|
53
|
-
if (!field) return;
|
|
54
|
-
|
|
55
|
-
// 应用自定义渲染
|
|
56
|
-
if (col.customRender) {
|
|
57
|
-
newRow[field] = col.customRender({
|
|
58
|
-
text: row[field],
|
|
59
|
-
record: row,
|
|
60
|
-
index: index,
|
|
61
|
-
column: col,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
return newRow;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// 5. 生成 CSV 内容
|
|
69
|
-
const parser = new JSON2CSVParser({ fields });
|
|
70
|
-
const csvContent = parser.parse(processedData);
|
|
122
|
+
const processedData = processData(selectRows, exportColumns);
|
|
71
123
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
downloadBlob(blob, processedFileName);
|
|
124
|
+
// 5. 生成 CSV 内容并下载
|
|
125
|
+
await downloadCSV(processedFileName, fields, processedData);
|
|
75
126
|
} catch (error) {
|
|
76
127
|
console.error('导出失败:', error);
|
|
77
|
-
// throw new Error('文件导出失败,请稍后重试');
|
|
78
128
|
message.error('文件导出失败,请稍后重试');
|
|
79
129
|
}
|
|
80
130
|
};
|
|
@@ -96,8 +146,6 @@ export const exportResults = async <T extends Record<string, any>>(
|
|
|
96
146
|
url: IUrlInfo,
|
|
97
147
|
) => {
|
|
98
148
|
try {
|
|
99
|
-
const mod = await import('@json2csv/plainjs');
|
|
100
|
-
const JSON2CSVParser = mod.Parser;
|
|
101
149
|
// 1. 处理文件名中的日期格式
|
|
102
150
|
const processedFileName = formatFileName(fileName);
|
|
103
151
|
|
|
@@ -105,10 +153,7 @@ export const exportResults = async <T extends Record<string, any>>(
|
|
|
105
153
|
const exportColumns = columns.filter((col) => col.visible !== false);
|
|
106
154
|
|
|
107
155
|
// 3. 准备 CSV 字段配置
|
|
108
|
-
const fields = exportColumns
|
|
109
|
-
label: col.title,
|
|
110
|
-
value: col.dataIndex || col.key || '',
|
|
111
|
-
}));
|
|
156
|
+
const fields = getFields(exportColumns);
|
|
112
157
|
|
|
113
158
|
// 4. 获取数据
|
|
114
159
|
let pageCtrl = gridCtrl.page;
|
|
@@ -118,39 +163,16 @@ export const exportResults = async <T extends Record<string, any>>(
|
|
|
118
163
|
if (url.authorize === undefined) url.authorize = pageCtrl.authorize;
|
|
119
164
|
|
|
120
165
|
gridCtrl.isGridLoading.value = true;
|
|
121
|
-
return httpPost<T>(url, newParams).then((result: ApiResponse<T> | null) => {
|
|
166
|
+
return httpPost<T>(url, newParams).then(async (result: ApiResponse<T> | null) => {
|
|
122
167
|
gridCtrl.isGridLoading.value = false;
|
|
123
168
|
if (result?.status === ResStatus.SUCCESS) {
|
|
124
169
|
if (result.data) {
|
|
125
170
|
// 5. 处理数据行
|
|
126
171
|
let results = result.data as unknown as T[];
|
|
127
|
-
const processedData = results
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const field = col.dataIndex || col.key;
|
|
132
|
-
if (!field) return;
|
|
133
|
-
|
|
134
|
-
// 应用自定义渲染
|
|
135
|
-
if (col.customRender) {
|
|
136
|
-
newRow[field] = col.customRender({
|
|
137
|
-
text: row[field],
|
|
138
|
-
record: row,
|
|
139
|
-
index: index,
|
|
140
|
-
column: col,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
return newRow;
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// 5. 生成 CSV 内容
|
|
148
|
-
const parser = new JSON2CSVParser({ fields });
|
|
149
|
-
const csvContent = parser.parse(processedData);
|
|
150
|
-
|
|
151
|
-
// 6. 创建并下载文件
|
|
152
|
-
const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv' });
|
|
153
|
-
downloadBlob(blob, processedFileName);
|
|
172
|
+
const processedData = processData(results, exportColumns);
|
|
173
|
+
|
|
174
|
+
// 6. 生成 CSV 内容并下载
|
|
175
|
+
await downloadCSV(processedFileName, fields, processedData);
|
|
154
176
|
}
|
|
155
177
|
} else if (result?.errno == LoginExpiredError) {
|
|
156
178
|
const userInfoStore = useUserInfo();
|
|
@@ -161,10 +183,9 @@ export const exportResults = async <T extends Record<string, any>>(
|
|
|
161
183
|
});
|
|
162
184
|
} catch (error) {
|
|
163
185
|
console.error('导出失败:', error);
|
|
164
|
-
// throw new Error('文件导出失败,请稍后重试');
|
|
165
186
|
message.error('文件导出失败,请稍后重试');
|
|
166
187
|
}
|
|
167
188
|
};
|
|
168
189
|
|
|
169
190
|
// 后端处理
|
|
170
|
-
// 下载文件或二进制输出文件
|
|
191
|
+
// 下载文件或二进制输出文件
|