uview-plus 3.8.86 → 3.8.106
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/changelog.md +150 -3
- package/components/u-action-sheet/u-action-sheet.vue +21 -2
- package/components/u-barcode/u-barcode.vue +0 -2
- package/components/u-button/u-button.vue +2 -5
- package/components/u-canvas/u-canvas.vue +368 -74
- package/components/u-cell/u-cell.vue +8 -0
- package/components/u-datetime-picker/u-datetime-picker.vue +53 -4
- package/components/u-dragsort/u-dragsort.vue +55 -31
- package/components/u-icon/u-icon.vue +12 -3
- package/components/u-icon/util.js +81 -10
- package/components/u-index-item/u-index-item.vue +1 -1
- package/components/u-index-list/u-index-list.vue +1 -1
- package/components/u-novel-reader/content-normalizer.js +80 -0
- package/components/u-novel-reader/layout-engine.js +225 -0
- package/components/u-novel-reader/measure-adapter.js +69 -0
- package/components/u-novel-reader/novelReader.js +35 -0
- package/components/u-novel-reader/persistence.js +144 -0
- package/components/u-novel-reader/props.js +100 -0
- package/components/u-novel-reader/reader-catalog.vue +234 -0
- package/components/u-novel-reader/reader-content.vue +226 -0
- package/components/u-novel-reader/reader-core.js +151 -0
- package/components/u-novel-reader/reader-settings.vue +325 -0
- package/components/u-novel-reader/reader-toolbar.vue +313 -0
- package/components/u-novel-reader/theme-vars.scss +49 -0
- package/components/u-novel-reader/u-novel-reader.vue +836 -0
- package/components/u-poster/u-poster.vue +236 -219
- package/components/u-qrcode/qrcode.js +56 -49
- package/components/u-qrcode/u-qrcode.vue +159 -88
- package/components/u-scroll-list/scrollWxs.wxs +1 -1
- package/components/u-slider/u-slider.vue +1 -1
- package/components/u-swipe-action-item/index.wxs +43 -25
- package/components/u-swipe-action-item/nvue.js +9 -0
- package/components/u-swipe-action-item/other.js +32 -21
- package/components/u-swipe-action-item/props.js +5 -0
- package/components/u-swipe-action-item/swipeActionItem.js +1 -0
- package/components/u-swipe-action-item/u-swipe-action-item.vue +28 -4
- package/components/u-switch/u-switch.vue +1 -1
- package/components/u-tabbar/u-tabbar.vue +2 -1
- package/components/u-tabbar-item/u-tabbar-item.vue +64 -21
- package/components/u-tabs/u-tabs.vue +11 -8
- package/components/u-tabs-pro/u-tabs-pro.vue +212 -0
- package/components/u-text/text.js +1 -1
- package/components/u-text/u-text.vue +18 -6
- package/components/u-upload/u-upload.vue +1 -1
- package/components/u-waterfall/u-waterfall.vue +118 -65
- package/libs/config/config.js +1 -1
- package/libs/config/props.js +1 -0
- package/libs/function/index.js +66 -0
- package/libs/mixin/mixin.js +4 -50
- package/libs/root/index.js +78 -5
- package/libs/root/page.js +7 -7
- package/libs/root/root.js +73 -3
- package/libs/util/app-nvue-webview-canvas.js +486 -0
- package/libs/util/gcanvas/bridge/bridge-weex.js +0 -2
- package/libs/util/gcanvas/context-2d/FillStyleLinearGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStylePattern.js +0 -0
- package/libs/util/gcanvas/context-2d/FillStyleRadialGradient.js +0 -0
- package/libs/util/gcanvas/context-2d/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/ActiveInfo.js +0 -0
- package/libs/util/gcanvas/context-webgl/Buffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/Framebuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLenum.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLmethod.js +0 -0
- package/libs/util/gcanvas/context-webgl/GLtype.js +0 -0
- package/libs/util/gcanvas/context-webgl/Program.js +0 -0
- package/libs/util/gcanvas/context-webgl/Renderbuffer.js +0 -0
- package/libs/util/gcanvas/context-webgl/RenderingContext.js +0 -0
- package/libs/util/gcanvas/context-webgl/Shader.js +0 -0
- package/libs/util/gcanvas/context-webgl/ShaderPrecisionFormat.js +0 -0
- package/libs/util/gcanvas/context-webgl/Texture.js +0 -0
- package/libs/util/gcanvas/context-webgl/UniformLocation.js +0 -0
- package/libs/util/gcanvas/context-webgl/classUtils.js +0 -0
- package/libs/util/gcanvas/env/canvas.js +0 -0
- package/libs/util/gcanvas/env/image.js +0 -0
- package/libs/util/gcanvas/env/tool.js +0 -0
- package/libs/util/gcanvas/index.js +3 -3
- package/package.json +1 -1
- package/types/comps/swipeActionItem.d.ts +13 -0
- package/types/comps/tabs.d.ts +2 -1
- package/types/comps/text.d.ts +1 -1
- package/types/func.d.ts +23 -0
- package/types/index.d.ts +1 -0
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="up-poster">
|
|
3
3
|
<!-- canvas用于绘制海报 -->
|
|
4
|
-
<up-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
4
|
+
<view v-if="showCanvas" class="up-poster__renderer">
|
|
5
|
+
<up-canvas
|
|
6
|
+
ref="posterCanvas"
|
|
7
|
+
class="up-poster__hidden-canvas"
|
|
8
|
+
:canvas-id="canvasId"
|
|
9
|
+
:width="canvasWidth"
|
|
10
|
+
:height="canvasHeight"
|
|
11
|
+
bg-color="transparent"
|
|
12
|
+
:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }">
|
|
13
|
+
</up-canvas>
|
|
14
|
+
</view>
|
|
14
15
|
<!-- 隐藏的二维码组件,用于生成二维码图片 -->
|
|
15
|
-
<up-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
<view class="up-poster__renderer">
|
|
17
|
+
<up-qrcode
|
|
18
|
+
ref="qrCode"
|
|
19
|
+
:val="qrCodeValue"
|
|
20
|
+
:size="qrCodeSize"
|
|
21
|
+
:margin="0"
|
|
22
|
+
:loadMake="false"
|
|
23
|
+
:onval="false"
|
|
24
|
+
background="#ffffff"
|
|
25
|
+
foreground="#000000"
|
|
26
|
+
:class="['up-poster__hidden-qrcode', qrCodeShow ? '' : 'up-poster__hidden-qrcode--hidden']"
|
|
27
|
+
/>
|
|
28
|
+
</view>
|
|
25
29
|
</view>
|
|
26
30
|
</template>
|
|
27
31
|
|
|
@@ -89,80 +93,74 @@ export default {
|
|
|
89
93
|
* @author jry ijry@qq.com
|
|
90
94
|
*/
|
|
91
95
|
async exportImage() {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// 绘制背景
|
|
112
|
-
if (posterSize.background) {
|
|
113
|
-
// 支持渐变背景色
|
|
96
|
+
let timeoutId = null;
|
|
97
|
+
try {
|
|
98
|
+
const timeoutPromise = new Promise((resolve, reject) => {
|
|
99
|
+
timeoutId = setTimeout(() => reject(new Error('导出图片超时')), 20000);
|
|
100
|
+
});
|
|
101
|
+
const exportPromise = (async () => {
|
|
102
|
+
// 获取海报尺寸信息
|
|
103
|
+
const posterSize = this.json.css || {};
|
|
104
|
+
const width = this.convertRpxToPx(posterSize.width || '750rpx');
|
|
105
|
+
const height = this.convertRpxToPx(posterSize.height || '1114rpx');
|
|
106
|
+
|
|
107
|
+
this.canvasWidth = width;
|
|
108
|
+
this.canvasHeight = height;
|
|
109
|
+
this.showCanvas = true;
|
|
110
|
+
await this.$nextTick();
|
|
111
|
+
|
|
112
|
+
const posterCanvas = await this.getPosterCanvas();
|
|
113
|
+
const ctx = posterCanvas;
|
|
114
|
+
if (posterSize.background) {
|
|
114
115
|
if (posterSize.background.includes('linear-gradient') || posterSize.background.includes('radial-gradient')) {
|
|
115
116
|
this.drawGradientBackground(ctx, posterSize, 0, 0, width, height);
|
|
116
117
|
} else {
|
|
117
118
|
ctx.setFillStyle(posterSize.background);
|
|
118
|
-
|
|
119
|
+
ctx.fillRect(0, 0, width, height);
|
|
119
120
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.showCanvas = false;
|
|
164
|
-
reject(error);
|
|
165
|
-
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (const item of this.json.views || []) {
|
|
124
|
+
await this.drawItem(ctx, item, width, height);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await this.flushPosterCanvas(posterCanvas);
|
|
128
|
+
const res = await posterCanvas.toTempFilePath({ width, height });
|
|
129
|
+
const path = res && (res.tempFilePath || res.apFilePath);
|
|
130
|
+
if (!path) {
|
|
131
|
+
throw new Error('导出图片未返回文件路径');
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
width,
|
|
135
|
+
height,
|
|
136
|
+
path,
|
|
137
|
+
blob: this.dataURLToBlob(path)
|
|
138
|
+
};
|
|
139
|
+
})();
|
|
140
|
+
return await Promise.race([exportPromise, timeoutPromise]);
|
|
141
|
+
} finally {
|
|
142
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
143
|
+
this.showCanvas = false;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
flushPosterCanvas(posterCanvas) {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
let settled = false;
|
|
150
|
+
const finish = (error) => {
|
|
151
|
+
if (settled) return;
|
|
152
|
+
settled = true;
|
|
153
|
+
if (error) reject(error);
|
|
154
|
+
else resolve();
|
|
155
|
+
};
|
|
156
|
+
try {
|
|
157
|
+
const result = posterCanvas.draw(false, () => finish());
|
|
158
|
+
if (result && typeof result.then === 'function') {
|
|
159
|
+
result.then(() => finish(), finish);
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
finish(error);
|
|
163
|
+
}
|
|
166
164
|
});
|
|
167
165
|
},
|
|
168
166
|
|
|
@@ -172,7 +170,10 @@ export default {
|
|
|
172
170
|
if (!posterCanvas) {
|
|
173
171
|
throw new Error('无法获取海报画布实例');
|
|
174
172
|
}
|
|
175
|
-
await posterCanvas.initCanvas(
|
|
173
|
+
const initialized = await posterCanvas.initCanvas();
|
|
174
|
+
if (!initialized || !posterCanvas.getRawContext()) {
|
|
175
|
+
throw new Error('无法初始化海报画布');
|
|
176
|
+
}
|
|
176
177
|
return posterCanvas;
|
|
177
178
|
},
|
|
178
179
|
|
|
@@ -215,7 +216,7 @@ export default {
|
|
|
215
216
|
|
|
216
217
|
case 'text':
|
|
217
218
|
// 设置文本样式
|
|
218
|
-
|
|
219
|
+
ctx.setFillStyle(css.color || '#000000');
|
|
219
220
|
if (css.fontSize) {
|
|
220
221
|
const fontSize = this.convertRpxToPx(css.fontSize);
|
|
221
222
|
ctx.setFontSize(fontSize);
|
|
@@ -226,7 +227,7 @@ export default {
|
|
|
226
227
|
|
|
227
228
|
// 处理文本换行
|
|
228
229
|
if (css.lineClamp) {
|
|
229
|
-
this.drawTextWithLineClamp(ctx, item.text, left, top, width, css);
|
|
230
|
+
await this.drawTextWithLineClamp(ctx, item.text, left, top, width, css);
|
|
230
231
|
} else {
|
|
231
232
|
// 修复:文本垂直居中对齐问题
|
|
232
233
|
const textBaseLine = css.fontSize ? this.convertRpxToPx(css.fontSize) / 2 : 10;
|
|
@@ -236,28 +237,27 @@ export default {
|
|
|
236
237
|
|
|
237
238
|
case 'image':
|
|
238
239
|
// 绘制图片
|
|
239
|
-
return new Promise((resolve) => {
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
240
241
|
uni.getImageInfo({
|
|
241
242
|
src: item.src,
|
|
242
243
|
success: async (res) => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
244
|
+
let clipped = false;
|
|
245
|
+
try {
|
|
246
|
+
if (css.radius) {
|
|
247
|
+
const radius = this.convertRpxToPx(css.radius);
|
|
248
|
+
this.clipRoundRect(ctx, left, top, width, height, radius);
|
|
249
|
+
clipped = true;
|
|
250
|
+
}
|
|
251
|
+
await ctx.drawImage(res.path, left, top, width, height);
|
|
252
|
+
resolve();
|
|
253
|
+
} catch (error) {
|
|
254
|
+
reject(new Error(`海报图片绘制失败: ${error && error.message ? error.message : error}`));
|
|
255
|
+
} finally {
|
|
256
|
+
if (clipped) ctx.restore();
|
|
248
257
|
}
|
|
249
|
-
// 不能用item.src,要用res.path。
|
|
250
|
-
await ctx.drawImage(res.path, left, top, width, height);
|
|
251
|
-
// 恢复剪切区域
|
|
252
|
-
ctx.restore();
|
|
253
|
-
resolve();
|
|
254
258
|
},
|
|
255
259
|
fail: (e) => {
|
|
256
|
-
|
|
257
|
-
ctx.setFillStyle('#f5f5f5');
|
|
258
|
-
ctx.fillRect(left, top, width, height);
|
|
259
|
-
console.log('图片加载失败: ' + item.src, e);
|
|
260
|
-
resolve();
|
|
260
|
+
reject(new Error(`海报图片加载失败: ${e && e.errMsg ? e.errMsg : e}`));
|
|
261
261
|
}
|
|
262
262
|
});
|
|
263
263
|
});
|
|
@@ -267,26 +267,10 @@ export default {
|
|
|
267
267
|
if (item.text) {
|
|
268
268
|
// 使用u-qrcode生成二维码图片
|
|
269
269
|
const qrCodeImageUrl = await this.generateQRCode(item.text, width, height);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
await ctx.drawImage(res.path, left, top, width, height);
|
|
275
|
-
resolve();
|
|
276
|
-
},
|
|
277
|
-
fail: () => {
|
|
278
|
-
// 二维码加载失败时绘制占位符
|
|
279
|
-
ctx.setFillStyle('#f5f5f5');
|
|
280
|
-
ctx.fillRect(left, top, width, height);
|
|
281
|
-
ctx.setFillStyle('#999');
|
|
282
|
-
ctx.setFontSize(12);
|
|
283
|
-
ctx.setTextAlign('center');
|
|
284
|
-
ctx.fillText('QR', left + width/2, top + height/2);
|
|
285
|
-
ctx.setTextAlign('left');
|
|
286
|
-
resolve();
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
});
|
|
270
|
+
const drawn = await ctx.drawImage(qrCodeImageUrl, left, top, width, height);
|
|
271
|
+
if (drawn === false) {
|
|
272
|
+
throw new Error('二维码图片无法绘制到海报画布');
|
|
273
|
+
}
|
|
290
274
|
} else {
|
|
291
275
|
// 绘制二维码占位符
|
|
292
276
|
ctx.setFillStyle('#f5f5f5');
|
|
@@ -369,51 +353,71 @@ export default {
|
|
|
369
353
|
* @param {Object} css 样式配置
|
|
370
354
|
* @author jry ijry@qq.com
|
|
371
355
|
*/
|
|
372
|
-
drawTextWithLineClamp(ctx, text, x, y, maxWidth, css) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
356
|
+
async drawTextWithLineClamp(ctx, text, x, y, maxWidth, css) {
|
|
357
|
+
const lineClamp = Math.max(1, parseInt(css.lineClamp, 10) || 1);
|
|
358
|
+
const lineHeight = css.lineHeight ? this.convertRpxToPx(css.lineHeight) : 20;
|
|
359
|
+
const characters = Array.from(String(text || ''));
|
|
360
|
+
const lines = [];
|
|
361
|
+
const ellipsis = '...';
|
|
362
|
+
const measurementCache = new Map();
|
|
363
|
+
const measureTextWidth = async (value) => {
|
|
364
|
+
if (measurementCache.has(value)) {
|
|
365
|
+
return measurementCache.get(value);
|
|
366
|
+
}
|
|
367
|
+
let metrics;
|
|
368
|
+
if (ctx && typeof ctx.measureTextAsync === 'function') {
|
|
369
|
+
metrics = await ctx.measureTextAsync(value);
|
|
370
|
+
} else {
|
|
371
|
+
metrics = ctx.measureText(value);
|
|
372
|
+
}
|
|
373
|
+
const measuredWidth = Number(metrics && metrics.width) || 0;
|
|
374
|
+
measurementCache.set(value, measuredWidth);
|
|
375
|
+
return measuredWidth;
|
|
376
|
+
};
|
|
377
|
+
const findTextFitLength = async (values, suffix = '') => {
|
|
378
|
+
let low = 0;
|
|
379
|
+
let high = values.length;
|
|
380
|
+
while (low < high) {
|
|
381
|
+
const middle = Math.ceil((low + high) / 2);
|
|
382
|
+
const candidate = values.slice(0, middle).join('') + suffix;
|
|
383
|
+
if (await measureTextWidth(candidate) <= maxWidth) {
|
|
384
|
+
low = middle;
|
|
385
|
+
} else {
|
|
386
|
+
high = middle - 1;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return low;
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
let offset = 0;
|
|
393
|
+
while (offset < characters.length && lines.length < lineClamp) {
|
|
394
|
+
const remaining = characters.slice(offset);
|
|
395
|
+
const remainingText = remaining.join('');
|
|
396
|
+
if (await measureTextWidth(remainingText) <= maxWidth) {
|
|
397
|
+
lines.push(remainingText);
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const isLastLine = lines.length === lineClamp - 1;
|
|
402
|
+
if (isLastLine) {
|
|
403
|
+
let suffix = ellipsis;
|
|
404
|
+
while (suffix && await measureTextWidth(suffix) > maxWidth) {
|
|
405
|
+
suffix = suffix.slice(0, -1);
|
|
406
|
+
}
|
|
407
|
+
const fitLength = await findTextFitLength(remaining, suffix);
|
|
408
|
+
lines.push(remaining.slice(0, fitLength).join('') + suffix);
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const fitLength = Math.max(1, await findTextFitLength(remaining));
|
|
413
|
+
lines.push(remaining.slice(0, fitLength).join(''));
|
|
414
|
+
offset += fitLength;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const textBaseLine = css.fontSize ? this.convertRpxToPx(css.fontSize) / 2 : 10;
|
|
418
|
+
for (let i = 0; i < lines.length; i++) {
|
|
419
|
+
ctx.fillText(lines[i], x, y + (i * lineHeight) + textBaseLine);
|
|
420
|
+
}
|
|
417
421
|
},
|
|
418
422
|
|
|
419
423
|
/**
|
|
@@ -425,54 +429,41 @@ export default {
|
|
|
425
429
|
* @returns {Promise<String>} 二维码图片URL
|
|
426
430
|
* @author jry ijry@qq.com
|
|
427
431
|
*/
|
|
428
|
-
generateQRCode(text, width, height) {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
432
|
+
async generateQRCode(text, width, height) {
|
|
433
|
+
const qrCodeKey = `${text}_${width}_${height}`;
|
|
434
|
+
if (this.qrCodeMap.has(qrCodeKey)) {
|
|
435
|
+
return this.qrCodeMap.get(qrCodeKey);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
this.qrCodeValue = text;
|
|
439
|
+
this.qrCodeSize = Math.max(width, height);
|
|
440
|
+
this.qrCodeShow = true;
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
await this.$nextTick();
|
|
444
|
+
const qrCode = this.$refs.qrCode;
|
|
445
|
+
if (!qrCode) {
|
|
446
|
+
throw new Error('无法获取二维码组件实例');
|
|
437
447
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
try {
|
|
441
|
-
// 设置二维码参数
|
|
442
|
-
this.qrCodeValue = text;
|
|
443
|
-
this.qrCodeSize = Math.max(width, height);
|
|
444
|
-
this.qrCodeShow = true;
|
|
445
|
-
|
|
446
|
-
// 等待DOM更新
|
|
447
|
-
this.$nextTick(() => {
|
|
448
|
-
// 获取二维码组件实例并导出图片
|
|
449
|
-
if (this.$refs.qrCode) {
|
|
450
|
-
// 延迟一点时间确保二维码渲染完成
|
|
451
|
-
setTimeout(() => {
|
|
452
|
-
// 调用 u-qrcode 的 toTempFilePath 方法导出图片
|
|
453
|
-
this.$refs.qrCode.toTempFilePath({
|
|
454
|
-
success: (res) => {
|
|
455
|
-
// 缓存二维码图片路径
|
|
456
|
-
this.qrCodeMap.set(qrCodeKey, res.tempFilePath);
|
|
457
|
-
this.qrCodeShow = false;
|
|
458
|
-
resolve(res.tempFilePath);
|
|
459
|
-
},
|
|
460
|
-
fail: (err) => {
|
|
461
|
-
console.error('二维码生成失败:', err);
|
|
462
|
-
this.qrCodeShow = false;
|
|
463
|
-
}
|
|
464
|
-
});
|
|
465
|
-
}, 300);
|
|
466
|
-
} else {
|
|
467
|
-
// 如果没有 u-qrcode 组件,返回占位符
|
|
468
|
-
this.qrCodeShow = false;
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
|
-
} catch (error) {
|
|
472
|
-
console.error('生成二维码出错:', error);
|
|
473
|
-
this.qrCodeShow = false;
|
|
448
|
+
if (typeof qrCode.refreshCanvas === 'function') {
|
|
449
|
+
await qrCode.refreshCanvas();
|
|
474
450
|
}
|
|
475
|
-
|
|
451
|
+
const generatedPath = await qrCode._makeCode();
|
|
452
|
+
let tempFilePath = typeof generatedPath === 'string' ? generatedPath : '';
|
|
453
|
+
if (!tempFilePath) {
|
|
454
|
+
const res = await qrCode.toTempFilePath();
|
|
455
|
+
tempFilePath = res && (res.tempFilePath || res.apFilePath);
|
|
456
|
+
}
|
|
457
|
+
if (!tempFilePath) {
|
|
458
|
+
throw new Error('二维码组件未返回图片路径');
|
|
459
|
+
}
|
|
460
|
+
this.qrCodeMap.set(qrCodeKey, tempFilePath);
|
|
461
|
+
return tempFilePath;
|
|
462
|
+
} catch (error) {
|
|
463
|
+
throw new Error(`二维码生成失败: ${error && error.message ? error.message : error}`);
|
|
464
|
+
} finally {
|
|
465
|
+
this.qrCodeShow = false;
|
|
466
|
+
}
|
|
476
467
|
},
|
|
477
468
|
|
|
478
469
|
/**
|
|
@@ -625,6 +616,31 @@ export default {
|
|
|
625
616
|
overflow: hidden;
|
|
626
617
|
}
|
|
627
618
|
|
|
619
|
+
/* #ifdef APP-NVUE */
|
|
620
|
+
&__renderer {
|
|
621
|
+
position: fixed;
|
|
622
|
+
top: -1px;
|
|
623
|
+
left: -1px;
|
|
624
|
+
width: 1px;
|
|
625
|
+
height: 1px;
|
|
626
|
+
overflow: hidden;
|
|
627
|
+
z-index: -1;
|
|
628
|
+
opacity: 0.01;
|
|
629
|
+
pointer-events: none;
|
|
630
|
+
}
|
|
631
|
+
/* #endif */
|
|
632
|
+
|
|
633
|
+
/* #ifndef APP-NVUE */
|
|
634
|
+
&__renderer {
|
|
635
|
+
position: fixed;
|
|
636
|
+
top: -10000px;
|
|
637
|
+
left: -10000px;
|
|
638
|
+
width: 1px;
|
|
639
|
+
height: 1px;
|
|
640
|
+
overflow: hidden;
|
|
641
|
+
z-index: -1;
|
|
642
|
+
}
|
|
643
|
+
|
|
628
644
|
&__hidden-canvas {
|
|
629
645
|
position: fixed;
|
|
630
646
|
top: -10000px;
|
|
@@ -642,5 +658,6 @@ export default {
|
|
|
642
658
|
display: none;
|
|
643
659
|
}
|
|
644
660
|
}
|
|
661
|
+
/* #endif */
|
|
645
662
|
}
|
|
646
663
|
</style>
|
|
@@ -1164,60 +1164,67 @@ let QRCode = {};
|
|
|
1164
1164
|
}
|
|
1165
1165
|
}
|
|
1166
1166
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
});
|
|
1203
|
-
}
|
|
1204
|
-
}, options.text.length + 100);
|
|
1205
|
-
};
|
|
1167
|
+
// canvas2 绘制是自动的不需要手动绘制
|
|
1168
|
+
if(options.isNvue || isApp){
|
|
1169
|
+
let exportStarted = false;
|
|
1170
|
+
const reportFailure = function(error) {
|
|
1171
|
+
uni.hideLoading();
|
|
1172
|
+
if (options.cbResult) {
|
|
1173
|
+
options.cbResult(error instanceof Error ? error : new Error(String(error || '绘制失败')))
|
|
1174
|
+
}
|
|
1175
|
+
};
|
|
1176
|
+
const exportCode = async function() {
|
|
1177
|
+
if (exportStarted) return;
|
|
1178
|
+
exportStarted = true;
|
|
1179
|
+
try {
|
|
1180
|
+
if (!options.canvasHost || typeof options.canvasHost.toTempFilePath !== 'function') {
|
|
1181
|
+
throw new Error('无法获取二维码画布导出方法');
|
|
1182
|
+
}
|
|
1183
|
+
const res = await options.canvasHost.toTempFilePath({
|
|
1184
|
+
width: options.width || options.size,
|
|
1185
|
+
height: options.height || options.size,
|
|
1186
|
+
quality: Number(1)
|
|
1187
|
+
});
|
|
1188
|
+
const tempFilePath = res && (res.tempFilePath || res.apFilePath);
|
|
1189
|
+
if (empty(tempFilePath)) {
|
|
1190
|
+
throw new Error('二维码导出未返回文件路径');
|
|
1191
|
+
}
|
|
1192
|
+
if (options.cbResult) {
|
|
1193
|
+
options.cbResult(tempFilePath)
|
|
1194
|
+
}
|
|
1195
|
+
uni.hideLoading();
|
|
1196
|
+
} catch (error) {
|
|
1197
|
+
reportFailure(error);
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
try {
|
|
1201
|
+
let drawResult;
|
|
1206
1202
|
if (options.canvasHost && typeof options.canvasHost.draw === 'function') {
|
|
1207
|
-
options.canvasHost.draw(true, exportCode);
|
|
1203
|
+
drawResult = options.canvasHost.draw(true, exportCode);
|
|
1208
1204
|
} else if (ctx && typeof ctx.draw === 'function') {
|
|
1209
|
-
ctx.draw(true, exportCode);
|
|
1205
|
+
drawResult = ctx.draw(true, exportCode);
|
|
1210
1206
|
} else {
|
|
1211
1207
|
exportCode();
|
|
1212
1208
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1209
|
+
if (drawResult && typeof drawResult.then === 'function') {
|
|
1210
|
+
drawResult.then(exportCode, reportFailure);
|
|
1211
|
+
}
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
reportFailure(error);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
else{
|
|
1217
|
+
setTimeout(function() {
|
|
1218
|
+
options.cbResult("")
|
|
1219
|
+
}, options.usingComponents ? 100 : 200);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
createCanvas(this.options).catch(function(error) {
|
|
1223
|
+
uni.hideLoading();
|
|
1224
|
+
if (opt.cbResult) {
|
|
1225
|
+
opt.cbResult(error instanceof Error ? error : new Error(String(error || '二维码绘制失败')))
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1221
1228
|
// 空判定
|
|
1222
1229
|
let empty = function (v) {
|
|
1223
1230
|
let tp = typeof v,
|