feat: 打印预览组件化
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"toy-icon": "../../toy/icon/icon"
|
||||
},
|
||||
"styleIsolation": "apply-shared",
|
||||
"addGlobalClass": true
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
@import '../../style/theme.less';
|
||||
|
||||
.pc-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pc-card {
|
||||
position: relative;
|
||||
background: @bg-white;
|
||||
border-radius: @radius-xl;
|
||||
box-shadow: @shadow;
|
||||
padding: 40rpx;
|
||||
border: @border;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.pc-card--with-fab {
|
||||
padding-bottom: 56rpx;
|
||||
}
|
||||
|
||||
.pc-kicker {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: @fs-section-head-title;
|
||||
font-weight: bold;
|
||||
color: @text-secondary;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.pc-canvas-wrap {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 400rpx;
|
||||
background: @bg-gray;
|
||||
border-radius: @radius-sm;
|
||||
border: 2rpx dashed rgba(50, 46, 37, 0.12);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pc-canvas {
|
||||
max-width: 100%;
|
||||
border-radius: @radius-sm;
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
/* 底部角上的圆形操作按钮 */
|
||||
@pc-fab-size: 88rpx;
|
||||
|
||||
@keyframes pc-fab-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.pc-fab {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: @pc-fab-size;
|
||||
height: @pc-fab-size;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 6rpx 18rpx rgba(50, 46, 37, 0.12);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pc-fab--hover {
|
||||
opacity: 0.88;
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.pc-fab--refresh {
|
||||
left: 12rpx;
|
||||
bottom: 12rpx;
|
||||
background: #9add4c;
|
||||
}
|
||||
|
||||
.pc-fab--favorite {
|
||||
right: 12rpx;
|
||||
bottom: 12rpx;
|
||||
background: #f2eee4;
|
||||
}
|
||||
|
||||
.pc-fab__toy {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pc-fab__icon-wrap--a .pc-fab__toy,
|
||||
.pc-fab__icon-wrap--b .pc-fab__toy {
|
||||
animation: pc-fab-spin 0.55s linear 1;
|
||||
}
|
||||
|
||||
.pc-fab__icon-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import { PAPER_SIZE, A4_EXPORT_SIZE_150DPI } from '../../constants/colors';
|
||||
|
||||
Component({
|
||||
options: {
|
||||
styleIsolation: 'apply-shared',
|
||||
addGlobalClass: true,
|
||||
},
|
||||
|
||||
properties: {
|
||||
kicker: {
|
||||
type: String,
|
||||
value: '打印预览',
|
||||
},
|
||||
showRefresh: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
showFavorite: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
favorited: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
},
|
||||
favoritedIconColor: {
|
||||
type: String,
|
||||
value: '#E53935',
|
||||
},
|
||||
favoriteIdleIconColor: {
|
||||
type: String,
|
||||
value: '#605b50',
|
||||
},
|
||||
},
|
||||
|
||||
data: {
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
refreshIconAnimClass: '',
|
||||
withFab: false,
|
||||
},
|
||||
|
||||
observers: {
|
||||
'showRefresh, showFavorite'(
|
||||
showRefresh: boolean,
|
||||
showFavorite: boolean,
|
||||
) {
|
||||
this.setData({ withFab: !!showRefresh || !!showFavorite });
|
||||
},
|
||||
},
|
||||
|
||||
lifetimes: {
|
||||
ready() {
|
||||
this.initCanvas();
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
initCanvas() {
|
||||
const query = this.createSelectorQuery();
|
||||
query
|
||||
.select('#canvasWrapper')
|
||||
.boundingClientRect((rect) => {
|
||||
if (!rect) return;
|
||||
|
||||
const { width, height } = PAPER_SIZE['A4'];
|
||||
const boxWidth = rect.width;
|
||||
const boxHeight = boxWidth / (width / height);
|
||||
|
||||
this.setData({ boxWidth, boxHeight });
|
||||
|
||||
this.createSelectorQuery()
|
||||
.select('#canvasContent')
|
||||
.fields({ node: true, size: true })
|
||||
.exec((res) => {
|
||||
if (!res[0]) return;
|
||||
|
||||
const canvas = res[0].node;
|
||||
const ctx = canvas.getContext('2d');
|
||||
const dpr = wx.getSystemInfoSync().pixelRatio;
|
||||
|
||||
canvas.width = boxWidth * dpr;
|
||||
canvas.height = boxHeight * dpr;
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
this._canvas = canvas;
|
||||
|
||||
this.triggerEvent('canvas-ready', {
|
||||
canvas,
|
||||
ctx,
|
||||
width: boxWidth,
|
||||
height: boxHeight,
|
||||
});
|
||||
});
|
||||
})
|
||||
.exec();
|
||||
},
|
||||
|
||||
onRefreshTap() {
|
||||
this.triggerEvent('refresh', {});
|
||||
if ((this as any)._refreshSpinTimer) {
|
||||
clearTimeout((this as any)._refreshSpinTimer);
|
||||
}
|
||||
this.setData({ refreshIconAnimClass: '' });
|
||||
const n = ((this as any)._refreshTapCount || 0) + 1;
|
||||
(this as any)._refreshTapCount = n;
|
||||
const sub = n % 2 === 1 ? 'a' : 'b';
|
||||
(this as any)._refreshSpinTimer = setTimeout(() => {
|
||||
(this as any)._refreshSpinTimer = null;
|
||||
this.setData({
|
||||
refreshIconAnimClass: `pc-fab__icon-wrap--${sub}`,
|
||||
});
|
||||
}, 20);
|
||||
},
|
||||
|
||||
onFavoriteTap() {
|
||||
this.triggerEvent('favorite', {});
|
||||
},
|
||||
|
||||
getCanvas(): WechatMiniprogram.Canvas | null {
|
||||
return this._canvas || null;
|
||||
},
|
||||
|
||||
exportToTempFile(
|
||||
options?: { quality?: number; fileType?: 'png' | 'jpg' },
|
||||
): Promise<string> {
|
||||
const canvas = this._canvas;
|
||||
if (!canvas) {
|
||||
return Promise.reject(new Error('Canvas not initialized'));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.canvasToTempFilePath({
|
||||
canvas,
|
||||
fileType: options?.fileType || 'png',
|
||||
quality: options?.quality ?? 1,
|
||||
destWidth: A4_EXPORT_SIZE_150DPI.width,
|
||||
destHeight: A4_EXPORT_SIZE_150DPI.height,
|
||||
success: (res) => resolve(res.tempFilePath),
|
||||
fail: (err) => reject(err),
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
<view class="pc-wrap">
|
||||
<view class="pc-card {{withFab ? 'pc-card--with-fab' : ''}}">
|
||||
<text class="pc-kicker">{{kicker}}</text>
|
||||
<view id="canvasWrapper" class="pc-canvas-wrap">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="pc-canvas"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<view
|
||||
wx:if="{{showRefresh}}"
|
||||
class="pc-fab pc-fab--refresh"
|
||||
hover-class="pc-fab--hover"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
catchtap="onRefreshTap">
|
||||
<view
|
||||
class="pc-fab__icon-wrap {{refreshIconAnimClass}}">
|
||||
<toy-icon
|
||||
name="refresh"
|
||||
size="40rpx"
|
||||
color="#2d5016"
|
||||
custom-class="pc-fab__toy" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
wx:if="{{showFavorite}}"
|
||||
class="pc-fab pc-fab--favorite"
|
||||
hover-class="pc-fab--hover"
|
||||
hover-start-time="0"
|
||||
hover-stay-time="70"
|
||||
catchtap="onFavoriteTap">
|
||||
<toy-icon
|
||||
name="{{favorited ? 'heart' : 'heart-o'}}"
|
||||
size="40rpx"
|
||||
color="{{favorited ? favoritedIconColor : favoriteIdleIconColor}}"
|
||||
custom-class="pc-fab__toy" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
Reference in New Issue
Block a user