2026-01-15 18:38:21 +08:00

530 lines
12 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="my-code-page">
<webview src="https://dev-wx.igandan.com/expert/expertcodeimg?expert_uuid=9UFkll2Xo57km6224XE&fromtype=doctor"></webview>
<!-- 顶部导航栏 -->
<navBar title="我的二维码" />
<!-- 内容 -->
<scroll-view scroll-y class="page-scroll" id="pageContent">
<!-- 顶部蓝色横幅 -->
<view class="blue-banner">
<up-image :src="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="avatar-wrapper">
<image class="avatar" :src="docUrl+userInfo.photo" mode="aspectFill" />
</view>
<view class="name-viewne">{{ userInfo.realName }} {{ userInfo.positionName }}</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="viewnkImg" width="430rpx" height="131rpx" ></up-image>
</view>
<image class="qr-img" :src="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>点击邹建东专家工作室选择微信登录注册后直接发送随访申请</view>
<view class="descrow"><text>3</text>若未弹出随访申请发送成功提示请再次点击邹建东专家工作室发送随访申请</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">
<button class="save-btn" @click="onSave">保存到手机相册</button>
</view>
</view>
</template>
<script setup>
import navBar from '@/components/navBar/navBar.vue'
import { onShow } from "@dcloudio/uni-app";
import { ref } from 'vue';
import docUrl from '@/utils/docUrl'
import bgImg from "@/static/background.jpg"
import viewnkImg from "@/static/arr.png"
const userInfo = ref({})
const goBack = () => {
uni.navigateBack();
};
onShow(()=>{
userInfo.value = uni.getStorageSync('userInfo')
})
// 处理保存错误
const handleSaveError = (err) => {
if (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 = () => {
// #ifdef APP-PLUS
// 检查是否在 App 端
if (typeof plus === 'undefined') {
uni.showToast({ title: '当前环境不支持此功能', icon: 'none' });
return;
}
// 显示加载提示
uni.showLoading({ title: '保存中...' });
// 添加超时处理
let timeoutTimer = setTimeout(() => {
uni.hideLoading();
uni.showToast({
title: '操作超时,请重试',
icon: 'none',
duration: 2000
});
}, 15000); // 15秒超时
// 获取当前 webview
const currentWebview = plus.webview.currentWebview();
// 使用 draw 方法将页面绘制成图片
currentWebview.draw({
bitmap: true,
success: (bitmap) => {
clearTimeout(timeoutTimer);
try {
// 将 bitmap 转换为 base64
const base64 = bitmap.toBase64DataURL('image/png', 1.0);
// 去除 base64 前缀
const base64Data = base64.indexOf(',') > -1 ? base64.split(',')[1] : base64;
// 生成临时文件路径
const fileName = 'mycode_' + Date.now() + '.png';
const tempPath = `_doc/${fileName}`;
// 使用 plus.io 写入文件
plus.io.resolveLocalFileSystemURL('_doc', (entry) => {
entry.getFile(fileName, { create: true, exclusive: false }, (fileEntry) => {
fileEntry.createWriter((writer) => {
// base64 转 ArrayBuffer
const binaryString = atob(base64Data);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
writer.onwriteend = () => {
// 写入完成,回收 bitmap
bitmap.recycle();
// 转换为本地路径
const localPath = plus.io.convertLocalFileSystemURL(fileEntry.fullPath);
// 保存到相册
uni.saveImageToPhotosAlbum({
filePath: localPath,
success: () => {
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success',
duration: 2000
});
},
fail: (err) => {
uni.hideLoading();
console.error('保存到相册失败:', err);
handleSaveError(err);
}
});
};
writer.onerror = (err) => {
uni.hideLoading();
console.error('写入文件失败:', err);
bitmap.recycle();
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
};
// 创建 Blob 对象并写入
try {
const blob = new Blob([bytes], { type: 'image/png' });
writer.write(blob);
} catch (e) {
// 如果 Blob 不支持,尝试直接写入 ArrayBuffer
try {
writer.write(bytes.buffer);
} catch (e2) {
uni.hideLoading();
console.error('写入失败:', e2);
bitmap.recycle();
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
}
}
}, (err) => {
clearTimeout(timeoutTimer);
uni.hideLoading();
console.error('创建写入器失败:', err);
bitmap.recycle();
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
});
}, (err) => {
clearTimeout(timeoutTimer);
uni.hideLoading();
console.error('获取文件失败:', err);
bitmap.recycle();
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
});
}, (err) => {
clearTimeout(timeoutTimer);
uni.hideLoading();
console.error('解析文件系统失败:', err);
bitmap.recycle();
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
});
} catch (error) {
clearTimeout(timeoutTimer);
uni.hideLoading();
console.error('处理失败:', error);
if (bitmap && bitmap.recycle) {
bitmap.recycle();
}
uni.showToast({
title: '保存失败,请重试',
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
clearTimeout(timeoutTimer);
uni.hideLoading();
console.error('截图失败:', err);
uni.showToast({
title: '截图失败,请重试',
icon: 'none',
duration: 2000
});
}
});
// #endif
// #ifndef APP-PLUS
uni.showToast({ title: '此功能仅支持 App 端', icon: 'none' });
// #endif
};
</script>
<style lang="scss" scoped>
.my-code-page {
min-height: 100vh;
background-color: #0d7dfd;
}
.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: 76rpx 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;
}
.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: 34rpx;
color: #1a76d2;
margin-top: 10rpx;
}
.org-viewne {
text-align: center;
font-size: 28rpx;
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;
}
}
</style>