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 +1 -1
- package/src/components/table/plugins/cell-area/index.js +14 -0
- package/src/components/xform/form-designer/form-widget/field-widget/select-export-item-button-widget.vue +5 -1
- package/src/components/xform/form-designer/setting-panel/property-editor/container-data-table/data-table-editor.vue +10 -10
- package/src/components/xform/form-designer/setting-panel/property-editor/field-table-export-button/select-export-item-button-editor.vue +16 -1
- package/src/components/xform/form-designer/widget-panel/widgetsConfig.js +3 -1
- package/src/components/xform/form-render/container-item/data-table-mixin.js +38 -18
- package/src/utils/pdfUtil.js +130 -2
package/package.json
CHANGED
|
@@ -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 {
|
|
3349
|
-
* @
|
|
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(
|
|
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:
|
|
3356
|
-
columns: this.buildExportItemColumns(
|
|
3361
|
+
scriptCode: scriptCode || options.formScriptCode,
|
|
3362
|
+
columns: this.buildExportItemColumns(columns),
|
|
3363
|
+
// 取数参数脚本与表格上其它脚本一样走 handleCustomEvent(它负责注入 dataId /
|
|
3364
|
+
// formCode 并统一弹前端脚本异常)。原先写的 handleCustomParam 全库没有定义,
|
|
3365
|
+
// 配了「明细导出查询参数」的表格一点导出就抛 TypeError。
|
|
3357
3366
|
param: () => {
|
|
3358
|
-
if (
|
|
3359
|
-
return this.
|
|
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
|
-
*
|
|
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
|
-
|
|
3376
|
-
|
|
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();
|
package/src/utils/pdfUtil.js
CHANGED
|
@@ -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
|
|
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) {
|