2026-02-02 17:44:10 +08:00

982 lines
22 KiB
Vue
Raw 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="navbox" v-if="!jieping">
<view class="status_bar"></view>
<uni-nav-bar
left-icon="left"
title="我的二维码"
@clickLeft="goBack"
color="#8B2316"
:border="false"
backgroundColor="#eeeeee"
>
<template #right>
<view class="nav-actions">
<view class="collect-img" @click="shareToggle" >
<image
class="share-img-icon"
:src="shareIcon"
mode="aspectFill"
/>
</view>
</view>
</template>
</uni-nav-bar>
</view>
<!-- <navBar title="我的二维码" v-if="!jieping"/> -->
<!-- 自己实现的 html2canvas 截图组件 -->
<MyHtml2canvas
ref="shotRef"
domId="render-dom"
@renderOver="onRenderOver"
/>
<view class="my-code-page renderDom" id="render-dom" :class="{'active':jieping}">
<!-- <webview src="https://dev-wx.igandan.com/expert/expertcodeimg?expert_uuid=9UFkll2Xo57km6224XE&fromtype=doctor"></webview> -->
<!-- 顶部导航栏 -->
<!-- 内容 -->
<scroll-view scroll-y class="page-scroll" id="pageContent">
<!-- 顶部蓝色横幅 -->
<view class="blue-banner">
<up-image :src="bgBase64 || bgImg" width="100%" mode="widthFix" ></up-image>
</view>
<!-- 白色信息卡 -->
<view class="qrbox">
<view class="qr-card">
<view class="leftCircle"></view>
<view class="rightCircle"></view>
<view class="halfCircle"></view>
<view class="star" v-if="userInfo.isStar==1">
咨询专家
</view>
<view class="avatar-wrapper">
<image
class="avatar"
:src="userInfo.avatarBase64 || userInfo.localPhoto || (docUrl+userInfo.photo)"
mode="aspectFill"
/>
</view>
<view class="name-viewne">{{ userInfo.realName }} <text class="position-name">{{ userInfo.positionName }}</text></view>
<view class="org-viewne">{{ userInfo.hospitalName }}</view>
<view class="dash-viewne"></view>
<view class="slogan">
<text class="h1">不方便到医院就诊</text>
<text >我在<text class="hl">肝胆相照</text>线上帮助您!</text>
</view>
<view class="contact-qr">
<view class="contact-btn">
<up-image :src="viewnkBase64 || viewnkImg" width="430rpx" height="131rpx" ></up-image>
</view>
<image class="qr-img" :src="userInfo.localQrcode || (docUrl+userInfo.qrcode)" mode="aspectFit" />
</view>
</view>
</view>
<view class="squrebox">
<view class="square">
<view class="s-top"></view>
<view class="s-middle">
<view id="head">如何添加我为随访医生?</view>
</view>
<view class="s-bottom">
<view>
<view class="descrow"><text>1</text>微信扫一扫上方二维码,关注“肝胆相照一家人”公众号</view>
<view class="descrow"><text>2</text>点击“{{ userInfo.realName }}专家工作室”,选择微信登录,注册后直接发送随访申请</view>
<view class="descrow"><text>3</text>若未弹出“随访申请发送成功”提示,请再次点击“{{ userInfo.realName }}专家工作室”发送随访申请</view>
<view class="descrow"><text>4</text>审核通过后,点击“就医服务-随访交流”,与专家进行图文交流</view>
</view>
</view>
<view class="s-half-circle"></view>
<view class="s-half-circle-left"></view>
</view>
</view>
</scroll-view>
<!-- 底部保存按钮 -->
<view class="save-bar" v-if="!jieping">
<button class="save-btn" @click="onSave">保存到手机相册</button>
</view>
</view>
<!-- 分享弹窗 -->
<uni-popup ref="shareRef" type="bottom" safeArea backgroundColor="#fff">
<view class="share-popup">
<view class="share-title">分享到</view>
<view class="share-content">
<view class="share-item" @click="shareToWechat">
<view class="share-icon wechat-icon">
<image class="share-img" :src="wxImg" mode="aspectFill" />
</view>
<text class="share-text">微信</text>
</view>
<view class="share-item" @click="shareToMoments">
<view class="share-icon moments-icon">
<image class="share-img" :src="friendImg" mode="aspectFill" />
</view>
<text class="share-text">朋友圈</text>
</view>
<view class="share-item" @click="shareToWeibo">
<view class="share-icon weibo-icon">
<image class="share-img" :src="sinaImg" mode="aspectFill" />
</view>
<text class="share-text">新浪微博</text>
</view>
</view>
<view class="share-cancel" @click="closeShare">
<text>取消</text>
</view>
</view>
</uni-popup>
</template>
<script setup >
import { base64ToPath } from 'image-tools'
import { urlToBase64 } from '@/uni_modules/sp-html2canvas-render/utils/index.js'
import navBar from '@/components/navBar/navBar.vue'
import { onShow,onBackPress,onLoad} from "@dcloudio/uni-app";
import { ref } from 'vue';
import MyHtml2canvas from '@/components/MyHtml2canvas.vue'
import docUrl from '@/utils/docUrl'
import otherHost from '@/utils/otherHost'
import shareIcon from "@/static/icon_share.png";
import sinaImg from "@/static/share_sina.png";
import wxImg from "@/static/share_weixin.png";
import friendImg from "@/static/share_wxc.png";
import logoImg from "@/static/weiboShare.png";
import isAndroid from "@/utils/platform.js";
// 背景图使用线上地址
const bgImg = 'https://oss.igandan.com/images/expertcode_background.jpg?v=1'
import viewnkImg from "@/static/arr.png"
const userInfo = ref({})
const jieping = ref(false)
const shotRef = ref(null)
const bgBase64 = ref('')
const viewnkBase64 = ref('')
const from=ref('');
const title=ref('');
const summary=ref('参与爱肝行动,共创美好前程');
const link=ref('');
onBackPress(() => {
if(!from.value){
plus.runtime.quit();
return true;
}
});
const goBack = () => {
if(!from.value){
plus.runtime.quit();
return true;
}else{
uni.navigateBack();
}
};
onLoad((options) => {
from.value = options.from;
})
// 页面显示时拉取用户信息,并尽量把图片转成 base64避免 html2canvas 跨域污染
onShow(async () => {
const info = uni.getStorageSync('userInfo') || {}
console.log('[myCode] onShow userInfo:', info)
title.value = '不方便到医院就诊,'+info.realName+'医生在肝胆相照平台帮助您!';
link.value = otherHost+'/expert/expertcodeimg?expert_uuid='+info.uuid+'&fromtype=doctor'
// 背景图转 base64
if (!bgBase64.value) {
try {
console.log('[myCode] 准备将背景图转为 base64:', bgImg)
const bg = await urlToBase64(bgImg, 'jpg')
bgBase64.value = bg
console.log('[myCode] 背景图转 base64 成功,长度:', bg && bg.length)
} catch (e) {
console.error('[myCode] 背景图转 base64 失败:', e)
}
}
// 按钮图转 base64
if (!viewnkBase64.value) {
try {
console.log('[myCode] 准备将按钮图转为 base64:', viewnkImg)
const btn = await urlToBase64(viewnkImg, 'png')
viewnkBase64.value = btn
console.log('[myCode] 按钮图转 base64 成功,长度:', btn && btn.length)
} catch (e) {
console.error('[myCode] 按钮图转 base64 失败:', e)
}
}
// 头像:远程地址转 base64写入 avatarBase64优先被使用
if (info.photo && !info.avatarBase64) {
const avatarUrl = docUrl + info.photo
try {
console.log('[myCode] 准备将头像转为 base64:', avatarUrl)
const avatarBase64 = await urlToBase64(avatarUrl, 'jpg')
info.avatarBase64 = avatarBase64
console.log('[myCode] 头像转 base64 成功,长度:', avatarBase64 && avatarBase64.length)
} catch (e) {
console.error('[myCode] 头像转 base64 失败:', e)
}
}
// 二维码:远程地址转 base64写入 localQrcode模板已优先使用该字段
if (info.qrcode && !info.localQrcode) {
const qrUrl = docUrl + info.qrcode
try {
console.log('[myCode] 准备将二维码转为 base64:', qrUrl)
const qrBase64 = await urlToBase64(qrUrl, 'png')
info.localQrcode = qrBase64
console.log('[myCode] 二维码转 base64 成功,长度:', qrBase64 && qrBase64.length)
} catch (e) {
console.error('[myCode] 二维码转 base64 失败:', e)
}
}
userInfo.value = info
})
// 保存失败时统一处理
const handleSaveError = (err) => {
if (err && err.errMsg && (err.errMsg.includes('auth deny') || err.errMsg.includes('authorize'))) {
uni.showModal({
title: '权限提示',
content: '需要相册权限才能保存图片,请在系统设置中开启相册权限',
showCancel: false,
confirmText: '知道了'
});
} else {
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
}
};
// 点击“保存到手机相册”
const onSave = () => {
console.log('[myCode] onSave 点击,准备开始截图')
// 使用自定义组件触发 html2canvas内部通过 renderjs 在视图层执行)
jieping.value = true
// 调用自定义组件方法开始渲染
if (shotRef.value && shotRef.value.h2cRenderDom) {
shotRef.value.h2cRenderDom()
} else {
console.error('[myCode] shotRef 或 h2cRenderDom 不存在')
jieping.value = false
uni.showToast({
title: '截图组件未准备好',
icon: 'none'
})
}
};
// 自定义组件截图完成回调(拿到 base64
const onRenderOver = (base64) => {
console.log('[myCode] onRenderOver base64 长度:', base64 && base64.length)
if (!base64) {
jieping.value = false
uni.showToast({
title: '生成图片失败',
icon: 'none'
})
return
}
base64ToPath(base64)
.then((res) => {
const path = res.path || res
console.log('[myCode] base64ToPath 转换成功path:', path)
uni.saveImageToPhotosAlbum({
filePath: path,
success() {
uni.showToast({
title: '保存图片成功',
mask: false,
duration: 1500
})
},
fail(err) {
console.error('[myCode] 保存到相册失败:', err)
handleSaveError(err)
},
complete() {
jieping.value = false
}
})
})
.catch((err) => {
console.error('[myCode] base64ToPath 失败:', err)
handleSaveError(err)
jieping.value = false
})
}
const shareRef = ref();
// 分享APP
const shareToggle = () => {
shareRef.value.open();
};
// 关闭分享弹窗
const closeShare = () => {
shareRef.value.close();
};
// 分享到微信
const shareToWechat = () => {
// #ifdef APP-PLUS
// 使用系统分享
uni.downloadFile({
url:'https://doc.igandan.com/app/html/img/2016/20160714132557.png',
success: function (res) {
uni.compressImage({
src: res.tempFilePath,
quality: 60,
success: function (res2) {
uni.share({
provider: "weixin",
scene: "WXSceneSession",
type: 0,
title: title.value,
summary: summary.value,
href: link.value,
imageUrl: res2.tempFilePath,
success: function (res) {
console.log("success:" + JSON.stringify(res));
},
fail: function (err) {
console.log("fail:" + JSON.stringify(err));
},
});
},
});
},
});
// uni.share({
// provider: "weixin",
// scene: "WXSceneSession",
// type: 0,
// title: videoInfo.value.name,
// summary: videoInfo.value.note,
// href: shareLink.value,
// imageUrl: docUrl + videoInfo.value.imgpath,
// success: function (res) {
// //console.log("success:" + JSON.stringify(res));
// },
// fail: function (err) {
// //console.log("fail:" + JSON.stringify(err));
// },
// });
// #endif
// #ifdef H5
// H5环境下的分享
if (navigator.share) {
navigator
.share({
title: "肝胆相照APP",
text: "专业的医疗健康平台,快来下载体验吧!",
url: shareLink.value,
})
.then(() => {
uni.showToast({
title: "分享成功",
icon: "success",
});
})
.catch(() => {
uni.showToast({
title: "分享失败",
icon: "none",
});
});
} else {
// 复制到剪贴板
uni.setClipboardData({
data:
"肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接" +
shareLink.value,
success: () => {
uni.showToast({
title: "已复制到剪贴板",
icon: "success",
});
},
});
}
// #endif
// #ifdef MP-WEIXIN
// 微信小程序分享
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"],
});
// #endif
closeShare();
};
// 分享到朋友圈
const shareToMoments = () => {
// #ifdef APP-PLUS
uni.downloadFile({
url: 'https://doc.igandan.com/app/html/img/2016/20160714132557.png',
success: function (res) {
uni.compressImage({
src: res.tempFilePath,
quality: 60,
success: function (res2) {
uni.share({
provider: "weixin",
scene: "WXSceneTimeline",
type: 0,
title: title.value,
summary: summary.value,
href: link.value,
imageUrl: res2.tempFilePath,
success: function (res) {
//console.log("success:" + JSON.stringify(res));
},
fail: function (err) {
//console.log("fail:" + JSON.stringify(err));
},
});
},
});
},
});
// #endif
// #ifdef H5
// 复制到剪贴板
uni.setClipboardData({
data: "肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接https://www.igandan.com",
success: () => {
uni.showToast({
title: "已复制到剪贴板,可分享到朋友圈",
icon: "success",
});
},
});
// #endif
// #ifdef MP-WEIXIN
uni.showShareMenu({
withShareTicket: true,
menus: ["shareAppMessage", "shareTimeline"],
});
// #endif
closeShare();
};
// 分享到新浪微博
const shareToWeibo = () => {
// #ifdef APP-PLUS
uni.share({
provider: "sinaweibo",
type: 0,
title: title.value,
summary: summary.value,
href: link.value,
imageUrl: logoImg,
success: function (res) {
//console.log("分享成功");
},
fail: function (err) {
//console.log("fail:" + JSON.stringify(err));
},
});
// plus.share.sendWithSystem({
// type: 'text',
// content: '肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接https://www.igandan.com'
// }, () => {
// uni.showToast({
// title: '分享成功',
// icon: 'success'
// })
// }, (err) => {
// console.log('分享失败:', err)
// uni.showToast({
// title: '分享失败',
// icon: 'none'
// })
// })
// #endif
// #ifdef H5
// 复制到剪贴板
uni.setClipboardData({
data: "肝胆相照APP - 专业的医疗健康平台,快来下载体验吧!\n下载链接https://www.igandan.com",
success: () => {
uni.showToast({
title: "已复制到剪贴板,可分享到微博",
icon: "success",
});
},
});
// #endif
closeShare();
};
</script>
<style lang="scss" scoped>
.share-img {
width: 100rpx;
height: 100rpx;
}
.share-img-icon {
width: 40rpx;
height:40rpx;
}
.nav-actions {
display: flex;
align-items: center;
justify-content: center;
}
.my-code-page {
min-height: 100vh;
background-color: #0d7dfd;
.active{
transform: scale(0.8);
margin-top: -calc(var(--status-bar-height) + 44px);
transition: all 0.3s ease-in-out;
}
}
.squrebox{
margin:0 30rpx;
overflow: hidden;
}
.square{
background-color: #3492FC;
border-radius: 30rpx;
padding-bottom: 40rpx;
border: 2rpx solid #fff;
position: relative;
.s-half-circle-left{
border: 2rpx solid #fff;
position: absolute;
width: 60rpx;
height: 60rpx;
z-index:1;
border-radius:50%;
top:53rpx;
background-color: #0d7dfd;
left: -30rpx;
}
.s-half-circle{
border: 2rpx solid #fff;
position: absolute;
width: 60rpx;
height: 60rpx;
z-index:1;
border-radius:50%;
top:53rpx;
background-color: #0d7dfd;
right: -30rpx;
}
text{
display: inline-flex;
width: 40rpx;
background-color: #6FB3FE;
height: 40rpx;
margin-right: 10rpx;
border-radius:50%;
align-items: center;
justify-content: center;
}
#head {
width: 75%;
height: 60rpx;
line-height: 60rpx;
color: white;
margin: 0 auto;
text-align: center;
border-radius: 30rpx;
border: 2rpx solid white;
letter-spacing: 8rpx;
}
.s-top{
margin-top: 50rpx;
}
.s-bottom{
padding:0 40rpx;
font-size: 26rpx;
margin-top: 30rpx;
line-height: 40rpx;
color:#fff;
.descrow{
margin-bottom: 10rpx;
}
}
}
.page-scroll {
margin-top: calc(var(--status-bar-height) + 44px);
// height: calc(100vh - var(--status-bar-height) - 44px);
padding-bottom: 140rpx;
}
.blue-banner {
height: 520rpx;
width:100%;
color: #ffffff;
text-align: center;
.banner-title-small {
font-size: 26rpx;
opacity: .9;
}
.banner-title {
margin-top: 40rpx;
font-size: 44rpx;
letter-spacing: 2rpx;
font-weight: 600;
}
}
.qr-card {
position: relative;
margin: -80rpx 30rpx 30rpx;
background: #ffffff;
border-radius: 20rpx;
padding: 140rpx 30rpx 30rpx;
box-shadow: 0 12rpx 30rpx rgba(0,0,0,.08);
.leftCircle{
position: absolute;
top:251rpx;
left:-20rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #0d7dfd;
}
.rightCircle{
position: absolute;
top:251rpx;
right:-20rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background-color: #0d7dfd;
}
.star{
position: absolute;
top: 48rpx;
height: 40rpx;
padding:0 16rpx;
left: 50%;
transform: translateX(-50%);
background-color:#ff9f1a;
color: #fff;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
z-index:10;
font-size: 24rpx;
}
.halfCircle{
border-radius: 50%;
position: absolute;
border: 10rpx solid #6DB7FD;
border-width: 10rpx 10rpx 0 10rpx;
width: 160rpx;
padding:10rpx;
height: 80rpx;
left: 50%;
border-radius: 100rpx 100rpx 0 0;
top: -110rpx;
transform: translateX(-50%);
overflow: hidden;
}
.avatar-wrapper {
position: absolute;
left: 50%;
top: -100rpx;
transform: translateX(-50%);
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background: #ffffff;
padding: 8rpx;
box-shadow: 0 6rpx 20rpx rgba(0,0,0,.1);
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.name-viewne {
text-align: center;
font-size: 36rpx;
color: #1a76d2;
font-weight: bold;
margin-top: 10rpx;
.position-name{
font-weight: normal;
}
}
.org-viewne {
text-align: center;
font-size: 36rpx;
color: #4a90e2;
margin-top: 16rpx;
}
.dash-viewne {
margin: 24rpx auto;
height: 0;
border-bottom: 2rpx dashed #1c90fd;
}
.slogan {
display: flex;
font-weight:bold;
letter-spacing: 8rpx;
flex-direction: column;
text-align: center;
font-size: 40rpx;
color: #1e88e5;
line-height: 1.6;
text{
text-align: center;
}
.hl {
color: #ff9f1a;
margin-left: 8rpx;
}
}
.contact-qr {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20rpx;
margin-top: 30rpx;
.contact-btn {
flex: 1;
position: relative;
color: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
.contact-text {
white-space: pre-viewne;
font-size: 26rpx;
line-height: 1.5;
}
.arrow {
font-size: 36rpx;
opacity: .8;
}
}
.qr-img {
width: 220rpx;
height: 220rpx;
border-radius: 12rpx;
border: 2rpx soviewd #e0e0e0;
}
}
}
.bottom-gap { height: 120rpx; }
.save-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index:10;
background: #ffffff;
border-top: 1rpx soviewd #eaeaea;
.save-btn {
width: 100%;
height: 100rpx;
background: #00cac1;
color: #ffffff;
border: none;
border-radius: 0;
font-size: 32rpx;
font-weight: normal;
}
}
/* 分享弹窗样式 */
.share-popup {
background-color: #fff;
border-radius: 20rpx 20rpx 0 0;
padding: 40rpx 0 0;
}
.share-title {
text-align: center;
font-size: 32rpx;
color: #333;
margin-bottom: 40rpx;
font-weight: 500;
}
.share-content {
display: flex;
justify-content: space-around;
padding: 0 40rpx 40rpx;
}
.share-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.share-icon {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
}
.wechat-icon {
background-color: #07c160;
}
.moments-icon {
background-color: #07c160;
}
.weibo-icon {
background-color: #e6162d;
}
.qq-icon {
background-color: #12b7f5;
}
.share-icon-text {
font-size: 50rpx;
color: #fff;
font-weight: bold;
}
.share-text {
font-size: 24rpx;
color: #666;
}
.share-cancel {
height: 100rpx;
display: flex;
align-items: center;
justify-content: center;
border-top: 1rpx solid #f0f0f0;
font-size: 32rpx;
color: #333;
}
.share-cancel:active {
background-color: #f5f5f5;
}
// 删除确认弹窗
.delete-modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.delete-modal-content {
width: 600rpx;
background-color: #fff;
border-radius: 24rpx;
padding: 60rpx 40rpx 40rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
}
.delete-modal-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
text-align: center;
margin-bottom: 30rpx;
}
.delete-modal-text {
font-size: 28rpx;
color: #666;
text-align: center;
line-height: 1.6;
margin-bottom: 50rpx;
word-break: break-all;
}
.delete-modal-buttons {
display: flex;
gap: 20rpx;
}
.delete-modal-btn {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
font-size: 32rpx;
font-weight: 500;
transition: all 0.3s ease;
}
.cancel-btn {
background-color: #f5f5f5;
color: #333;
&:active {
background-color: #e0e0e0;
}
}
.confirm-btn {
background-color: #8b2316;
color: #fff;
&:active {
background-color: #6b1a0f;
}
}
</style>