224 lines
5.7 KiB
Vue
224 lines
5.7 KiB
Vue
|
|
<template>
|
|||
|
|
<full-page :customScroll="true" pageStyle="background: #f5f5f5;">
|
|||
|
|
<view class="auth-page">
|
|||
|
|
<view class="title">医保本人授权</view>
|
|||
|
|
<view class="description">请核对就诊人信息,并由就诊人本人完成医保授权</view>
|
|||
|
|
|
|||
|
|
<view v-if="patient" class="patient-card">
|
|||
|
|
<view class="info-row">
|
|||
|
|
<text class="label">姓名</text>
|
|||
|
|
<text class="value">{{ patient.name || '--' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-row">
|
|||
|
|
<text class="label">证件号</text>
|
|||
|
|
<text class="value">{{ patient.certNo || '--' }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-row">
|
|||
|
|
<text class="label">年龄</text>
|
|||
|
|
<text class="value">{{ patient.age === '' ? '--' : `${patient.age}岁` }}</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="info-row">
|
|||
|
|
<text class="label">性别</text>
|
|||
|
|
<text class="value">{{ patient.sex || '--' }}</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<view v-else-if="errorMessage" class="state-card state-card--error">
|
|||
|
|
<view>{{ errorMessage }}</view>
|
|||
|
|
<view class="retry" @click="loadPatient">重新加载</view>
|
|||
|
|
</view>
|
|||
|
|
<view v-else class="state-card">正在加载就诊人信息...</view>
|
|||
|
|
|
|||
|
|
<view v-if="completed" class="success-card">
|
|||
|
|
<view class="success-title">授权成功</view>
|
|||
|
|
<view>医保建档已完成,请返回原手机继续操作</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<template v-if="patient && !completed" #footer>
|
|||
|
|
<footer-button :text="submitting ? '授权处理中...' : '确认授权'" @onClick="authorize" />
|
|||
|
|
</template>
|
|||
|
|
</full-page>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { ref } from "vue";
|
|||
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|||
|
|
import useUser from "@/hooks/useUser";
|
|||
|
|
import ybPlugin from "@/utils/insurance-plugin";
|
|||
|
|
import { getSelfAuthPatient, submitSelfAuth } from "@/utils/api";
|
|||
|
|
import { get, remove, set } from "@/utils/cache";
|
|||
|
|
import { toast } from "@/utils/widget";
|
|||
|
|
|
|||
|
|
import fullPage from "@/components/full-page.vue";
|
|||
|
|
import footerButton from "@/components/footer-button.vue";
|
|||
|
|
|
|||
|
|
const AUTH_ID_PATTERN = /^[a-f\d]{24}$/i;
|
|||
|
|
|
|||
|
|
const { getAuthCode, login, logined } = useUser();
|
|||
|
|
const authId = ref("");
|
|||
|
|
const patient = ref(null);
|
|||
|
|
const errorMessage = ref("");
|
|||
|
|
const submitting = ref(false);
|
|||
|
|
const completed = ref(false);
|
|||
|
|
|
|||
|
|
function parseId(value) {
|
|||
|
|
const id = typeof value === "string" ? value.trim() : "";
|
|||
|
|
if (!AUTH_ID_PATTERN.test(id)) return false;
|
|||
|
|
authId.value = id;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function ensureLogin() {
|
|||
|
|
if (!logined.value) await login();
|
|||
|
|
if (!logined.value) throw new Error("登录失败,请重新进入授权页面");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function loadPatient(autoAuth = false) {
|
|||
|
|
errorMessage.value = "";
|
|||
|
|
patient.value = null;
|
|||
|
|
if (!authId.value) {
|
|||
|
|
errorMessage.value = "授权链接无效";
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
try {
|
|||
|
|
await ensureLogin();
|
|||
|
|
const { success, data, message } = await getSelfAuthPatient(authId.value);
|
|||
|
|
if (!success || !data) throw new Error(message || "获取就诊人信息失败");
|
|||
|
|
patient.value = data;
|
|||
|
|
completed.value = data.status === "success";
|
|||
|
|
if (autoAuth) {
|
|||
|
|
authorize()
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
errorMessage.value = error.message || error || "获取就诊人信息失败";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function completeAuthorization(psnToken) {
|
|||
|
|
if (completed.value || submitting.value) return;
|
|||
|
|
if (typeof psnToken !== "string" || !psnToken.trim()) {
|
|||
|
|
toast("获取医保授权信息失败");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
submitting.value = true;
|
|||
|
|
try {
|
|||
|
|
const { success, message } = await submitSelfAuth(authId.value, psnToken.trim());
|
|||
|
|
if (!success) throw new Error(message || "医保建档失败");
|
|||
|
|
completed.value = true;
|
|||
|
|
remove("self-auth-action-id");
|
|||
|
|
toast("授权并建档成功");
|
|||
|
|
} catch (error) {
|
|||
|
|
toast(error.message || error || "医保建档失败");
|
|||
|
|
} finally {
|
|||
|
|
submitting.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function authorize() {
|
|||
|
|
if (!patient.value || submitting.value || completed.value) return;
|
|||
|
|
try {
|
|||
|
|
const authCode = await getAuthCode("nhsamp");
|
|||
|
|
const psnToken = await ybPlugin.getArchiveToken(authCode);
|
|||
|
|
if (psnToken) await completeAuthorization(psnToken);
|
|||
|
|
} catch (error) {
|
|||
|
|
toast(error.message || error || "获取医保授权失败");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onLoad(async (options) => {
|
|||
|
|
const optionId = options && typeof options.id === "string" ? options.id : "";
|
|||
|
|
const targetId = optionId || get("self-auth-action-id", "");
|
|||
|
|
if (!parseId(targetId)) {
|
|||
|
|
errorMessage.value = "授权链接无效";
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
set("self-auth-action-id", authId.value, 10 * 60);
|
|||
|
|
ybPlugin.init({
|
|||
|
|
archiveTokenCallback: () => {
|
|||
|
|
uni.redirectTo({
|
|||
|
|
url: `/pages/consult/self-auth/auth-action?id=${optionId}&type=afterAuth`,
|
|||
|
|
})
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
await loadPatient(options.type === 'afterAuth');
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.auth-page {
|
|||
|
|
padding: 48rpx 30rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.title {
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 40rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.description {
|
|||
|
|
margin: 20rpx 0 40rpx;
|
|||
|
|
color: #888;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.patient-card,
|
|||
|
|
.state-card,
|
|||
|
|
.success-card {
|
|||
|
|
padding: 12rpx 30rpx;
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 16rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-row {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
min-height: 92rpx;
|
|||
|
|
border-bottom: 1rpx solid #eee;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-row:last-child {
|
|||
|
|
border-bottom: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.label {
|
|||
|
|
color: #888;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.value {
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.state-card {
|
|||
|
|
padding: 50rpx 30rpx;
|
|||
|
|
color: #888;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.state-card--error {
|
|||
|
|
color: #d9534f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.retry {
|
|||
|
|
margin-top: 28rpx;
|
|||
|
|
color: #8b6538;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.success-card {
|
|||
|
|
margin-top: 30rpx;
|
|||
|
|
padding: 40rpx 30rpx;
|
|||
|
|
color: #666;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.success-title {
|
|||
|
|
margin-bottom: 16rpx;
|
|||
|
|
color: #2e9b58;
|
|||
|
|
font-size: 36rpx;
|
|||
|
|
font-weight: 600;
|
|||
|
|
}
|
|||
|
|
</style>
|