Files
xk-doctor-wx/subPackages/sub_workbench/workbench_code.vue
2026-06-05 12:28:36 +08:00

260 lines
7.3 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="body-code-qr">
<!-- 海报展示区利用 flex: 1 自动撑开并居中 -->
<view class="poster-display-area">
<view class="poster-shadow-wrap">
<l-painter ref="cardPainter" :key="cardPosterKey" :board="cardPoster" />
</view>
</view>
<!-- 隐藏的纯二维码绘制区 -->
<view class="qr-only-wrap">
<l-painter ref="qrPainter" :key="qrPosterKey" :board="qrOnlyPoster" />
</view>
<!-- 底部操作面板精简为单按钮背景透明 -->
<view class="bottom-action-panel">
<u-button
:throttle-time="500"
shape="circle"
:custom-style="btnStylePrimary"
:ripple="true"
@click="saveCard"
>
{{ labels.card || '保存卡片' }}
</u-button>
</view>
</view>
</template>
<script>
import LPainter from './uni_modules/lime-painter/components/l-painter/l-painter.vue'
import { getClinicAdminStoreQr } from '@/api/clinicAdmin.js'
import { getClinicSalespersonMyInfo } from '@/api/clinicSalesperson.js'
import {
consumeQrPreviewPayload,
buildSalespersonQrPayload,
buildClinicQrPayload,
} from '@/utils/qrCodePreview.js'
import {
buildPosterBoard,
buildQrOnlyPoster,
resolveQrImageUrl,
buttonLabelsForType,
navTitleForType,
} from './components/qr-code-preview/build-poster.js'
export default {
components: { 'l-painter': LPainter },
data() {
return {
previewType: 'doctor',
previewPayload: {},
cardPoster: { css: { width: '580rpx' }, views: [] },
qrOnlyPoster: { css: { width: '420rpx' }, views: [] },
cardPosterKey: 'doctor-',
qrPosterKey: 'doctor-qr-',
initDone: false,
pageReady: false,
// 优化后的主按钮样式
btnStylePrimary: {
width: '600rpx',
height: '88rpx',
backgroundColor: '#6ACDBB',
color: '#fff',
fontSize: '32rpx',
fontWeight: 'bold',
marginBottom: '24rpx',
border: 'none'
}
}
},
computed: {
labels() {
return buttonLabelsForType(this.previewType)
},
},
watch: {
previewType: 'refreshPosters',
previewPayload: {
deep: true,
handler: 'refreshPosters',
},
},
async onLoad(query) {
await this.initPreview(query)
this.initDone = true
this.tryRefreshPosters()
},
onReady() {
this.pageReady = true
this.tryRefreshPosters()
},
methods: {
tryRefreshPosters() {
if (!this.initDone || !this.pageReady) return
this.$nextTick(() => {
this.refreshPosters()
})
},
async initPreview(query) {
const cached = consumeQrPreviewPayload()
const type = (query && query.type) || (cached && cached.type) || 'doctor'
if (cached && cached.payload) {
this.previewPayload = cached.payload
} else if (type === 'doctor') {
this.previewPayload = uni.getStorageSync('doctorInfo') || {}
} else {
this.previewPayload = await this.loadFallbackPayload(type)
}
this.previewType = type
const title = (cached && cached.title) || navTitleForType(type)
uni.setNavigationBarTitle({ title })
},
async loadFallbackPayload(type) {
try {
if (type === 'salesperson') {
const wrap = await getClinicSalespersonMyInfo()
if (wrap && wrap.ok && wrap.data) {
return buildSalespersonQrPayload(wrap.data).payload
}
}
if (type === 'clinic') {
const wrap = await getClinicAdminStoreQr()
if (wrap && wrap.ok && wrap.data) {
return buildClinicQrPayload(wrap.data).payload
}
}
return {}
} catch (error) {
console.error('加载二维码数据失败', error)
return {}
}
},
refreshPosters() {
const payload = this.previewType === 'doctor'
? (Object.keys(this.previewPayload || {}).length ? this.previewPayload : uni.getStorageSync('doctorInfo') || {})
: (this.previewPayload || {})
const qrUrl = resolveQrImageUrl(this.previewType, payload)
if (!qrUrl && this.previewType !== 'doctor') {
uni.showToast({ title: '暂无二维码', icon: 'none' })
}
this.cardPosterKey = `${this.previewType}-${qrUrl}`
this.qrPosterKey = `${this.previewType}-qr-${qrUrl}`
this.cardPoster = buildPosterBoard(this.previewType, payload)
this.qrOnlyPoster = buildQrOnlyPoster(qrUrl)
},
saveCard() {
this.withAlbumAuth(() => this.exportPainter('cardPainter'))
},
withAlbumAuth(done) {
uni.getSetting({
success: (res) => {
if (res.authSetting['scope.writePhotosAlbum']) {
done()
return
}
uni.authorize({
scope: 'scope.writePhotosAlbum',
success: () => done(),
fail: () => {
uni.showModal({
title: '权限请求',
content: '需要您授权保存图片到相册的权限,以便保存二维码',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting({ success: () => {} })
}
}
})
},
})
},
})
},
exportPainter(refName) {
const painter = this.$refs[refName]
if (!painter) return
// 添加指定文案的 Loading
uni.showLoading({ title: '正在生成二维码卡片', mask: true })
painter.canvasToTempFilePathSync({
fileType: 'png',
pathType: 'url',
quality: 1,
success: (res) => {
uni.hideLoading()
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
uni.showToast({ title: '已保存到相册', icon: 'success' })
},
fail: () => {
uni.showToast({ title: '保存失败,请重试', icon: 'none' })
},
})
},
fail: () => {
uni.hideLoading()
uni.showToast({ title: '生成图片失败', icon: 'none' })
},
})
},
},
}
</script>
<style lang="scss" scoped>
.body-code-qr {
display: flex;
flex-direction: column;
width: 100%;
min-height: 100vh;
/* 优化渐变色:使其看起来更加平滑和现代 */
background: linear-gradient(135deg, #5b98ff 0%, #17cdc1 100%);
}
.poster-display-area {
flex: 1; /* 占据除底部按钮外的所有空间 */
display: flex;
justify-content: center;
align-items: center; /* 垂直居中,适配各类屏幕 */
padding: 40rpx 0;
}
/* 为海报增加柔和的阴影,提升悬浮感和视觉层次 */
.poster-shadow-wrap {
border-radius: 24rpx;
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15);
overflow: hidden; /* 防止内部 canvas 的直角溢出 */
background-color: transparent;
}
.qr-only-wrap {
position: fixed;
left: -9999rpx;
top: 0;
width: 420rpx;
height: 420rpx;
opacity: 0;
pointer-events: none;
}
/* 底部操作区:背景透明,仅保留底部安全距离 */
.bottom-action-panel {
display: flex;
flex-direction: column;
align-items: center;
background-color: transparent; /* 背景透明 */
padding: 48rpx 32rpx;
padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); /* 核心:完美适配 iPhone 底部黑条 */
}
</style>