hn-hlw-app/pages/member/family-profile.vue
2026-07-27 11:26:39 +08:00

336 lines
10 KiB
Vue

<template>
<view class="container" :class="{ 'container--elder': elderMode }">
<template v-if="logined">
<view class="main">
<scroll-view scroll-y="true" class="main-scroll">
<view class="card" :class="{ 'card--elder': elderMode }">
<uni-list>
<!-- 信息条目 -->
<uni-list-item direction="column">
<template v-slot:body>
<view class="info-item" :class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">姓名</text>
<text v-if="id" class="item-value" :class="{ 'item-value--elder': elderMode }">
{{ currentProfile ? currentProfile.name : '' }}
</text>
<input v-else v-model="form.name" :maxlength="10" class="item-value"
:class="{ 'item-value--elder': elderMode }" type="text" placeholder="请输入姓名" />
</view>
<view class="info-item" :class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">证件类型</text>
<text class="item-value" :class="{ 'item-value--elder': elderMode }">身份证</text>
</view>
<view class="info-item" :class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">证件号</text>
<text v-if="id" class="item-value" :class="{ 'item-value--elder': elderMode }">
{{ currentProfile ? currentProfile.maskCertNo : '' }}
</text>
<input v-else v-model="form.socialno" :maxlength="20" class="item-value"
:class="{ 'item-value--elder': elderMode }" type="idcard" placeholder="请输入证件号" />
</view>
<view class="info-item" :class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">手机号</text>
<input v-model="form.tel" :maxlength="15" class="item-value"
:class="{ 'item-value--elder': elderMode }" type="number" placeholder="请输入手机号" />
</view>
<view v-if="currentProfile && currentProfile.blhno" class="info-item"
:class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">病案号</text>
<text class="item-value highlight" :class="{ 'item-value--elder': elderMode }">
{{ currentProfile.blhno }}
</text>
</view>
<view class="info-item" :class="{ 'info-item--elder': elderMode }">
<text class="item-label" :class="{ 'item-label--elder': elderMode }">地址</text>
<input v-model="form.address" class="item-value" :class="{ 'item-value--elder': elderMode }"
type="text" placeholder="请输入地址" />
</view>
</template>
</uni-list-item>
</uni-list>
</view>
</scroll-view>
</view>
<view class="save-footer" :class="{ 'save-footer--elder': elderMode }" @click="confirm">
<view class="save-btn" :class="{ 'save-btn--elder': elderMode }">保存</view>
</view>
<view class="safearea-bottom"></view>
</template>
<re-login v-else height="80vh" />
</view>
</template>
<script setup>
import { computed, ref, watch } from 'vue';
import { onLoad } from "@dcloudio/uni-app";
import useUser from '@/hooks/useUser';
import { bindFamilyProfile, updateFamilyProfile, sm4Encrypt } from '@/utils/api';
import { isChinaId } from "@/utils/validator";
import { toast } from '@/utils/widget';
import ybPlugin from "@/utils/insurance-plugin";
import reLogin from '@/components/re-login.vue';
const id = ref('')
const form = ref({
name: '',
socialno: '',
tel: '',
address: '',
});
const loading = ref(false);
const elderMode = ref(false);
const { familyProfile, logined, userInfo, getFamilyProfile, myProfile, getAuthCode } = useUser();
const currentProfile = computed(() => familyProfile.value.find(i => i._id === id.value && id.value));
const source = ref('')
async function confirm() {
if (loading.value) return;
if (form.value.name.trim() === '') {
toast('姓名不能为空');
return;
}
if (form.value.socialno.trim() === '') {
toast('证件号不能为空');
return;
}
if (!isChinaId(form.value.socialno)[0]) {
toast('请输入有效的身份证号');
return;
}
if (form.value.tel.trim() === '') {
toast('手机号不能为空');
return;
}
if (!/^1[3-9]\d{9}$/.test(form.value.tel)) {
toast('请输入有效的手机号');
return;
}
loading.value = true;
try {
const payload = {
id: id.value,
name: form.value.name.trim(),
socialno: form.value.socialno.trim(),
tel: form.value.tel.trim(),
address: form.value.address.trim(),
userId: userInfo.value.userId,
};
uni.setStorageSync('family-profile-form', JSON.stringify(form.value));
const psnToken = await getPsnToken(payload.name, payload.socialno);
if (!psnToken) {
loading.value = false;
return;
}
payload.psnToken = psnToken;
const api = id.value ? updateFamilyProfile : bindFamilyProfile;
const { message, success } = await api(payload);
if (success) {
toast('保存成功')
getFamilyProfile()
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.redirectTo({
url: source.value || '/pages/home/home'
});
}
} else {
toast(message || '保存失败')
}
} catch (e) {
toast(e.message);
}
loading.value = false
}
async function getPsnToken(name, socialno) {
const res = await sm4Encrypt({ textGroup: [name, socialno] })
const [anotherName, anotherIdNo] = res && Array.isArray(res.textGroup) ? res.textGroup : [];
if (!anotherName || !anotherIdNo) {
toast('获取加密参数失败,请稍后再试');
return Promise.reject('获取加密参数失败,请稍后再试');
}
const authCode = await getAuthCode("nhsamp");
const psnToken = await ybPlugin.getArchiveToken(authCode, anotherIdNo, anotherName);
if (!psnToken) {
return Promise.reject('获取医保信息授权信息失败');
}
return psnToken;
}
onLoad((options) => {
if (options.source) {
source.value = decodeURIComponent(options.source);
}
if (options.type === 'getArchiveTokenRedirect') {
const cacheForm = uni.getStorageSync('family-profile-form');
try {
const obj = JSON.parse(cacheForm);
Object.keys(form.value).forEach(key => {
form.value[key] = obj[key] || '';
});
} catch (e) { }
}
uni.removeStorageSync('family-profile-form');
id.value = options.id || '';
ybPlugin.init({
archiveTokenCallback: () => {
uni.redirectTo({
url: `/pages/member/family-profile?type=getArchiveTokenRedirect&id=${id.value}&source=${source.value ? encodeURIComponent(source.value) : ''}`,
});
}
});
// 读取长辈模式状态
const savedElderMode = uni.getStorageSync('elderMode')
if (savedElderMode !== null && savedElderMode !== undefined) {
elderMode.value = savedElderMode
}
});
watch(currentProfile, val => {
if (id.value) {
form.value = {
name: val ? val.name : '',
socialno: val ? val.socialno : '',
tel: val ? val.tel : '',
address: val ? val.address : ''
}
}
}, { immediate: true })
</script>
<style lang="scss">
.container {
display: flex;
flex-direction: column;
background-color: #f5f5f5;
height: 100vh;
// 定义CSS变量
--font-scale: 1;
--spacing-scale: 1;
--border-radius-scale: 1;
--font-weight-scal: 1;
@at-root &--elder {
--font-scale: 1.4;
--spacing-scale: 1.2;
--border-radius-scale: 1.25;
--font-weight-scal: 1.2;
}
}
.main {
position: relative;
flex-grow: 1;
}
.main-scroll {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.card {
margin: calc(30rpx * var(--spacing-scale));
border-radius: calc(16rpx * var(--border-radius-scale));
overflow: hidden;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.info-item {
background: #fff;
display: flex;
justify-content: space-between;
align-items: center;
padding: calc(24rpx * var(--spacing-scale)) calc(30rpx * var(--spacing-scale));
border-bottom: 1rpx solid #eee;
min-height: calc(80rpx * var(--spacing-scale));
&:last-child {
border-bottom: none;
}
.item-label {
color: #666;
font-size: calc(30rpx * var(--font-scale));
width: calc(200rpx * var(--spacing-scale));
font-weight: 500;
.container--elder & {
font-weight: calc(600rpx * var(--font-size-scal));
}
}
.item-value {
color: #333;
font-size: calc(30rpx * var(--font-scale));
font-weight: 500;
flex: 1;
text-align: right;
&.highlight {
color: $theme-brown-primary;
font-weight: 600;
}
.container--elder & {
font-weight: 600;
}
}
}
.save-footer {
padding: calc(24rpx * var(--spacing-scale)) calc(30rpx * var(--spacing-scale));
background: white;
}
.save-btn {
height: calc(80rpx * var(--spacing-scale));
background: $theme-brown-primary;
color: white;
font-size: calc(28rpx * var(--font-scale));
border-radius: calc(16rpx * var(--border-radius-scale));
text-align: center;
line-height: calc(80rpx * var(--spacing-scale));
font-weight: 500;
.container--elder & {
font-weight: 600;
}
}
/* 交互效果 */
uni-list-item {
transition: all 0.2s;
&:active {
background-color: #f8f8f8;
}
}
.safearea-bottom {
height: env(safe-area-inset-bottom);
flex-shrink: 0;
}
.hint {
padding: 20rpx 30rpx 40rpx;
color: red;
font-size: 28rpx;
line-height: 50rpx;
}
.hint-large {
font-size: 40rpx;
line-height: 70rpx;
}
</style>