<template>
|
<view class="add-patient">
|
<!-- 基本信息表单 -->
|
<view class="form-card">
|
<view class="section-title">基本信息</view>
|
<view class="form-item">
|
<text class="label required">姓名</text>
|
<input
|
type="text"
|
v-model="form.name"
|
placeholder="请输入就诊人姓名"
|
maxlength="20"
|
/>
|
</view>
|
|
<view class="form-item">
|
<text class="label required">证件类型</text>
|
<picker
|
mode="selector"
|
:range="idTypeLabels"
|
:value="idTypeIndex"
|
@change="onIdTypeChange"
|
>
|
<view class="picker">
|
<text>{{ currentIdType }}</text>
|
<text class="iconfont icon-arrow-right"></text>
|
</view>
|
</picker>
|
</view>
|
|
<view class="form-item">
|
<text class="label required">证件号码</text>
|
<input
|
type="idcard"
|
v-model="form.idNumber"
|
:placeholder="idNumberPlaceholder"
|
:maxlength="idNumberMaxLength"
|
@blur="validateIdNumber"
|
/>
|
<text class="error" v-if="idNumberError">{{ idNumberError }}</text>
|
</view>
|
|
<view class="form-item">
|
<text class="label required">与本人关系</text>
|
<picker
|
mode="selector"
|
:range="relationLabels"
|
:value="relationIndex"
|
@change="onRelationChange"
|
>
|
<view class="picker">
|
<text>{{ currentRelation }}</text>
|
<text class="iconfont icon-arrow-right"></text>
|
</view>
|
</picker>
|
</view>
|
</view>
|
|
<!-- 就诊卡信息 -->
|
<view class="form-card">
|
<view class="section-title">就诊卡信息</view>
|
<view class="card-options">
|
<view
|
class="option-item"
|
:class="{ active: cardOption === 'bind' }"
|
@tap="cardOption = 'bind'"
|
>
|
<text class="radio"></text>
|
<text>绑定已有就诊卡</text>
|
</view>
|
<view
|
class="option-item"
|
:class="{ active: cardOption === 'new' }"
|
@tap="cardOption = 'new'"
|
>
|
<text class="radio"></text>
|
<text>办理新就诊卡</text>
|
</view>
|
</view>
|
|
<!-- 绑定已有就诊卡 -->
|
<block v-if="cardOption === 'bind'">
|
<view class="form-item">
|
<text class="label required">就诊卡号</text>
|
<input
|
type="text"
|
v-model="form.cardNo"
|
placeholder="请输入就诊卡号"
|
maxlength="12"
|
/>
|
</view>
|
<view class="form-item">
|
<text class="label required">手机号码</text>
|
<input
|
type="number"
|
v-model="form.phone"
|
placeholder="请输入办卡时预留的手机号"
|
maxlength="11"
|
/>
|
</view>
|
<view class="form-item verify-code">
|
<text class="label required">验证码</text>
|
<input
|
type="number"
|
v-model="form.verifyCode"
|
placeholder="请输入验证码"
|
maxlength="6"
|
/>
|
<button
|
class="send-btn"
|
:disabled="!!countdown"
|
@tap="sendVerifyCode"
|
>
|
{{ countdown ? `${countdown}s` : '获取验证码' }}
|
</button>
|
</view>
|
</block>
|
|
<!-- 办理新就诊卡 -->
|
<block v-else>
|
<view class="form-item">
|
<text class="label required">手机号码</text>
|
<input
|
type="number"
|
v-model="form.phone"
|
placeholder="请输入手机号码"
|
maxlength="11"
|
/>
|
</view>
|
<view class="notice">
|
<text class="dot"></text>
|
<text>新办就诊卡需要到医院自助机或服务台激活后才能使用</text>
|
</view>
|
</block>
|
</view>
|
|
<!-- 其他设置 -->
|
<view class="form-card">
|
<view class="switch-item">
|
<text>设为默认就诊人</text>
|
<switch
|
:checked="form.isDefault"
|
@change="e => form.isDefault = e.detail.value"
|
color="#0f95b0"
|
/>
|
</view>
|
</view>
|
|
<!-- 底部按钮 -->
|
<view class="bottom-bar">
|
<button
|
class="submit-btn primary-btn"
|
:disabled="!canSubmit"
|
@tap="submitForm"
|
>保存</button>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, computed } from 'vue'
|
|
// 证件类型选项
|
const idTypes = [
|
{ value: 'macauId', label: '青岛居民身份证' },
|
{ value: 'mainlandId', label: '内地居民身份证' },
|
{ value: 'hkId', label: '香港居民身份证' },
|
{ value: 'passport', label: '护照' },
|
{ value: 'other', label: '其他证件' }
|
]
|
|
// 选中的索引
|
const idTypeIndex = ref(0) // 默认选中第一个选项
|
const relationIndex = ref(-1) // -1 表示未选择
|
|
// 证件类型标签列表
|
const idTypeLabels = computed(() => idTypes.map(item => item.label))
|
|
// 关系选项
|
const relations = [
|
{ value: 'self', label: '本人' },
|
{ value: 'parent', label: '父母' },
|
{ value: 'spouse', label: '配偶' },
|
{ value: 'child', label: '子女' },
|
{ value: 'grandparent', label: '祖父母' },
|
{ value: 'sibling', label: '兄弟姐妹' },
|
{ value: 'other', label: '其他' }
|
]
|
|
// 关系标签列表
|
const relationLabels = computed(() => relations.map(item => item.label))
|
|
// 表单数据
|
const form = ref({
|
name: '',
|
idType: 'macauId', // 默认选中青岛居民身份证
|
idNumber: '',
|
relation: '',
|
cardNo: '',
|
phone: '',
|
verifyCode: '',
|
isDefault: false
|
})
|
|
// 就诊卡选项
|
const cardOption = ref('bind')
|
|
// 验证码倒计时
|
const countdown = ref(0)
|
let timer = null
|
|
// 身份证号码验证
|
const idNumberError = ref('')
|
const idNumberMaxLength = computed(() => {
|
const type = idTypes[idTypeIndex.value].value
|
return type === 'mainlandId' ? 18 : 20
|
})
|
const idNumberPlaceholder = computed(() => {
|
const type = idTypes[idTypeIndex.value].value
|
switch(type) {
|
case 'macauId': return '请输入8位青岛居民身份证号码'
|
case 'mainlandId': return '请输入18位内地居民身份证号码'
|
case 'hkId': return '请输入香港居民身份证号码'
|
case 'passport': return '请输入护照号码'
|
default: return '请输入证件号码'
|
}
|
})
|
|
// 证件号码验证规则
|
const idNumberRules = {
|
macauId: {
|
pattern: /^[1-9]\d{7}$/,
|
message: '请输入8位数字的青岛居民身份证号码'
|
},
|
mainlandId: {
|
pattern: /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/,
|
message: '请输入正确的内地居民身份证号码'
|
},
|
hkId: {
|
pattern: /^[A-Z]{1,2}[0-9]{6}\([0-9A]\)$/,
|
message: '请输入正确的香港居民身份证号码'
|
}
|
}
|
|
// 验证身份证号码
|
const validateIdNumber = () => {
|
if (!form.value.idNumber) {
|
idNumberError.value = '请输入证件号码'
|
return false
|
}
|
|
const type = idTypes[idTypeIndex.value].value
|
const rule = idNumberRules[type]
|
|
if (rule) {
|
const reg = rule.pattern
|
if (!reg.test(form.value.idNumber)) {
|
idNumberError.value = rule.message
|
return false
|
}
|
}
|
|
idNumberError.value = ''
|
return true
|
}
|
|
// 验证手机号码
|
const validatePhone = (phone) => {
|
// 青岛手机号规则: 6开头的8位数字
|
const macauMobile = /^6\d{7}$/
|
// 内地手机号规则
|
const mainlandMobile = /^1[3-9]\d{9}$/
|
// 香港手机号规则: 5/6/9开头的8位数字
|
const hkMobile = /^[569]\d{7}$/
|
|
return macauMobile.test(phone) || mainlandMobile.test(phone) || hkMobile.test(phone)
|
}
|
|
// 发送验证码
|
const sendVerifyCode = () => {
|
if (countdown.value > 0) return
|
if (!form.value.phone) {
|
uni.showToast({
|
title: '请输入手机号码',
|
icon: 'none'
|
})
|
return
|
}
|
|
if (!validatePhone(form.value.phone)) {
|
uni.showToast({
|
title: '请输入正确的手机号码',
|
icon: 'none'
|
})
|
return
|
}
|
|
// 这里调用发送验证码API
|
countdown.value = 60
|
timer = setInterval(() => {
|
countdown.value--
|
if (countdown.value <= 0) {
|
clearInterval(timer)
|
timer = null
|
}
|
}, 1000)
|
}
|
|
// 验证就诊卡号
|
const validateCardNo = (cardNo) => {
|
// 青岛镜湖医院就诊卡规则: KW开头加8位数字
|
const kiangWuCard = /^KW\d{8}$/
|
// 科大医院就诊卡规则: MUST开头加6位数字
|
const mustCard = /^MUST\d{6}$/
|
|
return kiangWuCard.test(cardNo) || mustCard.test(cardNo)
|
}
|
|
// 证件类型变更
|
const onIdTypeChange = (e) => {
|
const index = parseInt(e.detail.value)
|
idTypeIndex.value = index
|
form.value.idType = idTypes[index].value
|
form.value.idNumber = ''
|
idNumberError.value = ''
|
}
|
|
// 关系变更
|
const onRelationChange = (e) => {
|
const index = parseInt(e.detail.value)
|
relationIndex.value = index
|
form.value.relation = relations[index].value
|
}
|
|
// 是否可以提交
|
const canSubmit = computed(() => {
|
const { name, idNumber, relation, phone } = form.value
|
if (!name || !idNumber || !relation || !phone) return false
|
if (idNumberError.value) return false
|
if (cardOption.value === 'bind' && !form.value.cardNo) return false
|
return true
|
})
|
|
// 提交表单
|
const submitForm = () => {
|
if (!canSubmit.value) return
|
if (!validateIdNumber()) return
|
|
if (cardOption.value === 'bind') {
|
if (!validateCardNo(form.value.cardNo)) {
|
uni.showToast({
|
title: '请输入正确的就诊卡号',
|
icon: 'none'
|
})
|
return
|
}
|
}
|
|
if (!validatePhone(form.value.phone)) {
|
uni.showToast({
|
title: '请输入正确的手机号码',
|
icon: 'none'
|
})
|
return
|
}
|
|
uni.showLoading({
|
title: '保存中...'
|
})
|
|
// 这里调用保存API
|
console.log('提交表单:', form.value)
|
|
setTimeout(() => {
|
uni.hideLoading()
|
uni.showToast({
|
title: '保存成功',
|
icon: 'success'
|
})
|
setTimeout(() => {
|
uni.navigateBack()
|
}, 1500)
|
}, 1000)
|
}
|
|
// 组件销毁时清除定时器
|
onUnmounted(() => {
|
if (timer) {
|
clearInterval(timer)
|
timer = null
|
}
|
})
|
|
// 当前选中的证件类型
|
const currentIdType = computed(() => {
|
if (idTypeIndex.value >= 0 && idTypes[idTypeIndex.value]) {
|
return idTypes[idTypeIndex.value].label
|
}
|
return '请选择证件类型'
|
})
|
|
// 当前选中的关系
|
const currentRelation = computed(() => {
|
if (relationIndex.value >= 0 && relations[relationIndex.value]) {
|
return relations[relationIndex.value].label
|
}
|
return '请选择关系'
|
})
|
</script>
|
|
<style lang="scss">
|
.add-patient {
|
min-height: 100vh;
|
background: $bg-color;
|
padding: 20rpx 20rpx 120rpx;
|
|
.form-card {
|
background: #fff;
|
border-radius: $radius-lg;
|
padding: 30rpx;
|
margin-bottom: 20rpx;
|
|
.form-item {
|
margin-bottom: 30rpx;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
|
.label {
|
font-size: 28rpx;
|
color: $text-primary;
|
margin-bottom: 16rpx;
|
display: block;
|
|
&.required::before {
|
content: '*';
|
color: $danger;
|
margin-right: 4rpx;
|
}
|
}
|
|
input {
|
width: 100%;
|
height: 88rpx;
|
background: $bg-color;
|
border-radius: $radius-lg;
|
padding: 0 30rpx;
|
font-size: 28rpx;
|
color: $text-primary;
|
}
|
|
.picker {
|
height: 88rpx;
|
background: $bg-color;
|
border-radius: $radius-lg;
|
padding: 0 30rpx;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
text {
|
font-size: 28rpx;
|
color: $text-primary;
|
|
&.icon-arrow-right {
|
font-size: 24rpx;
|
color: $text-secondary;
|
}
|
}
|
}
|
|
.error {
|
font-size: 24rpx;
|
color: $danger;
|
margin-top: 8rpx;
|
display: block;
|
}
|
|
&.verify-code {
|
display: flex;
|
gap: 20rpx;
|
|
input {
|
flex: 1;
|
}
|
|
.send-btn {
|
width: 200rpx;
|
height: 88rpx;
|
line-height: 88rpx;
|
font-size: 26rpx;
|
color: $primary-color;
|
background: $primary-light;
|
border-radius: $radius-lg;
|
|
&[disabled] {
|
opacity: 0.5;
|
}
|
}
|
}
|
}
|
|
.card-options {
|
display: flex;
|
gap: 30rpx;
|
margin-bottom: 30rpx;
|
|
.option-item {
|
flex: 1;
|
height: 88rpx;
|
background: $bg-color;
|
border-radius: $radius-lg;
|
display: flex;
|
align-items: center;
|
padding: 0 30rpx;
|
|
.radio {
|
width: 32rpx;
|
height: 32rpx;
|
border: 2rpx solid $text-secondary;
|
border-radius: 50%;
|
margin-right: 16rpx;
|
position: relative;
|
|
&::after {
|
content: '';
|
position: absolute;
|
left: 50%;
|
top: 50%;
|
transform: translate(-50%, -50%);
|
width: 16rpx;
|
height: 16rpx;
|
background: $primary-color;
|
border-radius: 50%;
|
opacity: 0;
|
transition: all 0.3s;
|
}
|
}
|
|
text {
|
font-size: 28rpx;
|
color: $text-regular;
|
}
|
|
&.active {
|
background: $primary-light;
|
|
.radio {
|
border-color: $primary-color;
|
|
&::after {
|
opacity: 1;
|
}
|
}
|
|
text {
|
color: $primary-color;
|
}
|
}
|
}
|
}
|
|
.notice {
|
display: flex;
|
align-items: flex-start;
|
margin-top: 20rpx;
|
|
.dot {
|
width: 12rpx;
|
height: 12rpx;
|
background: $warning;
|
border-radius: 50%;
|
margin-top: 8rpx;
|
margin-right: 12rpx;
|
flex-shrink: 0;
|
}
|
|
text {
|
flex: 1;
|
font-size: 24rpx;
|
color: $warning;
|
line-height: 1.6;
|
}
|
}
|
|
.switch-item {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
|
text {
|
font-size: 28rpx;
|
color: $text-primary;
|
}
|
}
|
}
|
|
.bottom-bar {
|
position: fixed;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
padding: 20rpx 30rpx;
|
background: #fff;
|
box-shadow: $shadow-lg;
|
|
.submit-btn {
|
width: 100%;
|
|
&[disabled] {
|
opacity: 0.6;
|
}
|
}
|
}
|
}
|
</style>
|