hn-hlw-app/pages/consult/self-auth/auth-qrcode.vue
2026-08-13 13:50:42 +08:00

163 lines
4.9 KiB
Vue

<template>
<full-page :customScroll="true">
<view class="qrcode-container">
<view class="qrcode-content">
<canvas id="auth-qrcode" canvas-id="auth-qrcode" class="qrcode-canvas"
:style="{ width: `${qrcodeSize}px`, height: `${qrcodeSize}px` }" />
<view class="tip">请使用就诊人本人的支付宝</view>
<view class="tip">扫描上方二维码授权</view>
</view>
</view>
<template #footer>
<footer-button :text="checking ? '查询中...' : '已授权'" @onClick="hasAuthed" />
</template>
</full-page>
</template>
<script setup>
import { computed, ref } from "vue";
import { onLoad, onReady } from "@dcloudio/uni-app";
import UQRCode from "@/uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js";
import { get, remove, set } from "@/utils/cache";
import { getSelfAuthResult } from "@/utils/api";
import { toast } from "@/utils/widget";
import fullPage from "@/components/full-page.vue";
import footerButton from "@/components/footer-button.vue";
const baseQrcode = process.env.YB_AUTH_QRCODE;
const AUTH_ID_PATTERN = /^[a-f\d]{24}$/i;
const id = ref("");
const patient = ref(null);
const checking = ref(false);
const qrcodeValue = computed(() => id.value && patient.value ? `${baseQrcode}${id.value}` : "");
const qrcodeSize = Math.round(uni.upx2px(640));
function drawQrcode() {
const value = qrcodeValue.value;
if (!value) return;
try {
const qr = new UQRCode();
qr.data = value;
qr.size = qrcodeSize;
qr.make();
const context = uni.createCanvasContext("auth-qrcode");
const moduleCount = qr.moduleCount;
// 二维码四周至少保留 4 个模块的静区,并使用整数像素避免模块间出现白线。
const moduleSize = Math.max(1, Math.floor(qrcodeSize / (moduleCount + 8)));
const contentSize = moduleSize * moduleCount;
const offset = Math.floor((qrcodeSize - contentSize) / 2);
context.setFillStyle("#fff");
context.fillRect(0, 0, qrcodeSize, qrcodeSize);
context.setFillStyle("#000");
// 将每行相邻的黑色模块合并成一个矩形,避免支付宝 canvas
// 一次提交数千条绘制指令导致页面主线程阻塞。
for (let row = 0; row < moduleCount; row += 1) {
let runStart = -1;
for (let col = 0; col <= moduleCount; col += 1) {
const isBlack = col < moduleCount && qr.isBlack(row, col);
if (isBlack && runStart < 0) {
runStart = col;
} else if (!isBlack && runStart >= 0) {
context.fillRect(
offset + runStart * moduleSize,
offset + row * moduleSize,
(col - runStart) * moduleSize,
moduleSize
);
runStart = -1;
}
}
}
context.draw(false);
} catch (error) {
console.error("生成授权二维码失败", error);
toast("生成授权二维码失败,请重新进入页面");
}
}
async function hasAuthed() {
if (checking.value || !id.value || !patient.value) return;
checking.value = true;
try {
const { success, data, message } = await getSelfAuthResult(id.value);
if (!success || !data) {
toast(message || "查询授权结果失败");
return;
}
if (data.status === "success" && data.hisArchive) {
set("current-his-archive", data.hisArchive);
remove("auth-hlw-patient");
uni.redirectTo({
url: `/pages/consult/disease-description?socialno=${patient.value.certNo}&feeType=YB`,
});
return;
}
if (data.status === "failed") {
toast(data.errorMessage || message || "医保授权失败,请让就诊人重新授权");
return;
}
if (data.status === "expired") {
remove("auth-hlw-patient");
toast("授权链接已过期,请重新发起授权").then(() => uni.navigateBack());
return;
}
toast(data.status === "processing" ? "授权正在处理中,请稍后再试" : "尚未完成授权");
} catch (error) {
toast(error.message || error || "查询授权结果失败");
} finally {
checking.value = false;
}
}
onLoad((options) => {
id.value = options && typeof options.id === "string" ? options.id.trim() : "";
patient.value = get("auth-hlw-patient");
if (!AUTH_ID_PATTERN.test(id.value) || !patient.value || !patient.value.certNo) {
toast("就诊人无效,请重新选择").then(() => uni.navigateBack());
}
});
onReady(() => {
// 等待支付宝小程序完成 canvas 节点挂载后再绘制一次。
setTimeout(drawQrcode, 50);
});
</script>
<style lang="scss" scoped>
.qrcode-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
text-align: center;
}
.qrcode-content {
display: flex;
flex-direction: column;
align-items: center;
transform: translateY(-60rpx);
}
.qrcode-canvas {
display: block;
background: #fff;
}
.tip {
font-size: 32rpx;
line-height: 52rpx;
color: #333;
font-weight: bold;
}
</style>