bl-common-vue3 3.8.108 → 3.8.110

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": "bl-common-vue3",
3
- "version": "3.8.108",
3
+ "version": "3.8.110",
4
4
  "main": "index.js",
5
5
  "module": "index.js",
6
6
  "description": "bailing vue3 common components lib",
@@ -41,7 +41,9 @@
41
41
  "file-saver": "^2.0.5",
42
42
  "style-resources-loader": "^1.5.0"
43
43
  },
44
- "dependencies": {},
44
+ "dependencies": {
45
+ "xlsx-js-style": "^1.2.0"
46
+ },
45
47
  "devDependencies": {
46
48
  "@vue/cli-plugin-babel": "~4.5.13",
47
49
  "@vue/cli-plugin-eslint": "~4.5.13",
@@ -1,8 +1,193 @@
1
- import fs from "file-saver";
2
- import * as XLSX from "kj-xlsx";
3
- export function xlsx(json, fields, filename = ".xlsx") {
4
- //导出xlsx
5
- json.forEach((item) => {
1
+ import fs from "file-saver";
2
+ import * as LegacyXLSX from "kj-xlsx";
3
+
4
+ const DEFAULT_COLUMN_WIDTH = 16;
5
+ const MAX_COLUMN_WIDTH = 36;
6
+ const HEADER_ROW_HEIGHT = 30;
7
+ const DATA_ROW_HEIGHT = 24;
8
+
9
+ const createBorder = (color) => ({
10
+ top: { style: "thin", color: { rgb: color } },
11
+ bottom: { style: "thin", color: { rgb: color } },
12
+ left: { style: "thin", color: { rgb: color } },
13
+ right: { style: "thin", color: { rgb: color } },
14
+ });
15
+
16
+ const getStyledExportOptions = (options = {}) => {
17
+ // 公共导出默认使用统一基础样式,特殊旧模板仍可通过 enabled: false 关闭。
18
+ const {
19
+ enabled = true,
20
+ detailFields = [],
21
+ fieldAlignments = {},
22
+ columnWidths = {},
23
+ } = options;
24
+
25
+ return {
26
+ enabled,
27
+ detailFields: Array.isArray(detailFields) ? detailFields : [],
28
+ fieldAlignments:
29
+ fieldAlignments && typeof fieldAlignments === "object"
30
+ ? fieldAlignments
31
+ : {},
32
+ columnWidths:
33
+ columnWidths && typeof columnWidths === "object" ? columnWidths : {},
34
+ };
35
+ };
36
+
37
+ export function getVerticalMergeCells(json, fields, options = {}) {
38
+ const { groupField, mergeFields = [] } = options;
39
+ if (!groupField || !Array.isArray(mergeFields) || !mergeFields.length) {
40
+ return [];
41
+ }
42
+
43
+ const fieldKeys = Object.keys(fields);
44
+ const mergeColumnIndexes = mergeFields
45
+ .map((field) => fieldKeys.indexOf(field))
46
+ .filter((columnIndex) => columnIndex >= 0);
47
+ if (!mergeColumnIndexes.length) {
48
+ return [];
49
+ }
50
+
51
+ const merges = [];
52
+ let groupStartIndex = 0;
53
+
54
+ const appendGroupMerges = (groupEndIndex) => {
55
+ const groupValue = json[groupStartIndex]?.[groupField];
56
+ if (
57
+ groupEndIndex <= groupStartIndex ||
58
+ groupValue === undefined ||
59
+ groupValue === null ||
60
+ groupValue === ""
61
+ ) {
62
+ return;
63
+ }
64
+
65
+ mergeColumnIndexes.forEach((columnIndex) => {
66
+ merges.push({
67
+ // 工作表第 0 行是表头,数据行需要整体向下偏移一行。
68
+ s: { r: groupStartIndex + 1, c: columnIndex },
69
+ e: { r: groupEndIndex + 1, c: columnIndex },
70
+ });
71
+ });
72
+ };
73
+
74
+ for (let rowIndex = 1; rowIndex <= json.length; rowIndex++) {
75
+ const groupChanged =
76
+ rowIndex === json.length ||
77
+ json[rowIndex]?.[groupField] !== json[groupStartIndex]?.[groupField];
78
+ if (!groupChanged) {
79
+ continue;
80
+ }
81
+
82
+ appendGroupMerges(rowIndex - 1);
83
+ groupStartIndex = rowIndex;
84
+ }
85
+
86
+ return merges;
87
+ }
88
+
89
+ const getAutoColumnWidths = (json, fields, columnWidths = {}) => {
90
+ return Object.entries(fields).map(([field, title]) => {
91
+ const configuredWidth = Number(columnWidths[field]);
92
+ if (configuredWidth > 0) {
93
+ return { wch: configuredWidth };
94
+ }
95
+
96
+ const contentWidth = json.reduce((width, item) => {
97
+ const valueLength = String(item?.[field] ?? "").length;
98
+ return Math.max(width, valueLength);
99
+ }, String(title ?? "").length);
100
+
101
+ return {
102
+ wch: Math.min(MAX_COLUMN_WIDTH, Math.max(DEFAULT_COLUMN_WIDTH, contentWidth + 2)),
103
+ };
104
+ });
105
+ };
106
+
107
+ const applyStyledExport = (XLSX, ws, json, fields, options) => {
108
+ const fieldKeys = Object.keys(fields);
109
+ const detailFieldSet = new Set(options.detailFields);
110
+ const headerBorder = createBorder("FFB4C7E7");
111
+ const bodyBorder = createBorder("FFD9E2F3");
112
+
113
+ fieldKeys.forEach((field, columnIndex) => {
114
+ const headerCell = ws[XLSX.utils.encode_cell({ r: 0, c: columnIndex })];
115
+ if (headerCell) {
116
+ const isDetailField = detailFieldSet.has(field);
117
+ headerCell.s = {
118
+ font: {
119
+ name: "Microsoft YaHei",
120
+ sz: 11,
121
+ bold: true,
122
+ color: { rgb: isDetailField ? "FF1F4E78" : "FFFFFFFF" },
123
+ },
124
+ fill: {
125
+ patternType: "solid",
126
+ fgColor: { rgb: isDetailField ? "FFDCE6F1" : "FF4472C4" },
127
+ },
128
+ alignment: {
129
+ horizontal: "center",
130
+ vertical: "center",
131
+ wrapText: true,
132
+ },
133
+ border: headerBorder,
134
+ };
135
+ }
136
+
137
+ json.forEach((item, dataIndex) => {
138
+ const cell = ws[
139
+ XLSX.utils.encode_cell({ r: dataIndex + 1, c: columnIndex })
140
+ ];
141
+ if (!cell) {
142
+ return;
143
+ }
144
+
145
+ const isDetailField = detailFieldSet.has(field);
146
+ cell.s = {
147
+ font: {
148
+ name: "Microsoft YaHei",
149
+ sz: 10,
150
+ color: { rgb: "FF333333" },
151
+ },
152
+ fill: {
153
+ patternType: "solid",
154
+ fgColor: { rgb: isDetailField ? "FFF7FAFD" : "FFFFFFFF" },
155
+ },
156
+ alignment: {
157
+ horizontal: options.fieldAlignments[field] || "left",
158
+ vertical: "center",
159
+ wrapText: true,
160
+ },
161
+ border: bodyBorder,
162
+ };
163
+ });
164
+ });
165
+
166
+ ws["!cols"] = getAutoColumnWidths(json, fields, options.columnWidths);
167
+ ws["!rows"] = [
168
+ { hpt: HEADER_ROW_HEIGHT },
169
+ ...json.map(() => ({ hpt: DATA_ROW_HEIGHT })),
170
+ ];
171
+ };
172
+
173
+ // 暴露工作簿构建方法,便于对合并区域和样式做真实 xlsx 回归检查。
174
+ export async function createWorkbook(
175
+ json,
176
+ fields,
177
+ mergeOptions = {},
178
+ styleOptions = {}
179
+ ) {
180
+ const merges = getVerticalMergeCells(json, fields, mergeOptions);
181
+ const styledExportOptions = getStyledExportOptions(styleOptions);
182
+ const styledXLSXModule = styledExportOptions.enabled
183
+ ? await import("xlsx-js-style")
184
+ : null;
185
+ const XLSX = styledXLSXModule?.default || styledXLSXModule || LegacyXLSX;
186
+ const sourceJson = styledExportOptions.enabled
187
+ ? json.map((item) => ({ ...item }))
188
+ : null;
189
+ //导出xlsx
190
+ json.forEach((item) => {
6
191
  for (let i in item) {
7
192
  // eslint-disable-next-line no-prototype-builtins
8
193
  if (fields.hasOwnProperty(i)) {
@@ -11,11 +196,33 @@ export function xlsx(json, fields, filename = ".xlsx") {
11
196
  delete item[i]; //删除原先的对象属性
12
197
  }
13
198
  });
14
- let sheetName = filename.replace(/[\\/?*[\]:]/g, "").slice(0, 31) || "Sheet1"; //excel的文件名称
15
- let wb = XLSX.utils.book_new(); //工作簿对象包含一SheetNames数组,以及一个表对象映射表名称到表对象。XLSX.utils.book_new实用函数创建一个新的工作簿对象。
16
- let ws = XLSX.utils.json_to_sheet(json, { header: Object.values(fields) }); //将JS对象数组转换为工作表。
17
- wb.SheetNames.push(sheetName);
18
- wb.Sheets[sheetName] = ws;
199
+ let wb = XLSX.utils.book_new(); //工作簿对象包含一SheetNames数组,以及一个表对象映射表名称到表对象。XLSX.utils.book_new实用函数创建一个新的工作簿对象。
200
+ let ws = XLSX.utils.json_to_sheet(json, { header: Object.values(fields) }); //将JS对象数组转换为工作表。
201
+ if (merges.length) {
202
+ ws["!merges"] = merges;
203
+ }
204
+ if (styledExportOptions.enabled) {
205
+ applyStyledExport(XLSX, ws, sourceJson, fields, styledExportOptions);
206
+ }
207
+ return { XLSX, wb, ws };
208
+ }
209
+
210
+ export async function xlsx(
211
+ json,
212
+ fields,
213
+ filename = ".xlsx",
214
+ mergeOptions = {},
215
+ styleOptions = {}
216
+ ) {
217
+ const { XLSX, wb, ws } = await createWorkbook(
218
+ json,
219
+ fields,
220
+ mergeOptions,
221
+ styleOptions
222
+ );
223
+ const sheetName = filename.replace(/[\\/?*[\]:]/g, "").slice(0, 31) || "Sheet1"; //excel的文件名称
224
+ wb.SheetNames.push(sheetName);
225
+ wb.Sheets[sheetName] = ws;
19
226
  const defaultCellStyle = {
20
227
  font: { name: "Verdana", sz: 13, color: "FF00FF88" },
21
228
  fill: { fgColor: { rgb: "FFFFAA00" } },
@@ -67,6 +67,14 @@ export default defineComponent({
67
67
  type: Object,
68
68
  default: () => ({})
69
69
  },
70
+ merge_options: { //导出表格纵向合并配置
71
+ type: Object,
72
+ default: () => ({})
73
+ },
74
+ style_options: { //导出表格样式配置
75
+ type: Object,
76
+ default: () => ({})
77
+ },
70
78
  custom_result: { //导出表格头部
71
79
  type: Object,
72
80
  // eslint-disable-next-line vue/require-valid-default-prop
@@ -121,9 +129,9 @@ export default defineComponent({
121
129
  context.emit("customDataFunc" , state.ExportList)
122
130
  }else {
123
131
  console.log('导出文档')
124
- setTimeout(() => {
125
- let list = utils.deepClone(state.ExportList);
126
- xlsx(list, removeUndefined(props.columnstitle), props.title);
132
+ setTimeout(async () => {
133
+ let list = utils.deepClone(state.ExportList);
134
+ await xlsx(list, removeUndefined(props.columnstitle), props.title, props.merge_options, props.style_options);
127
135
  context.emit("success", t('ExportComponents.index.921182-2'));
128
136
  closeModal()
129
137
  }, 200);
@@ -138,9 +146,9 @@ export default defineComponent({
138
146
  context.emit("customDataFunc" , state.ExportList)
139
147
  }else {
140
148
  console.log('导出文档')
141
- setTimeout(() => {
142
- let list = utils.deepClone(state.ExportList);
143
- xlsx(list, removeUndefined(props.columnstitle), props.title);
149
+ setTimeout(async () => {
150
+ let list = utils.deepClone(state.ExportList);
151
+ await xlsx(list, removeUndefined(props.columnstitle), props.title, props.merge_options, props.style_options);
144
152
  context.emit("success", t('ExportComponents.index.921182-2'));
145
153
  closeModal()
146
154
  }, 200);
@@ -158,9 +166,9 @@ export default defineComponent({
158
166
  watch(
159
167
  () => props.custom_result,
160
168
  (val) => {
161
- setTimeout(() => {
162
- let list = utils.deepClone(val);
163
- xlsx(list, removeUndefined(props.columnstitle), props.title);
169
+ setTimeout(async () => {
170
+ let list = utils.deepClone(val);
171
+ await xlsx(list, removeUndefined(props.columnstitle), props.title, props.merge_options, props.style_options);
164
172
  context.emit("success", t('ExportComponents.index.921182-2'));
165
173
  closeModal()
166
174
  }, 200);