cloud-web-corejs 1.0.54-dev.808 → 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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cloud-web-corejs",
3
3
  "private": false,
4
- "version": "1.0.54-dev.808",
4
+ "version": "1.0.54-dev.809",
5
5
  "scripts": {
6
6
  "dev": "vue-cli-service serve",
7
7
  "dev:micro": "vue-cli-service serve --port 17527",
@@ -1007,6 +1007,20 @@ const cellAreaMixin = {
1007
1007
  return this.$nextTick();
1008
1008
  },
1009
1009
  },
1010
+ created() {
1011
+ // 只管开了区域选取的表格,不去动别的表格的编辑行为
1012
+ if (!this.mouseConfig || !this.mouseOpts || !this.mouseOpts.area) return;
1013
+ // 把 core 的 handleFocus 包一层,补一次兜底聚焦。
1014
+ // 不能改成监听 edit-actived:mode=row 时 core 是在 checkValidate('blur').then() 里
1015
+ // 异步调 handleActived 的,而且同一行换列走的是「不发事件、只 setTimeout(handleFocus)」
1016
+ // 那条分支(edit/mixin.js:442)。包 handleFocus 才能覆盖双击/换列/API/键盘所有入口。
1017
+ const rawHandleFocus = this.handleFocus;
1018
+ const $table = this;
1019
+ this.handleFocus = function (params, evnt) {
1020
+ if (rawHandleFocus) rawHandleFocus.call($table, params, evnt);
1021
+ if (params) focusCellEditor($table, params.row, params.column);
1022
+ };
1023
+ },
1010
1024
  updated() {
1011
1025
  // Vue 重渲染会把打在 td 上的高亮 class 冲掉,这里补回来
1012
1026
  if (this.mouseConfig && this.mouseOpts && this.mouseOpts.area) {
@@ -103,8 +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
- // 按钮上单独维护了导出列就以按钮为准,留空则沿用数据表格属性上那套
106
+ // 明细导出的列 / 脚本编码 / 取数参数三项各自独立:按钮上维护了哪项就以哪项为准,
107
+ // 留空的那项沿用数据表格属性上那套。参数脚本原样下发,由数据表格求值(this 是
108
+ // 表格组件,与配在表格属性上时同义)
107
109
  exportItemColumns: this.field.options.exportItemColumns || null,
110
+ exportItemScriptCode: this.field.options.exportItemScriptCode || null,
111
+ exportItemParam: this.field.options.exportItemParam || null,
108
112
  };
109
113
  let tableExportParam = this.handleCustomEvent(this.field.options.tableExportParam);
110
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 {
@@ -34,6 +34,19 @@
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>
37
50
  <el-form-item label="明细导出列维护">
38
51
  <a
39
52
  href="javascript:void(0);"
@@ -50,7 +63,7 @@
50
63
  </el-form-item>
51
64
  <el-form-item label-width="0">
52
65
  <div class="export-item-columns-tip">
53
- 不维护则按数据表格属性上的明细导出列导出;维护后本按钮只导出这里配的列。
66
+ 脚本编码、查询参数、导出列三项各自独立:留空的那项沿用数据表格属性上配的,维护了就以本按钮为准。
54
67
  </div>
55
68
  </el-form-item>
56
69
  <exportItemColumnsDialog
@@ -82,6 +95,8 @@ export default {
82
95
  data() {
83
96
  return {
84
97
  eventParams: [],
98
+ // 与数据表格属性面板同一套:脚本里可直接用 dataId / formCode
99
+ tableConfigParams: ["dataId", "formCode"],
85
100
  showExportItemColumnsDialog: false,
86
101
  };
87
102
  },
@@ -3318,8 +3318,10 @@ export const advancedFields = [
3318
3318
  showImageAtTable: false,
3319
3319
  selectExportItemFlag: 1,
3320
3320
  exportItemConditionEnabled: !1,
3321
- // 按钮级明细导出列:留空时沿用数据表格属性上维护的那套
3321
+ // 按钮级明细导出配置:三项各自独立,留空的那项沿用数据表格属性上维护的
3322
3322
  exportItemColumns: [],
3323
+ exportItemScriptCode: null,
3324
+ exportItemParam: "",
3323
3325
 
3324
3326
  onCreated: "",
3325
3327
  onMounted: "",
@@ -2442,9 +2442,7 @@ modules = {
2442
2442
  };
2443
2443
  }
2444
2444
 
2445
- let exportItemConfig = this.buildExportItemConfig(
2446
- this.widget.options.exportItemColumns
2447
- );
2445
+ let exportItemConfig = this.buildExportItemConfig();
2448
2446
  let rowConfig = {};
2449
2447
  if (this.widget.options.tableRowHeight) {
2450
2448
  rowConfig.height = this.widget.options.tableRowHeight;
@@ -3343,20 +3341,31 @@ modules = {
3343
3341
  });
3344
3342
  },
3345
3343
  /**
3346
- * 组装一份明细导出配置。列既可能来自表格属性,也可能来自导出按钮自身的配置(按钮优先),
3347
- * 但脚本编码与自定义参数始终取表格属性:按钮只决定导出哪些列,不另开取数通道。
3348
- * @param {Array} rawColumns 明细导出列。
3349
- * @returns {Object|null} 未维护列时返回 null。
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。
3350
3351
  */
3351
- buildExportItemConfig(rawColumns) {
3352
- if (!rawColumns || !rawColumns.length) return null;
3352
+ buildExportItemConfig(overrides) {
3353
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;
3354
3360
  return {
3355
- scriptCode: options.exportItemScriptCode || options.formScriptCode,
3356
- columns: this.buildExportItemColumns(rawColumns),
3361
+ scriptCode: scriptCode || options.formScriptCode,
3362
+ columns: this.buildExportItemColumns(columns),
3363
+ // 取数参数脚本与表格上其它脚本一样走 handleCustomEvent(它负责注入 dataId /
3364
+ // formCode 并统一弹前端脚本异常)。原先写的 handleCustomParam 全库没有定义,
3365
+ // 配了「明细导出查询参数」的表格一点导出就抛 TypeError。
3357
3366
  param: () => {
3358
- if (options.exportItemParam) {
3359
- return this.handleCustomParam(options.exportItemParam);
3367
+ if (paramScript) {
3368
+ return this.handleCustomEvent(paramScript);
3360
3369
  }
3361
3370
  },
3362
3371
  };
@@ -3364,16 +3373,27 @@ modules = {
3364
3373
  /**
3365
3374
  * 使用统一 Excel 导出器导出当前表格。
3366
3375
  * @param {Object} option 导出参数;prefix 为空时自动使用当前服务前缀。
3367
- * option.exportItemColumns 为按钮上维护的明细导出列,传了就以它为准(同一张表格可以挂
3368
- * 多个明细导出按钮、各导各自的列),组装成 exportItemConfig 随导出参数下发,
3369
- * 下游(字段选择弹框 / 分页取数 / 脱敏剔除)一律优先读它,读不到才回落表格属性那套。
3376
+ * option.exportItemColumns / exportItemScriptCode / exportItemParam 为按钮上维护的
3377
+ * 明细导出列、脚本编码与取数参数脚本,三项各自独立:传了哪项就以哪项为准(同一张表格
3378
+ * 可以挂多个明细导出按钮、各导各自的列、各走各自的脚本),组装成 exportItemConfig
3379
+ * 导出参数下发,下游(字段选择弹框 / 分页取数 / 脱敏剔除)一律优先读它,读不到才回落
3380
+ * 表格属性那套。
3370
3381
  */
3371
3382
  exportData(option) {
3372
3383
  let exportOption = { ...option };
3373
3384
  let itemColumns = exportOption.exportItemColumns;
3385
+ let itemScriptCode = exportOption.exportItemScriptCode;
3386
+ let itemParam = exportOption.exportItemParam;
3374
3387
  delete exportOption.exportItemColumns;
3375
- if (isItemExport(exportOption.type) && itemColumns?.length) {
3376
- exportOption.exportItemConfig = this.buildExportItemConfig(itemColumns);
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
+ });
3377
3397
  }
3378
3398
  this.markMaskedColumnsForExport(exportOption.exportItemConfig?.columns);
3379
3399
  let tableRef = this.getTableRef();
@@ -106,6 +106,132 @@ function pickScale(width, height, expected) {
106
106
  return Math.max(scale, 1);
107
107
  }
108
108
 
109
+ //Safari(WebKit) 的 getComputedStyle 会把颜色序列化成 color(srgb …) / color(display-p3 …),
110
+ //而 html2canvas 1.4.1 只认 rgb/rgba/hsl/hsla 四种,撞上就抛
111
+ //"Attempting to parse an unsupported color function" 直接整个导出失败。
112
+ //项目源码里并没有写过 color(),是浏览器自己吐出来的,改样式没用——只能在克隆体上就地换算。
113
+ const COLOR_FUNCTION = /color\([^()]*\)/g;
114
+
115
+ //html2canvas 会按颜色解析的属性(format: 'color'),外加三个值里可能夹着颜色的:
116
+ //渐变、阴影里的 color() 一样会把解析器打挂。
117
+ const COLOR_PROPS = [
118
+ "color",
119
+ "background-color",
120
+ "border-top-color",
121
+ "border-right-color",
122
+ "border-bottom-color",
123
+ "border-left-color",
124
+ "text-decoration-color",
125
+ "-webkit-text-stroke-color",
126
+ "background-image",
127
+ "box-shadow",
128
+ "text-shadow",
129
+ ];
130
+
131
+ //探针色:用来判断 canvas 到底认没认这个值(不认的话 fillStyle 会保持原样)
132
+ const PROBE_COLOR = "#010203";
133
+
134
+ function isWebKit() {
135
+ const ua = typeof navigator === "undefined" ? "" : navigator.userAgent || "";
136
+ //Chrome 系的 UA 里也有 AppleWebKit 字样,但它不会这么序列化,得排掉
137
+ return /AppleWebKit/.test(ua) && !/Chrome|Chromium|Edg\//.test(ua);
138
+ }
139
+
140
+ function probeContext() {
141
+ try {
142
+ const canvas = document.createElement("canvas");
143
+ return typeof canvas.getContext === "function" ? canvas.getContext("2d") : null;
144
+ } catch (err) {
145
+ return null;
146
+ }
147
+ }
148
+
149
+ function toByte(raw) {
150
+ const num = parseFloat(raw);
151
+ if (isNaN(num)) return 0;
152
+ const ratio = raw.indexOf("%") === -1 ? num : num / 100;
153
+ return Math.max(0, Math.min(255, Math.round(ratio * 255)));
154
+ }
155
+
156
+ function toAlpha(raw) {
157
+ const num = parseFloat(raw);
158
+ if (isNaN(num)) return 1;
159
+ const alpha = raw.indexOf("%") === -1 ? num : num / 100;
160
+ return Math.max(0, Math.min(1, alpha));
161
+ }
162
+
163
+ //浏览器不肯换算时的兜底:按 sRGB 直接取分量。
164
+ //display-p3 这样算是近似(色域没做映射),但截图里看不出差别,总好过整个导出失败。
165
+ function fallbackColor(value) {
166
+ const inside = value.slice(value.indexOf("(") + 1, value.lastIndexOf(")"));
167
+ const parts = inside.split("/");
168
+ //第一段是色彩空间名(srgb / display-p3 …),分量从第二段开始
169
+ const numbers = parts[0].trim().split(/\s+/).slice(1);
170
+ const channels = [0, 1, 2].map((i) => toByte(numbers[i] || "0"));
171
+ const alpha = parts.length > 1 ? toAlpha(parts[1]) : 1;
172
+ return "rgba(" + channels.join(", ") + ", " + alpha + ")";
173
+ }
174
+
175
+ function toLegacyColor(value, ctx) {
176
+ return value.replace(COLOR_FUNCTION, (match) => {
177
+ //优先让浏览器自己换:它认识 color(),换出来的 sRGB 值是准的
178
+ if (ctx) {
179
+ ctx.fillStyle = PROBE_COLOR;
180
+ ctx.fillStyle = match;
181
+ const converted = ctx.fillStyle;
182
+ const usable = typeof converted === "string" && converted.indexOf("color(") === -1;
183
+ if (usable && converted !== PROBE_COLOR) return converted;
184
+ }
185
+ return fallbackColor(match);
186
+ });
187
+ }
188
+
189
+ //在 html2canvas 的克隆体上把 color() 全部换成 rgba(),写成内联样式(优先级最高)。
190
+ //只在需要时才挂上去:这一趟要给每个节点再算一次样式,白跑一遍就是白卡一下。
191
+ export function normalizeColorFunctions(clonedDoc, clonedElement) {
192
+ const view = clonedDoc.defaultView;
193
+ if (!view || typeof view.getComputedStyle !== "function") return;
194
+ const ctx = probeContext();
195
+ const targets = [];
196
+ //除了目标元素,html2canvas 还会读 html/body 的背景色(parseBackgroundColor),一起带上
197
+ if (clonedDoc.documentElement) targets.push(clonedDoc.documentElement);
198
+ if (clonedDoc.body) targets.push(clonedDoc.body);
199
+ targets.push(clonedElement);
200
+ const children = clonedElement.querySelectorAll("*");
201
+ for (let i = 0; i < children.length; i++) targets.push(children[i]);
202
+
203
+ for (let i = 0; i < targets.length; i++) {
204
+ const el = targets[i];
205
+ if (!el || !el.style) continue;
206
+ const style = view.getComputedStyle(el);
207
+ for (let j = 0; j < COLOR_PROPS.length; j++) {
208
+ const prop = COLOR_PROPS[j];
209
+ const value = style.getPropertyValue(prop);
210
+ if (!value || value.indexOf("color(") === -1) continue;
211
+ el.style.setProperty(prop, toLegacyColor(value, ctx));
212
+ }
213
+ }
214
+ }
215
+
216
+ function isUnsupportedColorError(err) {
217
+ const message = err && err.message ? err.message : String(err || "");
218
+ return message.indexOf("unsupported color function") !== -1;
219
+ }
220
+
221
+ function withColorFix(options) {
222
+ return Object.assign({}, options, { onclone: normalizeColorFunctions });
223
+ }
224
+
225
+ async function renderCanvas(dom, options, fixColors) {
226
+ try {
227
+ return await html2canvas(dom, fixColors ? withColorFix(options) : options);
228
+ } catch (err) {
229
+ //别的引擎哪天也开始这么序列化,兜一次;已经带着换算跑过就别再重来了
230
+ if (fixColors || !isUnsupportedColorError(err)) throw err;
231
+ return html2canvas(dom, withColorFix(options));
232
+ }
233
+ }
234
+
109
235
  function buildIgnore(selectors) {
110
236
  let list = [LOADING_MASK];
111
237
  if (selectors) list = list.concat(selectors);
@@ -273,7 +399,7 @@ export async function exportPDF(option) {
273
399
  //只能靠文案说明进度,真要更快就得降 scale 或少截点内容。
274
400
  if (loading) loading.setText("正在渲染页面,请稍候…");
275
401
  await nextFrame();
276
- const canvas = await html2canvas(dom, {
402
+ const options = {
277
403
  backgroundColor: "#ffffff",
278
404
  useCORS: true,
279
405
  logging: false,
@@ -285,7 +411,9 @@ export async function exportPDF(option) {
285
411
  windowWidth: Math.max(document.documentElement.clientWidth, fullWidth),
286
412
  windowHeight: fullHeight + 200,
287
413
  ignoreElements: buildIgnore(option.ignoreSelectors),
288
- });
414
+ };
415
+ //WebKit 上直接带着颜色换算跑:先失败一次再重来,等于把那几秒的冻结再来一遍
416
+ const canvas = await renderCanvas(dom, options, isWebKit());
289
417
  await canvasToPdf(canvas, fileName, loading);
290
418
  return true;
291
419
  } catch (err) {