uview-plus 3.8.55 → 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 +2147 -1828
- package/components/u-action-sheet/props.js +3 -1
- package/components/u-action-sheet/u-action-sheet.vue +22 -2
- package/components/u-album/props.js +3 -1
- package/components/u-alert/props.js +4 -2
- package/components/u-avatar/props.js +4 -1
- package/components/u-avatar-group/props.js +4 -1
- package/components/u-back-top/props.js +4 -1
- package/components/u-badge/props.js +4 -1
- package/components/u-barcode/u-barcode.vue +44 -15
- package/components/u-box/props.js +5 -2
- package/components/u-button/props.js +4 -1
- package/components/u-button/u-button.vue +2 -5
- package/components/u-button/vue.scss +3 -3
- package/components/u-calendar/calendar.js +8 -0
- package/components/u-calendar/props.js +43 -1
- package/components/u-calendar/u-calendar.vue +16 -0
- package/components/u-calendar-strip/props.js +3 -1
- package/components/u-canvas/u-canvas.vue +1028 -423
- package/components/u-car-keyboard/props.js +3 -1
- package/components/u-card/props.js +3 -1
- package/components/u-cell/props.js +4 -1
- package/components/u-cell/u-cell.vue +9 -1
- package/components/u-cell-group/props.js +4 -1
- package/components/u-checkbox/props.js +4 -1
- package/components/u-checkbox/u-checkbox.vue +7 -3
- package/components/u-checkbox-group/props.js +3 -1
- package/components/u-circle-progress/props.js +4 -1
- package/components/u-code/props.js +4 -1
- package/components/u-code-input/props.js +4 -1
- package/components/u-col/props.js +4 -1
- package/components/u-collapse/props.js +4 -1
- package/components/u-collapse-item/props.js +4 -1
- package/components/u-color-picker/u-color-picker.vue +1 -0
- package/components/u-column-notice/props.js +4 -1
- package/components/u-count-down/props.js +4 -1
- package/components/u-count-to/props.js +4 -1
- package/components/u-cropper/u-cropper.vue +95 -51
- package/components/u-datetime-picker/props.js +4 -1
- package/components/u-datetime-picker/u-datetime-picker.vue +53 -4
- package/components/u-divider/props.js +4 -1
- package/components/u-dragsort/u-dragsort.vue +57 -32
- package/components/u-dropdown/props.js +3 -1
- package/components/u-dropdown-item/props.js +3 -1
- package/components/u-empty/props.js +4 -1
- package/components/u-form/props.js +4 -1
- package/components/u-form-item/props.js +4 -1
- package/components/u-gap/props.js +4 -1
- package/components/u-grid/props.js +4 -1
- package/components/u-grid-item/props.js +4 -1
- package/components/u-guide/props.js +3 -1
- package/components/u-icon/props.js +4 -1
- package/components/u-icon/u-icon.vue +11 -2
- package/components/u-icon/upicon.ttf +0 -0
- package/components/u-icon/util.js +103 -10
- package/components/u-image/props.js +4 -1
- package/components/u-image/u-image.vue +280 -267
- package/components/u-index-anchor/props.js +4 -1
- package/components/u-index-item/props.js +2 -1
- package/components/u-index-item/u-index-item.vue +1 -1
- package/components/u-index-list/props.js +4 -1
- package/components/u-index-list/u-index-list.vue +1 -1
- package/components/u-input/props.js +3 -1
- package/components/u-keyboard/props.js +4 -1
- package/components/u-line/props.js +4 -1
- package/components/u-line-progress/props.js +6 -3
- package/components/u-link/props.js +4 -1
- package/components/u-list/props.js +4 -1
- package/components/u-list-item/props.js +4 -1
- package/components/u-loading-icon/props.js +4 -1
- package/components/u-loading-page/props.js +3 -1
- package/components/u-loadmore/props.js +4 -1
- package/components/u-markdown/u-markdown.vue +45 -3
- package/components/u-modal/props.js +4 -1
- package/components/u-navbar/props.js +4 -2
- package/components/u-navbar/u-navbar.vue +15 -9
- package/components/u-no-network/props.js +4 -1
- package/components/u-notice-bar/props.js +4 -1
- package/components/u-notify/props.js +4 -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-number-box/props.js +4 -1
- package/components/u-number-box/u-number-box.vue +2 -2
- package/components/u-number-keyboard/props.js +4 -1
- package/components/u-overlay/props.js +4 -1
- package/components/u-pagination/u-pagination.vue +60 -39
- package/components/u-parse/props.js +4 -1
- package/components/u-picker/props.js +4 -1
- package/components/u-picker-column/props.js +3 -1
- package/components/u-popover/props.js +2 -1
- package/components/u-popup/props.js +4 -1
- package/components/u-poster/u-poster.vue +246 -214
- package/components/u-qrcode/qrcode.js +105 -97
- package/components/u-qrcode/u-qrcode.vue +242 -289
- package/components/u-radio/props.js +4 -1
- package/components/u-radio/u-radio.vue +7 -3
- package/components/u-radio-group/props.js +4 -1
- package/components/u-rate/props.js +4 -1
- package/components/u-read-more/props.js +4 -1
- package/components/u-row/props.js +4 -1
- package/components/u-row-notice/props.js +4 -1
- package/components/u-safe-bottom/props.js +2 -1
- package/components/u-scroll-list/props.js +4 -1
- package/components/u-scroll-list/scrollWxs.wxs +1 -1
- package/components/u-search/props.js +3 -1
- package/components/u-skeleton/props.js +4 -1
- package/components/u-slider/props.js +3 -1
- package/components/u-slider/u-slider.vue +71 -29
- package/components/u-status-bar/props.js +4 -1
- package/components/u-steps/props.js +4 -1
- package/components/u-steps-item/props.js +4 -1
- package/components/u-sticky/props.js +4 -1
- package/components/u-sticky/u-sticky.vue +11 -0
- package/components/u-subsection/props.js +3 -1
- package/components/u-swipe-action/props.js +4 -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 +9 -1
- package/components/u-swipe-action-item/swipeActionItem.js +1 -0
- package/components/u-swipe-action-item/u-swipe-action-item.vue +42 -4
- package/components/u-swiper/props.js +9 -1
- package/components/u-swiper/swiper.js +1 -0
- package/components/u-swiper/u-swiper.vue +2 -0
- package/components/u-swiper-indicator/props.js +4 -1
- package/components/u-switch/props.js +4 -1
- package/components/u-switch/u-switch.vue +1 -1
- package/components/u-tabbar/props.js +3 -1
- package/components/u-tabbar/u-tabbar.vue +2 -1
- package/components/u-tabbar-item/props.js +4 -1
- package/components/u-tabbar-item/u-tabbar-item.vue +64 -21
- package/components/u-table/props.js +3 -1
- package/components/u-table2/tableRow.vue +155 -69
- package/components/u-table2/u-table2.vue +327 -137
- package/components/u-tabs/props.js +4 -1
- package/components/u-tabs/u-tabs.vue +11 -8
- package/components/u-tabs-item/props.js +3 -1
- package/components/u-tabs-pro/u-tabs-pro.vue +212 -0
- package/components/u-tag/props.js +4 -1
- package/components/u-td/props.js +3 -1
- package/components/u-text/props.js +4 -1
- package/components/u-text/text.js +1 -1
- package/components/u-text/u-text.vue +18 -6
- package/components/u-textarea/props.js +3 -1
- package/components/u-textarea/u-textarea.vue +16 -5
- package/components/u-th/props.js +3 -1
- package/components/u-toolbar/props.js +4 -1
- package/components/u-tooltip/props.js +10 -2
- package/components/u-tooltip/tooltip.js +2 -1
- package/components/u-tooltip/u-tooltip.vue +31 -3
- package/components/u-tr/props.js +3 -1
- package/components/u-transition/nvue-ani-map.js +75 -68
- package/components/u-transition/props.js +4 -1
- package/components/u-transition/transitionMixin.js +164 -163
- package/components/u-upload/props.js +4 -1
- package/components/u-upload/u-upload.vue +48 -41
- package/components/u-waterfall/u-waterfall.vue +162 -110
- package/index.js +65 -8
- package/libs/config/config.js +1 -1
- package/libs/config/props.js +180 -204
- package/libs/css/components.scss +3 -0
- package/libs/function/index.js +66 -0
- package/libs/function/uni-api-shims.js +29 -0
- package/libs/i18n/index.js +71 -36
- package/libs/i18n/locale-packs.js +11 -0
- package/libs/i18n/locales/all.js +23 -0
- package/libs/i18n/locales/{de.json → de.js} +1 -1
- package/libs/i18n/locales/{en.json → en.js} +2 -2
- package/libs/i18n/locales/{es.json → es.js} +1 -1
- package/libs/i18n/locales/{fr.json → fr.js} +2 -2
- package/libs/i18n/locales/{ja.json → ja.js} +1 -1
- package/libs/i18n/locales/{ko.json → ko.js} +1 -1
- package/libs/i18n/locales/{ru.json → ru.js} +1 -1
- package/libs/i18n/locales/{th.json → th.js} +1 -1
- package/libs/i18n/locales/{zh-Hans.json → zh-Hans.js} +1 -1
- package/libs/i18n/locales/{zh-Hant.json → zh-Hant.js} +1 -1
- package/libs/mixin/mixin.js +18 -54
- 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 +10 -7
- 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 +4 -5
- package/tsconfig.json +18 -0
- package/types/comps/calendar.d.ts +38 -0
- package/types/comps/checkbox.d.ts +12 -0
- package/types/comps/lineProgress.d.ts +5 -0
- package/types/comps/navbar.d.ts +3 -3
- package/types/comps/swipeActionItem.d.ts +13 -0
- package/types/comps/swiper.d.ts +5 -0
- package/types/comps/tabs.d.ts +2 -1
- package/types/comps/text.d.ts +1 -1
- package/types/comps/tooltip.d.ts +36 -1
- package/types/func.d.ts +23 -0
- package/types/index.d.ts +1 -0
- /package/components/u-markdown/{marked.esm.js → marked.esm.mjs} +0 -0
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="up-poster">
|
|
3
3
|
<!-- canvas用于绘制海报 -->
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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>
|
|
11
15
|
<!-- 隐藏的二维码组件,用于生成二维码图片 -->
|
|
12
|
-
<up-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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>
|
|
22
29
|
</view>
|
|
23
30
|
</template>
|
|
24
31
|
|
|
@@ -86,80 +93,89 @@ export default {
|
|
|
86
93
|
* @author jry ijry@qq.com
|
|
87
94
|
*/
|
|
88
95
|
async exportImage() {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// 绘制背景
|
|
109
|
-
if (posterSize.background) {
|
|
110
|
-
// 支持渐变背景色
|
|
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) {
|
|
111
115
|
if (posterSize.background.includes('linear-gradient') || posterSize.background.includes('radial-gradient')) {
|
|
112
116
|
this.drawGradientBackground(ctx, posterSize, 0, 0, width, height);
|
|
113
117
|
} else {
|
|
114
118
|
ctx.setFillStyle(posterSize.background);
|
|
115
|
-
|
|
119
|
+
ctx.fillRect(0, 0, width, height);
|
|
116
120
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
}
|
|
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
|
+
}
|
|
161
164
|
});
|
|
162
165
|
},
|
|
166
|
+
|
|
167
|
+
async getPosterCanvas() {
|
|
168
|
+
await this.$nextTick();
|
|
169
|
+
const posterCanvas = this.$refs.posterCanvas;
|
|
170
|
+
if (!posterCanvas) {
|
|
171
|
+
throw new Error('无法获取海报画布实例');
|
|
172
|
+
}
|
|
173
|
+
const initialized = await posterCanvas.initCanvas();
|
|
174
|
+
if (!initialized || !posterCanvas.getRawContext()) {
|
|
175
|
+
throw new Error('无法初始化海报画布');
|
|
176
|
+
}
|
|
177
|
+
return posterCanvas;
|
|
178
|
+
},
|
|
163
179
|
|
|
164
180
|
/**
|
|
165
181
|
* 绘制单个元素
|
|
@@ -200,7 +216,7 @@ export default {
|
|
|
200
216
|
|
|
201
217
|
case 'text':
|
|
202
218
|
// 设置文本样式
|
|
203
|
-
|
|
219
|
+
ctx.setFillStyle(css.color || '#000000');
|
|
204
220
|
if (css.fontSize) {
|
|
205
221
|
const fontSize = this.convertRpxToPx(css.fontSize);
|
|
206
222
|
ctx.setFontSize(fontSize);
|
|
@@ -211,7 +227,7 @@ export default {
|
|
|
211
227
|
|
|
212
228
|
// 处理文本换行
|
|
213
229
|
if (css.lineClamp) {
|
|
214
|
-
this.drawTextWithLineClamp(ctx, item.text, left, top, width, css);
|
|
230
|
+
await this.drawTextWithLineClamp(ctx, item.text, left, top, width, css);
|
|
215
231
|
} else {
|
|
216
232
|
// 修复:文本垂直居中对齐问题
|
|
217
233
|
const textBaseLine = css.fontSize ? this.convertRpxToPx(css.fontSize) / 2 : 10;
|
|
@@ -221,28 +237,27 @@ export default {
|
|
|
221
237
|
|
|
222
238
|
case 'image':
|
|
223
239
|
// 绘制图片
|
|
224
|
-
return new Promise((resolve) => {
|
|
240
|
+
return new Promise((resolve, reject) => {
|
|
225
241
|
uni.getImageInfo({
|
|
226
242
|
src: item.src,
|
|
227
|
-
success: (res) => {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
success: async (res) => {
|
|
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();
|
|
233
257
|
}
|
|
234
|
-
// 不能用item.src,要用res.path。
|
|
235
|
-
ctx.drawImage(res.path, left, top, width, height);
|
|
236
|
-
// 恢复剪切区域
|
|
237
|
-
ctx.restore();
|
|
238
|
-
resolve();
|
|
239
258
|
},
|
|
240
259
|
fail: (e) => {
|
|
241
|
-
|
|
242
|
-
ctx.setFillStyle('#f5f5f5');
|
|
243
|
-
ctx.fillRect(left, top, width, height);
|
|
244
|
-
console.log('图片加载失败: ' + item.src, e);
|
|
245
|
-
resolve();
|
|
260
|
+
reject(new Error(`海报图片加载失败: ${e && e.errMsg ? e.errMsg : e}`));
|
|
246
261
|
}
|
|
247
262
|
});
|
|
248
263
|
});
|
|
@@ -252,26 +267,10 @@ export default {
|
|
|
252
267
|
if (item.text) {
|
|
253
268
|
// 使用u-qrcode生成二维码图片
|
|
254
269
|
const qrCodeImageUrl = await this.generateQRCode(item.text, width, height);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
ctx.drawImage(res.path, left, top, width, height);
|
|
260
|
-
resolve();
|
|
261
|
-
},
|
|
262
|
-
fail: () => {
|
|
263
|
-
// 二维码加载失败时绘制占位符
|
|
264
|
-
ctx.setFillStyle('#f5f5f5');
|
|
265
|
-
ctx.fillRect(left, top, width, height);
|
|
266
|
-
ctx.setFillStyle('#999');
|
|
267
|
-
ctx.setFontSize(12);
|
|
268
|
-
ctx.setTextAlign('center');
|
|
269
|
-
ctx.fillText('QR', left + width/2, top + height/2);
|
|
270
|
-
ctx.setTextAlign('left');
|
|
271
|
-
resolve();
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
});
|
|
270
|
+
const drawn = await ctx.drawImage(qrCodeImageUrl, left, top, width, height);
|
|
271
|
+
if (drawn === false) {
|
|
272
|
+
throw new Error('二维码图片无法绘制到海报画布');
|
|
273
|
+
}
|
|
275
274
|
} else {
|
|
276
275
|
// 绘制二维码占位符
|
|
277
276
|
ctx.setFillStyle('#f5f5f5');
|
|
@@ -354,51 +353,71 @@ export default {
|
|
|
354
353
|
* @param {Object} css 样式配置
|
|
355
354
|
* @author jry ijry@qq.com
|
|
356
355
|
*/
|
|
357
|
-
drawTextWithLineClamp(ctx, text, x, y, maxWidth, css) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
402
421
|
},
|
|
403
422
|
|
|
404
423
|
/**
|
|
@@ -410,54 +429,41 @@ export default {
|
|
|
410
429
|
* @returns {Promise<String>} 二维码图片URL
|
|
411
430
|
* @author jry ijry@qq.com
|
|
412
431
|
*/
|
|
413
|
-
generateQRCode(text, width, height) {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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('无法获取二维码组件实例');
|
|
422
447
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
// 设置二维码参数
|
|
427
|
-
this.qrCodeValue = text;
|
|
428
|
-
this.qrCodeSize = Math.max(width, height);
|
|
429
|
-
this.qrCodeShow = true;
|
|
430
|
-
|
|
431
|
-
// 等待DOM更新
|
|
432
|
-
this.$nextTick(() => {
|
|
433
|
-
// 获取二维码组件实例并导出图片
|
|
434
|
-
if (this.$refs.qrCode) {
|
|
435
|
-
// 延迟一点时间确保二维码渲染完成
|
|
436
|
-
setTimeout(() => {
|
|
437
|
-
// 调用 u-qrcode 的 toTempFilePath 方法导出图片
|
|
438
|
-
this.$refs.qrCode.toTempFilePath({
|
|
439
|
-
success: (res) => {
|
|
440
|
-
// 缓存二维码图片路径
|
|
441
|
-
this.qrCodeMap.set(qrCodeKey, res.tempFilePath);
|
|
442
|
-
this.qrCodeShow = false;
|
|
443
|
-
resolve(res.tempFilePath);
|
|
444
|
-
},
|
|
445
|
-
fail: (err) => {
|
|
446
|
-
console.error('二维码生成失败:', err);
|
|
447
|
-
this.qrCodeShow = false;
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
}, 300);
|
|
451
|
-
} else {
|
|
452
|
-
// 如果没有 u-qrcode 组件,返回占位符
|
|
453
|
-
this.qrCodeShow = false;
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
} catch (error) {
|
|
457
|
-
console.error('生成二维码出错:', error);
|
|
458
|
-
this.qrCodeShow = false;
|
|
448
|
+
if (typeof qrCode.refreshCanvas === 'function') {
|
|
449
|
+
await qrCode.refreshCanvas();
|
|
459
450
|
}
|
|
460
|
-
|
|
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
|
+
}
|
|
461
467
|
},
|
|
462
468
|
|
|
463
469
|
/**
|
|
@@ -610,6 +616,31 @@ export default {
|
|
|
610
616
|
overflow: hidden;
|
|
611
617
|
}
|
|
612
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
|
+
|
|
613
644
|
&__hidden-canvas {
|
|
614
645
|
position: fixed;
|
|
615
646
|
top: -10000px;
|
|
@@ -627,5 +658,6 @@ export default {
|
|
|
627
658
|
display: none;
|
|
628
659
|
}
|
|
629
660
|
}
|
|
661
|
+
/* #endif */
|
|
630
662
|
}
|
|
631
663
|
</style>
|