cloud-web-corejs 1.0.54-dev.807 → 1.0.54-dev.809

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.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 自研区域选取的高亮样式。
3
+ *
4
+ * 高亮是把 class 直接打在 td 上(见同目录 index.js),而不是画绝对定位的遮罩:
5
+ * 业务表常配 row-config.height: auto + autosize 的 textarea,行高会在输入过程中变化,
6
+ * 遮罩会漂移;而且有固定列时同一个单元格在 left/main/right 三个 wrapper 里各有一份,
7
+ * 打 class 一次就能全覆盖。
8
+ *
9
+ * 边框走 ::after 伪元素而不是 border,避免占用盒模型把单元格内容挤动。
10
+ */
11
+ $vxe-cell-area-color: #409eff !default;
12
+ $vxe-cell-area-background-color: rgba(64, 158, 255, 0.12) !default;
13
+ $vxe-cell-area-border-width: 2px !default;
14
+
15
+ .vxe-table {
16
+ // 框选过程中禁掉文字选中,否则拖拽会连带选中单元格文本
17
+ &.drag--range {
18
+ .vxe-table--main-wrapper,
19
+ .vxe-table--fixed-left-wrapper,
20
+ .vxe-table--fixed-right-wrapper {
21
+ user-select: none;
22
+
23
+ * {
24
+ user-select: none;
25
+ }
26
+ }
27
+ }
28
+
29
+ .vxe-body--column {
30
+ &.col--area,
31
+ &.col--copy-area {
32
+ position: relative;
33
+
34
+ &::after {
35
+ content: "";
36
+ position: absolute;
37
+ top: 0;
38
+ right: 0;
39
+ bottom: 0;
40
+ left: 0;
41
+ pointer-events: none;
42
+ z-index: 1;
43
+ }
44
+ }
45
+
46
+ &.col--area {
47
+ background-color: $vxe-cell-area-background-color;
48
+ }
49
+
50
+ // 只有落在选区边界上的单元格画对应那条边,四条拼起来就是完整矩形
51
+ &.col--area-t::after {
52
+ border-top: $vxe-cell-area-border-width solid $vxe-cell-area-color;
53
+ }
54
+
55
+ &.col--area-b::after {
56
+ border-bottom: $vxe-cell-area-border-width solid $vxe-cell-area-color;
57
+ }
58
+
59
+ &.col--area-l::after {
60
+ border-left: $vxe-cell-area-border-width solid $vxe-cell-area-color;
61
+ }
62
+
63
+ &.col--area-r::after {
64
+ border-right: $vxe-cell-area-border-width solid $vxe-cell-area-color;
65
+ }
66
+
67
+ // 锚点格:shift 扩选与方向键移动的基准,不铺底色以示区别
68
+ &.col--area-active {
69
+ background-color: transparent;
70
+ }
71
+
72
+ // 复制源:虚线框,粘贴或取消后清除
73
+ &.col--copy-area::after {
74
+ border: 1px dashed $vxe-cell-area-color;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,224 @@
1
+ /**
2
+ * 区域选取插件的纯函数部分:可选列/行的口径、矩形运算、剪贴板 TSV 编解码。
3
+ * 抽出来是为了能脱离 vxe 实例单测,index.js 只负责跟 vxe 的钩子对接。
4
+ */
5
+
6
+ // 这些列没有业务值(复选框/单选/序号/展开),不参与区域选取与复制粘贴
7
+ const NOT_AREA_COLUMN_TYPES = ["checkbox", "radio", "seq", "expand"];
8
+
9
+ /**
10
+ * 参与区域选取的列。索引空间以本函数返回的数组为准,
11
+ * 复制粘贴的列对齐、方向键移动都按这个下标算。
12
+ */
13
+ export function getAreaColumns(columns, excludeFields) {
14
+ let excludes = excludeFields || [];
15
+ return (columns || []).filter((column) => {
16
+ if (!column) return false;
17
+ let field = column.property || column.field;
18
+ if (!field) return false;
19
+ if (NOT_AREA_COLUMN_TYPES.includes(column.type)) return false;
20
+ return !excludes.includes(field);
21
+ });
22
+ }
23
+
24
+ export function getColumnField(column) {
25
+ return column ? column.property || column.field : null;
26
+ }
27
+
28
+ /**
29
+ * 下标钳制到 [0, size - 1],size 为 0 时返回 -1 表示无可用格。
30
+ *
31
+ * 只接受**算术下标**(锚点 ± 偏移,越界钳回边界是对的,比如在第 0 行按 ↑ 得到 -1 应该停在 0)。
32
+ * **不要拿 indexOf 的结果喂它** —— 那里的 -1 是"找不到",钳成 0 会把查找失败
33
+ * 静默变成"第 0 行",选区反向铺开。
34
+ */
35
+ export function clampIndex(index, size) {
36
+ if (!size || size < 1) return -1;
37
+ if (index < 0) return 0;
38
+ if (index > size - 1) return size - 1;
39
+ return index;
40
+ }
41
+
42
+ /**
43
+ * 由锚点与焦点格构造矩形。锚点是 shift 扩选与方向键移动的基准,
44
+ * 始终保留在区域里,不随 start/end 归一化而丢失。
45
+ */
46
+ export function buildArea(anchorRow, anchorCol, focusRow, focusCol) {
47
+ return {
48
+ anchorRow: anchorRow,
49
+ anchorCol: anchorCol,
50
+ focusRow: focusRow,
51
+ focusCol: focusCol,
52
+ startRow: Math.min(anchorRow, focusRow),
53
+ endRow: Math.max(anchorRow, focusRow),
54
+ startCol: Math.min(anchorCol, focusCol),
55
+ endCol: Math.max(anchorCol, focusCol),
56
+ };
57
+ }
58
+
59
+ /** 折叠成单格 */
60
+ export function buildCellArea(rowIndex, colIndex) {
61
+ return buildArea(rowIndex, colIndex, rowIndex, colIndex);
62
+ }
63
+
64
+ /** 以现有锚点扩选到目标格;没有区域时退化为单格 */
65
+ export function extendArea(area, focusRow, focusCol) {
66
+ if (!area) return buildCellArea(focusRow, focusCol);
67
+ return buildArea(area.anchorRow, area.anchorCol, focusRow, focusCol);
68
+ }
69
+
70
+ /**
71
+ * 方向键移动。extend 为真时移动焦点边(Shift+方向键),
72
+ * 否则整体折叠成单格移动到新位置。
73
+ */
74
+ export function moveArea(area, offsetRow, offsetCol, rowSize, colSize, extend) {
75
+ if (!area) return null;
76
+ if (extend) {
77
+ let focusRow = clampIndex(area.focusRow + offsetRow, rowSize);
78
+ let focusCol = clampIndex(area.focusCol + offsetCol, colSize);
79
+ return buildArea(area.anchorRow, area.anchorCol, focusRow, focusCol);
80
+ }
81
+ let rowIndex = clampIndex(area.anchorRow + offsetRow, rowSize);
82
+ let colIndex = clampIndex(area.anchorCol + offsetCol, colSize);
83
+ return buildCellArea(rowIndex, colIndex);
84
+ }
85
+
86
+ /**
87
+ * Tab/Enter 的顺序移动:列走到头换行。越界则停在原地。
88
+ */
89
+ export function moveNextCell(area, isRowFirst, isBack, rowSize, colSize) {
90
+ if (!area) return null;
91
+ let rowIndex = area.anchorRow;
92
+ let colIndex = area.anchorCol;
93
+ let step = isBack ? -1 : 1;
94
+ if (isRowFirst) {
95
+ colIndex += step;
96
+ if (colIndex > colSize - 1) {
97
+ colIndex = 0;
98
+ rowIndex += 1;
99
+ } else if (colIndex < 0) {
100
+ colIndex = colSize - 1;
101
+ rowIndex -= 1;
102
+ }
103
+ } else {
104
+ rowIndex += step;
105
+ }
106
+ if (rowIndex < 0 || rowIndex > rowSize - 1) {
107
+ return area;
108
+ }
109
+ return buildCellArea(rowIndex, clampIndex(colIndex, colSize));
110
+ }
111
+
112
+ /**
113
+ * 粘贴时需要补几行:从 startRowIndex 起放下 pasteRowSize 行,超出当前行数的部分。
114
+ */
115
+ export function getIncrementRowSize(startRowIndex, pasteRowSize, rowSize) {
116
+ let need = startRowIndex + pasteRowSize - rowSize;
117
+ return need > 0 ? need : 0;
118
+ }
119
+
120
+ const NEED_QUOTE_RE = /[\t\r\n"]/;
121
+
122
+ /** 单元格文本 -> 剪贴板片段,按 Excel 约定转义 */
123
+ export function encodeClipCell(value) {
124
+ let text = value === null || value === undefined ? "" : "" + value;
125
+ if (NEED_QUOTE_RE.test(text)) {
126
+ return '"' + text.replace(/"/g, '""') + '"';
127
+ }
128
+ return text;
129
+ }
130
+
131
+ /** 二维数组 -> TSV 文本 */
132
+ export function encodeClipText(matrix) {
133
+ return (matrix || [])
134
+ .map((row) => (row || []).map(encodeClipCell).join("\t"))
135
+ .join("\n");
136
+ }
137
+
138
+ const HTML_ESCAPE_MAP = {
139
+ "&": "&amp;",
140
+ "<": "&lt;",
141
+ ">": "&gt;",
142
+ '"': "&quot;",
143
+ };
144
+
145
+ export function escapeClipHtml(value) {
146
+ let text = value === null || value === undefined ? "" : "" + value;
147
+ return text.replace(/[&<>"]/g, function (ch) {
148
+ return HTML_ESCAPE_MAP[ch];
149
+ });
150
+ }
151
+
152
+ /**
153
+ * 二维数组 -> HTML 表格片段。
154
+ * 官方 copyCellArea() 的返回值是 { text, html },只写 text/plain 的话
155
+ * 粘进 Word / 邮件 / 富文本编辑器会丢掉表格结构,塌成一行文本。
156
+ */
157
+ export function encodeClipHtml(matrix) {
158
+ let trList = (matrix || []).map(function (row) {
159
+ let tdList = (row || []).map(function (cell) {
160
+ // 格内换行要转 <br>,否则富文本里同样会被挤成一行
161
+ return "<td>" + escapeClipHtml(cell).replace(/\r?\n/g, "<br>") + "</td>";
162
+ });
163
+ return "<tr>" + tdList.join("") + "</tr>";
164
+ });
165
+ return "<table><tbody>" + trList.join("") + "</tbody></table>";
166
+ }
167
+
168
+ /**
169
+ * TSV 文本 -> 二维数组。走状态机而不是 split,
170
+ * 因为 Excel 会把含换行/制表符的单元格用引号包起来。
171
+ */
172
+ export function decodeClipText(text) {
173
+ let source = text === null || text === undefined ? "" : "" + text;
174
+ if (!source) return [];
175
+ let rows = [];
176
+ let row = [];
177
+ let cell = "";
178
+ let inQuote = false;
179
+ let cellStart = true; // 只有单元格开头的引号才算包裹符
180
+ for (let i = 0; i < source.length; i++) {
181
+ let ch = source[i];
182
+ if (inQuote) {
183
+ if (ch === '"') {
184
+ if (source[i + 1] === '"') {
185
+ cell += '"';
186
+ i++;
187
+ } else {
188
+ inQuote = false;
189
+ }
190
+ } else {
191
+ cell += ch;
192
+ }
193
+ continue;
194
+ }
195
+ if (ch === '"' && cellStart) {
196
+ inQuote = true;
197
+ cellStart = false;
198
+ continue;
199
+ }
200
+ if (ch === "\t") {
201
+ row.push(cell);
202
+ cell = "";
203
+ cellStart = true;
204
+ continue;
205
+ }
206
+ if (ch === "\n" || ch === "\r") {
207
+ if (ch === "\r" && source[i + 1] === "\n") i++;
208
+ row.push(cell);
209
+ rows.push(row);
210
+ row = [];
211
+ cell = "";
212
+ cellStart = true;
213
+ continue;
214
+ }
215
+ cell += ch;
216
+ cellStart = false;
217
+ }
218
+ // 末尾没有换行时收尾;有换行时不要多造一个空行
219
+ if (cell !== "" || row.length || inQuote) {
220
+ row.push(cell);
221
+ rows.push(row);
222
+ }
223
+ return rows;
224
+ }
@@ -103,6 +103,12 @@ export default {
103
103
  targetRef: this.field.options.tableRef || null,
104
104
  pageSize: this.field.options.exportPageSize || null,
105
105
  showImageAtTable: this.field.options.showImageAtTable || null,
106
+ // 明细导出的列 / 脚本编码 / 取数参数三项各自独立:按钮上维护了哪项就以哪项为准,
107
+ // 留空的那项沿用数据表格属性上那套。参数脚本原样下发,由数据表格求值(this 是
108
+ // 表格组件,与配在表格属性上时同义)
109
+ exportItemColumns: this.field.options.exportItemColumns || null,
110
+ exportItemScriptCode: this.field.options.exportItemScriptCode || null,
111
+ exportItemParam: this.field.options.exportItemParam || null,
106
112
  };
107
113
  let tableExportParam = this.handleCustomEvent(this.field.options.tableExportParam);
108
114
  let options = { ...opt, ...tableExportParam, type: type };
@@ -320,7 +320,7 @@
320
320
  <el-form-item :label="i18nt('明细导出脚本编码')" class="form-item-label-top">
321
321
  <el-input v-model="optionModel.exportItemScriptCode"></el-input>
322
322
  </el-form-item>
323
- <el-form-item label="自定义明细导出参数">
323
+ <el-form-item label="明细导出查询参数">
324
324
  <a
325
325
  href="javascript:void(0);"
326
326
  class="a-link link-oneLind"
@@ -1107,15 +1107,15 @@ export default {
1107
1107
  </style>
1108
1108
 
1109
1109
  <style scoped>
1110
- .form-section-label {
1111
- margin: 16px 0 8px;
1112
- padding-left: 8px;
1113
- color: rgba(0, 0, 0, 0.65);
1114
- font-size: 13px;
1115
- font-weight: 600;
1116
- line-height: 1.3;
1117
- background: rgba(0, 0, 0, 0.04);
1118
- padding: 4px 0 4px 8px;
1110
+ .form-section-label {
1111
+ margin: 16px 0 8px;
1112
+ padding-left: 8px;
1113
+ color: rgba(0, 0, 0, 0.65);
1114
+ font-size: 13px;
1115
+ font-weight: 600;
1116
+ line-height: 1.3;
1117
+ background: rgba(0, 0, 0, 0.04);
1118
+ padding: 4px 0 4px 8px;
1119
1119
  }
1120
1120
 
1121
1121
  .label-tip-icon {
@@ -353,7 +353,13 @@ export default {
353
353
  return;
354
354
  }
355
355
  }
356
- this.optionModel.exportItemColumns = this.$baseLodash.cloneDeep(this.tableData);
356
+ // 用 $set:导出按钮是后加的宿主,历史表单 JSON 里没有 exportItemColumns 这个键,
357
+ // 直接赋值不会触发「已维护」文案更新
358
+ this.$set(
359
+ this.optionModel,
360
+ "exportItemColumns",
361
+ this.$baseLodash.cloneDeep(this.tableData)
362
+ );
357
363
  this.closeHandle();
358
364
  },
359
365
  generateRow() {
@@ -34,16 +34,59 @@
34
34
  <i class="el-icon-edit"></i>
35
35
  </a>
36
36
  </el-form-item>
37
+ <el-form-item label="明细导出脚本编码" class="form-item-label-top">
38
+ <el-input v-model="optionModel.exportItemScriptCode"></el-input>
39
+ </el-form-item>
40
+ <el-form-item label="明细导出查询参数">
41
+ <a
42
+ href="javascript:void(0);"
43
+ class="a-link link-oneLind"
44
+ @click="editEventHandler('exportItemParam', tableConfigParams)"
45
+ >
46
+ <span>{{ optionModel.exportItemParam }}</span>
47
+ <i class="el-icon-edit"></i>
48
+ </a>
49
+ </el-form-item>
50
+ <el-form-item label="明细导出列维护">
51
+ <a
52
+ href="javascript:void(0);"
53
+ class="a-link link-oneLind"
54
+ @click="showExportItemColumnsDialog = true"
55
+ >
56
+ <span>{{
57
+ optionModel.exportItemColumns && optionModel.exportItemColumns.length
58
+ ? "已维护"
59
+ : null
60
+ }}</span>
61
+ <i class="el-icon-edit"></i>
62
+ </a>
63
+ </el-form-item>
64
+ <el-form-item label-width="0">
65
+ <div class="export-item-columns-tip">
66
+ 脚本编码、查询参数、导出列三项各自独立:留空的那项沿用数据表格属性上配的,维护了就以本按钮为准。
67
+ </div>
68
+ </el-form-item>
69
+ <exportItemColumnsDialog
70
+ v-if="showExportItemColumnsDialog"
71
+ :visiable.sync="showExportItemColumnsDialog"
72
+ :designer="designer"
73
+ :selectedWidget="selectedWidget"
74
+ :optionModel="optionModel"
75
+ />
37
76
  </div>
38
77
  </template>
39
78
 
40
79
  <script>
41
80
  import i18n from "../../../../utils/i18n";
42
81
  import eventMixin from "../event-handler/eventMixin";
82
+ import exportItemColumnsDialog from "../container-data-table/exportItemColumns-dialog.vue";
43
83
 
44
84
  export default {
45
85
  name: "select-export-item-button-editor",
46
86
  mixins: [i18n, eventMixin],
87
+ components: {
88
+ exportItemColumnsDialog,
89
+ },
47
90
  props: {
48
91
  designer: Object,
49
92
  selectedWidget: Object,
@@ -52,6 +95,9 @@ export default {
52
95
  data() {
53
96
  return {
54
97
  eventParams: [],
98
+ // 与数据表格属性面板同一套:脚本里可直接用 dataId / formCode
99
+ tableConfigParams: ["dataId", "formCode"],
100
+ showExportItemColumnsDialog: false,
55
101
  };
56
102
  },
57
103
  methods: {
@@ -71,4 +117,9 @@ export default {
71
117
  ::v-deep.export-divider .el-divider__text.is-center {
72
118
  width: 162px;
73
119
  }
120
+ .export-item-columns-tip {
121
+ line-height: 18px;
122
+ color: #909399;
123
+ font-size: 12px;
124
+ }
74
125
  </style>
@@ -3318,6 +3318,10 @@ export const advancedFields = [
3318
3318
  showImageAtTable: false,
3319
3319
  selectExportItemFlag: 1,
3320
3320
  exportItemConditionEnabled: !1,
3321
+ // 按钮级明细导出配置:三项各自独立,留空的那项沿用数据表格属性上维护的
3322
+ exportItemColumns: [],
3323
+ exportItemScriptCode: null,
3324
+ exportItemParam: "",
3321
3325
 
3322
3326
  onCreated: "",
3323
3327
  onMounted: "",
@@ -2442,76 +2442,7 @@ modules = {
2442
2442
  };
2443
2443
  }
2444
2444
 
2445
- let exportItemConfig = null;
2446
- if (
2447
- this.widget.options.exportItemColumns &&
2448
- this.widget.options.exportItemColumns.length > 0
2449
- ) {
2450
- let formatKeys = [
2451
- "d1",
2452
- "d2",
2453
- "d3",
2454
- "d4",
2455
- "d5",
2456
- "n1",
2457
- "n2",
2458
- "n3",
2459
- "n4",
2460
- "n5",
2461
- "n6",
2462
- "n7",
2463
- ];
2464
- let exportItemColumns = this.widget.options.exportItemColumns.map(
2465
- (item) => {
2466
- let columnSlots = null;
2467
- let columnParams = {};
2468
- if (item.formatS === "render") {
2469
- let r = item.render
2470
- ? new Function("params", "h", item.render)
2471
- : null;
2472
- columnSlots = {
2473
- default: (params, h) => {
2474
- return r ? r.call(this, params, h) : "";
2475
- },
2476
- };
2477
- } else if (formatKeys.includes(item.formatS)) {
2478
- columnSlots = {
2479
- default: (params, h) => {
2480
- let cellValue = params.row[params.column.field];
2481
- return this.getFormatterValue(
2482
- cellValue,
2483
- item.formatS,
2484
- item.utcTransformEnabled
2485
- );
2486
- },
2487
- };
2488
- }
2489
- if (item.exportType) {
2490
- columnParams.exportType = item.exportType;
2491
- }
2492
- return {
2493
- id: item.id,
2494
- title: item.title,
2495
- field: item.field,
2496
- params: columnParams,
2497
- slots: columnSlots,
2498
- };
2499
- }
2500
- );
2501
- exportItemConfig = {
2502
- scriptCode:
2503
- this.widget.options.exportItemScriptCode ||
2504
- this.widget.options.formScriptCode,
2505
- columns: exportItemColumns,
2506
- param: () => {
2507
- if (this.widget.options.exportItemParam) {
2508
- return this.handleCustomParam(
2509
- this.widget.options.exportItemParam
2510
- );
2511
- }
2512
- },
2513
- };
2514
- }
2445
+ let exportItemConfig = this.buildExportItemConfig();
2515
2446
  let rowConfig = {};
2516
2447
  if (this.widget.options.tableRowHeight) {
2517
2448
  rowConfig.height = this.widget.options.tableRowHeight;
@@ -3228,7 +3159,9 @@ modules = {
3228
3159
  if (isItemExport(exportType)) {
3229
3160
  let $grid = this.getGridTable();
3230
3161
  let originOption = $grid.params.originOption;
3231
- let exportItemConfig = originOption.exportItemConfig || {};
3162
+ // 导出按钮上单独维护了明细列时,配置随导出参数下发,优先于表格属性那套
3163
+ let btnConfig = customParam?.exportParam?.exportItemConfig;
3164
+ let exportItemConfig = btnConfig || originOption.exportItemConfig || {};
3232
3165
  if (exportItemConfig.scriptCode) {
3233
3166
  scriptCode = exportItemConfig.scriptCode;
3234
3167
  }
@@ -3339,30 +3272,134 @@ modules = {
3339
3272
  let tableRef = this.widget.id;
3340
3273
  return tableRef;
3341
3274
  },
3342
- /** 导出前按脱敏规则剔除列,实现见 textMaskUtil.markMaskedExportColumns。 */
3343
- markMaskedColumnsForExport() {
3275
+ /**
3276
+ * 导出前按脱敏规则剔除列,实现见 textMaskUtil.markMaskedExportColumns。
3277
+ * @param {Array} [exportItemColumns] 本次明细导出实际使用的列;不传则取表格属性上配的那套。
3278
+ */
3279
+ markMaskedColumnsForExport(exportItemColumns) {
3344
3280
  markMaskedExportColumns(
3345
3281
  this.getGridTable(),
3346
3282
  buildTextMaskContext(
3347
3283
  this.getFormRef(),
3348
3284
  this.$store.getters.companyCode
3349
- )
3285
+ ),
3286
+ exportItemColumns
3350
3287
  );
3351
3288
  },
3289
+ /**
3290
+ * 明细导出列配置 → vxe 列:formatS/render 转成渲染插槽,exportType 落到 params。
3291
+ * @param {Array} rawColumns 设计器维护的明细导出列。
3292
+ * @returns {Array} vxe 列定义。
3293
+ */
3294
+ buildExportItemColumns(rawColumns) {
3295
+ let formatKeys = [
3296
+ "d1",
3297
+ "d2",
3298
+ "d3",
3299
+ "d4",
3300
+ "d5",
3301
+ "n1",
3302
+ "n2",
3303
+ "n3",
3304
+ "n4",
3305
+ "n5",
3306
+ "n6",
3307
+ "n7",
3308
+ ];
3309
+ return rawColumns.map((item) => {
3310
+ let columnSlots = null;
3311
+ let columnParams = {};
3312
+ if (item.formatS === "render") {
3313
+ let r = item.render ? new Function("params", "h", item.render) : null;
3314
+ columnSlots = {
3315
+ default: (params, h) => {
3316
+ return r ? r.call(this, params, h) : "";
3317
+ },
3318
+ };
3319
+ } else if (formatKeys.includes(item.formatS)) {
3320
+ columnSlots = {
3321
+ default: (params, h) => {
3322
+ let cellValue = params.row[params.column.field];
3323
+ return this.getFormatterValue(
3324
+ cellValue,
3325
+ item.formatS,
3326
+ item.utcTransformEnabled
3327
+ );
3328
+ },
3329
+ };
3330
+ }
3331
+ if (item.exportType) {
3332
+ columnParams.exportType = item.exportType;
3333
+ }
3334
+ return {
3335
+ id: item.id,
3336
+ title: item.title,
3337
+ field: item.field,
3338
+ params: columnParams,
3339
+ slots: columnSlots,
3340
+ };
3341
+ });
3342
+ },
3343
+ /**
3344
+ * 组装一份明细导出配置。导出列、脚本编码、自定义导出参数三项都可以由导出按钮单独维护,
3345
+ * 各自独立生效:按钮留空的那一项回落到表格属性,三项都留空就是表格属性上原来那套。
3346
+ * @param {Object} [overrides] 按钮级配置。
3347
+ * @param {Array} [overrides.columns] 明细导出列。
3348
+ * @param {String} [overrides.scriptCode] 明细导出脚本编码。
3349
+ * @param {String} [overrides.paramScript] 明细导出查询参数脚本。
3350
+ * @returns {Object|null} 两处都没维护列时返回 null。
3351
+ */
3352
+ buildExportItemConfig(overrides) {
3353
+ let options = this.widget.options;
3354
+ let o = overrides || {};
3355
+ let hasColumns = o.columns && o.columns.length;
3356
+ let columns = hasColumns ? o.columns : options.exportItemColumns;
3357
+ if (!columns || !columns.length) return null;
3358
+ let scriptCode = o.scriptCode || options.exportItemScriptCode;
3359
+ let paramScript = o.paramScript || options.exportItemParam;
3360
+ return {
3361
+ scriptCode: scriptCode || options.formScriptCode,
3362
+ columns: this.buildExportItemColumns(columns),
3363
+ // 取数参数脚本与表格上其它脚本一样走 handleCustomEvent(它负责注入 dataId /
3364
+ // formCode 并统一弹前端脚本异常)。原先写的 handleCustomParam 全库没有定义,
3365
+ // 配了「明细导出查询参数」的表格一点导出就抛 TypeError。
3366
+ param: () => {
3367
+ if (paramScript) {
3368
+ return this.handleCustomEvent(paramScript);
3369
+ }
3370
+ },
3371
+ };
3372
+ },
3352
3373
  /**
3353
3374
  * 使用统一 Excel 导出器导出当前表格。
3354
3375
  * @param {Object} option 导出参数;prefix 为空时自动使用当前服务前缀。
3376
+ * option.exportItemColumns / exportItemScriptCode / exportItemParam 为按钮上维护的
3377
+ * 明细导出列、脚本编码与取数参数脚本,三项各自独立:传了哪项就以哪项为准(同一张表格
3378
+ * 可以挂多个明细导出按钮、各导各自的列、各走各自的脚本),组装成 exportItemConfig 随
3379
+ * 导出参数下发,下游(字段选择弹框 / 分页取数 / 脱敏剔除)一律优先读它,读不到才回落
3380
+ * 表格属性那套。
3355
3381
  */
3356
3382
  exportData(option) {
3357
- this.markMaskedColumnsForExport();
3383
+ let exportOption = { ...option };
3384
+ let itemColumns = exportOption.exportItemColumns;
3385
+ let itemScriptCode = exportOption.exportItemScriptCode;
3386
+ let itemParam = exportOption.exportItemParam;
3387
+ delete exportOption.exportItemColumns;
3388
+ delete exportOption.exportItemScriptCode;
3389
+ delete exportOption.exportItemParam;
3390
+ let hasOverride = itemColumns?.length || itemScriptCode || itemParam;
3391
+ if (isItemExport(exportOption.type) && hasOverride) {
3392
+ exportOption.exportItemConfig = this.buildExportItemConfig({
3393
+ columns: itemColumns,
3394
+ scriptCode: itemScriptCode,
3395
+ paramScript: itemParam,
3396
+ });
3397
+ }
3398
+ this.markMaskedColumnsForExport(exportOption.exportItemConfig?.columns);
3358
3399
  let tableRef = this.getTableRef();
3359
3400
  let serviceName = this.getFormRef().reportTemplate.serviceName;
3360
- option.prefix = option.prefix || "/" + serviceName;
3361
- let exportTableTarget = this.$refs.exportTable;
3362
- let getExportTableRef = () => {
3363
- return exportTableTarget;
3364
- };
3365
- this.$excelExport({ targetRef: tableRef, ...option });
3401
+ exportOption.prefix = exportOption.prefix || "/" + serviceName;
3402
+ this.$excelExport({ targetRef: tableRef, ...exportOption });
3366
3403
  },
3367
3404
  async deleteRow(row, rowIndex) {
3368
3405
  let isTreeTable = this.widget.options.isTreeTable;