@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyfox2000/webui",
3
- "version": "1.4.17",
3
+ "version": "1.4.19",
4
4
  "description": "后台前端通用组件定义",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -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).map(([field, value]) => ({
60
- id: Date.now() + Math.random(),
61
- field,
62
- value,
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
- v-if="!selectList || selectList.length === 0"
129
- v-model:value="item.field"
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
- v-model:value="item.value"
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.map((col) => ({
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.map((row, index) => {
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
- // 6. 创建并下载文件
73
- const blob = new Blob([`\uFEFF${csvContent}`], { type: 'text/csv' });
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.map((col) => ({
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.map((row, index) => {
128
- const newRow: Record<string, any> = { ...row };
129
-
130
- exportColumns.forEach((col) => {
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
+ // 下载文件或二进制输出文件