尚医通是一个网上预约挂号系统,包含后台管理系统和前台用户系统,采用前后端分离开发模式。
尚医通是采用Vue3全家桶、TypeScript、Vite、Pinia、Element-plus等技术栈开发的在线医疗服务平台,集成了多家医院的挂号信息,提供全程跟踪服务,用户可以随时了解自己的挂号状态。
前端整体功能,已经学习完了,功能均已实现,在这里记录一下技术重点.....
十八、预约挂号
18.1 小错误
错误原因:workData刚开始有可能是空对象,所以{}.hosname 肯定报错undefined
修改:
加个?变成可选参数
18.1 页面展示
点击某个科室进入
接口:
接口类型
渲染页面
切换分页获取数据
完整代码
<template>
<div class="wrap">
<!-- 顶部结构 -->
<div class="top">
<div class="hosname">{{ workData.baseMap?.hosname }}</div>
<div class="line">|</div>
<div>{{ workData.baseMap?.bigname }}</div>
<div class="dot">.</div>
<div class="hosdeparment">{{ workData.baseMap?.depname }}</div>
</div>
<div class="center">
<h1 class="time">{{ workData.baseMap?.workDateString }}</h1>
<div class="container">
<div
class="item"
:class="{ active: item.status == -1 || item.availableNumber == -1 }"
v-for="item in workData.bookingScheduleList"
:key="item"
>
<div class="top2">{{ item.workDate }}-{{ item.dayOfWeek }}</div>
<div class="bottom">
<div v-if="item.status == -1">停止挂号</div>
<div v-if="item.status == 0">
{{
item.availableNumber == -1
? `约满了`
: `有号(${item.availableNumber})`
}}
</div>
<div v-if="item.status == 1">即将放号</div>
</div>
</div>
</div>
<el-pagination
v-model:current-page="pageNo"
layout="prev, pager, next"
:total="workData.total"
@current-change="fetchWorkData"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
import { reqHospitalWork } from "@/api/hospital";
import type { HospitalWorkData } from "@/api/hospital/type";
import { useRoute } from "vue-router";
// 获取路由对象
let $route = useRoute();
// 分页器当前页码
let pageNo = ref<number>(1);
// 每一页展示几条数据
let limit = ref<number>(6);
// 存储医院科室挂号的数据
let workData = ref<any>({});
// 组件挂载完毕发一次请求
onMounted(() => {
fetchWorkData();
});
// 获取挂号的数据
const fetchWorkData = async () => {
let result: HospitalWorkData = await reqHospitalWork(
pageNo.value,
limit.value,
$route.query.hoscode as string,
$route.query.depcode as string
);
console.log(result);
if (result.code == 200) {
workData.value = result.data;
}
};
</script>
<style scoped lang="scss">
.wrap {
.top {
display: flex;
color: #7f7f7f;
.line {
width: 5px;
height: 20px;
background: skyblue;
margin: 0px 5px;
}
.dot {
margin: 0px 5px;
color: skyblue;
}
}
.center {
margin: 20px 0px;
display: flex;
flex-direction: column;
align-items: center;
.time {
font-weight: 900;
}
.container {
width: 100%;
display: flex;
margin: 30px 0px;
.item {
flex: 1;
width: 100%;
border: 1px solid skyblue;
margin: 0px 5px;
&.active {
border: 1px solid #cccc;
color: #7f7f7f;
.top2 {
background: #ccc;
}
}
.top2 {
background: #e8f2ff;
/* margin: 5px 0px; */
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
}
.bottom {
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
}
}
}
}
}
</style>
18.2 预约挂号底部医生排班业务
ts数据类型
获取医生信息的逻辑
展示数据
完整代码:
<template>
<div class="wrap">
<!-- 顶部结构 -->
<div class="top">
<div class="hosname">{{ workData.baseMap?.hosname }}</div>
<div class="line">|</div>
<div>{{ workData.baseMap?.bigname }}</div>
<div class="dot">.</div>
<div class="hosdeparment">{{ workData.baseMap?.depname }}</div>
</div>
<!-- 中间展示日期的结构 -->
<div class="center">
<h1 class="time">{{ workData.baseMap?.workDateString }}</h1>
<div class="container">
<div
class="item"
:class="{
active: item.status == -1 || item.availableNumber == -1,
cur: item.workDate == workTime.workDate,
}"
v-for="item in workData.bookingScheduleList"
:key="item"
@click="changeTime(item)"
>
<div class="top2">{{ item.workDate }}-{{ item.dayOfWeek }}</div>
<div class="bottom">
<div v-if="item.status == -1">停止挂号</div>
<div v-if="item.status == 0">
{{
item.availableNumber == -1
? `约满了`
: `有号(${item.availableNumber})`
}}
</div>
<div v-if="item.status == 1">即将放号</div>
</div>
</div>
</div>
<el-pagination
v-model:current-page="pageNo"
layout="prev, pager, next"
:total="workData.total"
@current-change="fetchWorkData"
/>
</div>
<!-- 底部展示医生的结构 -->
<div class="bottom1">
<!-- 展示即将放号的时间 -->
<div class="will" v-if="workTime.status == 1">
<span class="time">2023年{{ workTime.workDateMd }}</span>
<span class="willtext">放号</span>
</div>
<!-- 展示医生的结构 :上午、下午-->
<div class="doctor" v-else>
<div class="morning">
<!-- 顶部文字提示 -->
<div class="tip1">
<svg
t="1689336807398"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2545"
width="32"
height="32"
>
<path
d="M304.5 691.8c0-145.3 117.8-263 263-263s263 117.8 263 263"
fill="#FDDA09"
p-id="2546"
></path>
<path
d="M567.5 428.8c-16.7 0-32.9 1.7-48.7 4.7 122 22.9 214.3 129.7 214.3 258.3h97.4c0.1-145.2-117.7-263-263-263z"
fill="#FDA906"
p-id="2547"
></path>
<path
d="M772.1 687.3c-8.3 0-15-6.7-15-15 0-66.2-25.8-128.5-72.6-175.4-46.8-46.8-109.1-72.6-175.4-72.6-66.2 0-128.5 25.8-175.4 72.6-46.8 46.8-72.6 109.1-72.6 175.4 0 8.3-6.7 15-15 15s-15-6.7-15-15c0-74.3 28.9-144.1 81.4-196.6 52.5-52.5 122.3-81.4 196.6-81.4s144.1 28.9 196.6 81.4c52.5 52.5 81.4 122.3 81.4 196.6 0 8.3-6.7 15-15 15z"
fill=""
p-id="2548"
></path>
<path
d="M914.1 704.5H120c-8.3 0-15-6.7-15-15s6.7-15 15-15h794.1c8.3 0 15 6.7 15 15s-6.7 15-15 15zM755.2 777.4H278.8c-8.3 0-15-6.7-15-15s6.7-15 15-15h476.5c8.3 0 15 6.7 15 15s-6.8 15-15.1 15zM605.6 858.1H428.5c-8.3 0-15-6.7-15-15s6.7-15 15-15h177.1c8.3 0 15 6.7 15 15s-6.8 15-15 15zM179.4 641h-63.5c-8.3 0-15-6.7-15-15s6.7-15 15-15h63.5c8.3 0 15 6.7 15 15s-6.7 15-15 15zM515.9 323.4c-8.3 0-15-6.7-15-15V181.3c0-8.3 6.7-15 15-15s15 6.7 15 15v127.1c0 8.3-6.7 15-15 15zM271 422.9c-3.8 0-7.7-1.5-10.6-4.4l-56.2-56.2c-5.9-5.9-5.9-15.4 0-21.2 5.9-5.9 15.4-5.9 21.2 0l56.2 56.2c5.9 5.9 5.9 15.4 0 21.2-2.9 3-6.8 4.4-10.6 4.4zM754.8 422.9c-3.8 0-7.7-1.5-10.6-4.4-5.9-5.9-5.9-15.4 0-21.2l56.2-56.2c5.9-5.9 15.4-5.9 21.2 0 5.9 5.9 5.9 15.4 0 21.2l-56.2 56.2c-2.9 3-6.7 4.4-10.6 4.4z"
fill=""
p-id="2549"
></path>
<path
d="M441.2 544.2a18.8 17.8 0 1 0 37.6 0 18.8 17.8 0 1 0-37.6 0Z"
fill="#050400"
p-id="2550"
></path>
<path
d="M553.9 544.2a18.8 17.8 0 1 0 37.6 0 18.8 17.8 0 1 0-37.6 0Z"
fill="#050400"
p-id="2551"
></path>
<path
d="M491.3 577.5c0 13.1 11.2 23.7 25 23.7s25-10.6 25-23.7h-50z"
fill="#050400"
p-id="2552"
></path>
<path
d="M406.9 588.6a21.9 20.7 0 1 0 43.8 0 21.9 20.7 0 1 0-43.8 0Z"
fill="#FDA906"
p-id="2553"
></path>
<path
d="M582.1 588.6a21.9 20.7 0 1 0 43.8 0 21.9 20.7 0 1 0-43.8 0Z"
fill="#FDA906"
p-id="2554"
></path>
</svg>
<span class="text">上午号源</span>
</div>
<!-- 每一个医生的信息 -->
<div class="doc_info" v-for="doctor in morningArr" :key="doctor.id">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>{{ doctor.title }}</span>
<span>|</span>
<span>{{ doctor.docname }}</span>
</div>
<div class="skill">{{ doctor.skill }}</div>
</div>
<!-- 右侧展示挂号的钱数 -->
<div class="right">
<div class="money">¥{{ doctor.amount }}元</div>
<el-button type="primary">{{ doctor.availableNumber }}</el-button>
</div>
</div>
</div>
<div class="morning">
<!-- 顶部文字提示 -->
<div class="tip1">
<svg
t="1689338507838"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="5411"
width="32"
height="32"
>
<path
d="M369.86 594.04c45.34-78.5 145.73-105.38 224.23-60.04a164.171 164.171 0 0 1 60.04 60.04H369.86z m306.23 82.05z m-492.26 0h656.35c22.66 0 41.02 18.36 41.02 41.02 0 22.66-18.36 41.02-41.02 41.02H183.83c-22.66 0-41.02-18.36-41.02-41.02-0.01-22.66 18.36-41.02 41.02-41.02z"
fill="#1296db"
p-id="5412"
></path>
<path
d="M678.58 383.66l43.51-43.51c24.03-24.03 62.99-24.03 87.02 0 24.03 24.03 24.03 62.99 0 87.02l-43.51 43.51c-24.14 23.92-63.1 23.74-87.02-0.39-23.77-23.99-23.77-62.65 0-86.63zM512 204.34c33.98 0 61.53 27.55 61.53 61.53v61.53c-0.14 33.98-27.8 61.42-61.78 61.28-33.79-0.14-61.15-27.49-61.28-61.28v-61.53c0-33.98 27.55-61.53 61.53-61.53zM217.08 340.15c24.03-24.03 62.99-24.03 87.02 0l43.51 43.51c24.03 24.03 24.03 62.99 0 87.02-24.03 24.03-62.99 24.03-87.02 0l-43.51-43.51c-24.03-24.03-24.03-62.99 0-87.02z"
fill="#1296db"
opacity=".3"
p-id="5413"
></path>
</svg>
<span class="text">下午号源</span>
</div>
<!-- 每一个医生的信息 -->
<div class="doc_info" v-for="doctor in afterArr" :key="doctor.id">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>{{ doctor.title }}</span>
<span>|</span>
<span>{{ doctor.docname }}</span>
</div>
<div class="skill">{{ doctor.skill }}</div>
</div>
<!-- 右侧展示挂号的钱数 -->
<div class="right">
<div class="money">¥{{ doctor.amount }}元</div>
<el-button type="primary">{{ doctor.availableNumber }}</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
import { reqHospitalWork, reqHospitalDoctor } from "@/api/hospital";
import type {
HospitalWorkData,
DoctorResponseData,
DocArr,
Doctor,
} from "@/api/hospital/type";
import { useRoute } from "vue-router";
// 获取路由对象
let $route = useRoute();
// 分页器当前页码
let pageNo = ref<number>(1);
// 每一页展示几条数据
let limit = ref<number>(6);
// 存储医院科室挂号的数据
let workData = ref<any>({});
// 存储当前排班日期:当前数据的第一个
let workTime: any = ref({});
// 存储排班医生的数据
let docArr = ref<DocArr>([]);
// 组件挂载完毕发一次请求
onMounted(() => {
fetchWorkData();
});
// 获取挂号的数据
const fetchWorkData = async () => {
let result: HospitalWorkData = await reqHospitalWork(
pageNo.value,
limit.value,
$route.query.hoscode as string,
$route.query.depcode as string
);
// console.log(result);
if (result.code == 200) {
workData.value = result.data;
// 存储第一天日期的数据
// workTime.value=workData.bookingScheduleList[0]
workTime.value = workData.value.bookingScheduleList[0];
// 获取一次医生的数据
getDoctorWorkData();
}
};
// 获取医生排班信息
const getDoctorWorkData = async () => {
// 医院的编号
let hoscode: string = $route.query.hoscode as string;
// 科室的编号
let depcode: string = $route.query.depcode as string;
// 时间
let workDate: string = workTime.value.workDate;
// 获取排班医生的数据
let result: DoctorResponseData = await reqHospitalDoctor(
hoscode,
depcode,
workDate
);
// console.log(result);
if (result.code == 200) {
docArr.value = result.data;
}
};
// 点击顶部某一天的时候触发回调
const changeTime = (item: any) => {
// console.log(item);
// 存储用户选择那一天的数据
workTime.value = item;
// 再发一次获取医生的排班的数据
getDoctorWorkData();
};
// 计算出上午排班的医生数据
let morningArr = computed(() => {
return docArr.value.filter((doc: Doctor) => {
return doc.workTime == 0;
});
});
// 计算出下午排班的医生数据
let afterArr = computed(() => {
return docArr.value.filter((doc: Doctor) => {
return doc.workTime == 1;
});
});
</script>
<style scoped lang="scss">
.wrap {
.top {
display: flex;
color: #7f7f7f;
.line {
width: 5px;
height: 20px;
background: skyblue;
margin: 0px 5px;
}
.dot {
margin: 0px 5px;
color: skyblue;
}
}
.center {
margin: 20px 0px;
display: flex;
flex-direction: column;
align-items: center;
transition: all 0.5s;
.time {
font-weight: 900;
}
.container {
width: 100%;
display: flex;
margin: 30px 0px;
.item {
flex: 1;
width: 100%;
border: 1px solid skyblue;
margin: 0px 5px;
&.active {
border: 1px solid #cccc;
color: #7f7f7f;
.top2 {
background: #ccc;
}
}
&.cur {
transform: scale(1.1);
}
.top2 {
background: #e8f2ff;
/* margin: 5px 0px; */
width: 100%;
height: 30px;
text-align: center;
line-height: 30px;
}
.bottom {
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
}
}
}
}
.bottom1 {
.will {
text-align: center;
font-size: 30px;
font-weight: 900;
.time {
color: red;
}
.willtext {
color: skyblue;
}
}
.doctor {
.morning {
.tip1 {
display: flex;
align-items: center;
.text {
color: #7f7f7f;
font-weight: 900;
}
}
.doc_info {
display: flex;
justify-content: space-between;
margin: 10px 0px;
border-bottom: 1px solid #ccc;
.left {
.info {
color: skyblue;
margin: 10px 0px;
span {
margin: 0px 5px;
font-size: 18px;
font-weight: 900;
}
}
.skill {
margin: 10px 0px;
color: #7f7f7f;
}
}
.right {
display: flex;
width: 150px;
justify-content: space-between;
align-items: center;
.money {
color: #7f7f7f;
font-weight: 900;
}
}
}
}
}
}
}
</style>
18.3、确认挂号信息
静态页面搭建
引入路由
创建文件
点击跳转到这个页面
使用路由
点击跳转,记得带上医生id
register_step2.vue
<template>
<div class="container">
<h1 class="tip">确认挂号信息</h1>
<!-- 卡片:展示就诊人信息 -->
<el-card class="box-card">
<!-- 卡片的头部 -->
<template #header>
<div class="card-header">
<span>请选择就诊人</span>
<el-button class="button" type="primary" size="default" :icon="User"
>添加就诊人</el-button
>
</div>
</template>
<!-- 卡片的身体部分展示就诊人信息 -->
<div class="user">
<!-- :user="user" :index="index" :currenIndex="index" 父组件传数据给子组件 -->
<Visitor
v-for="(user, index) in userArr"
:key="user.id"
class="item"
:user="user"
@click="changeIndex(index)"
:index="index"
:currentIndex="currentIndex"
/>
</div>
</el-card>
<!-- 底部展示医生的信息 -->
<el-card class="box-card">
<!-- 卡片头部 -->
<template #header>
<div class="card-header">
<span>挂号信息</span>
</div>
</template>
<!-- 卡片的身体部分:展示医生的信息-->
<el-descriptions class="margin-top" :column="2" border>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊日期:</div>
</template>
{{ docInfo.workDate }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊医院:</div>
</template>
{{ docInfo.param?.hosname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊科室:</div>
</template>
{{ docInfo.param?.depname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生姓名:</div>
</template>
{{ docInfo?.docname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生职称:</div>
</template>
{{ docInfo.title }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生专长:</div>
</template>
{{ docInfo.skill }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生服务费:</div>
</template>
<span style="color: red">{{ docInfo.amount }}</span>
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 确定挂号按钮 -->
<div class="btn">
<el-button
type="primary"
size="default"
:disabled="currentIndex == -1 ? true : false"
>确定挂号</el-button
>
</div>
</div>
</template>
<script setup lang="ts">
import { User } from "@element-plus/icons-vue";
// 引入获取就诊人的信息接口
import { reqGetUser, reqDoctorInfo } from "@/api/hospital";
import { onMounted, ref } from "vue";
import { useRoute } from "vue-router";
import type {
UserResponseData,
UserArr,
DoctorInfoData,
} from "@/api/hospital/type";
// 就诊人组件
import Visitor from "./visitor.vue";
// 获取路由对象
let $route = useRoute();
// 存储全部就诊人信息
let userArr = ref<UserArr>([]);
// 存储医生的信息
let docInfo = ref<any>({});
// 存储用户确认就诊人索引值
let currentIndex = ref<number>(-1);
// 组件挂载完毕获取数据
onMounted(() => {
// 获取就诊人信息
fetchUserData();
// 获取医生信息
fetchInfo();
});
// 获取就诊人信息
const fetchUserData = async () => {
let result: UserResponseData = await reqGetUser();
// console.log(result);
if (result.code == 200) {
userArr.value = result.data;
}
};
// 获取医生信息
const fetchInfo = async () => {
let result: DoctorInfoData = await reqDoctorInfo(
$route.query.docId as string
);
if (result.code == 200) {
docInfo.value = result.data;
}
};
// 点击就诊人子组件的回调
const changeIndex = (index: number) => {
// console.log(index);
// 存储当前用户选中就诊人信息索引值
currentIndex.value = index;
};
</script>
<style scoped lang="scss">
.container {
.tip {
font-weight: 900;
color: #7f7f7f;
font-size: 20px;
}
.box-card {
margin: 20px 0px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.user {
display: flex;
flex-wrap: wrap;
.item {
width: 32%;
margin: 5px;
}
}
}
.btn {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
}
</style>
visitor.vue
<template>
<div class="visitor">
<div class="top">
<div class="left">
<span class="free">{{ user.isInsure == 1 ? "医保" : "自费" }}</span>
<span class="username">{{ user.name }}</span>
</div>
<div class="right">
<el-button circle type="primary" :icon="Edit"></el-button>
</div>
</div>
<div class="bottom">
<p>证件类型:{{ user.param.certificatesTypeString }}</p>
<p>证件号码:{{ user.certificatesNo }}</p>
<p>用户性别:{{ user.sex == 0 ? "女生" : "男生" }}</p>
<p>出生日期:{{ user.birthdate }}</p>
<p>手机号码:{{ user.phone }}</p>
<p>婚姻状况:{{ user.isMarry == 1 ? "已婚" : "未婚" }}</p>
<p>当前住址:{{ user.param.cityString }}</p>
<p>详细住址:{{ user.paramfullAddress }}</p>
<!-- 红色已选择的盒子 -->
<transition name="confirm">
<div class="confirm" v-if="index === currentIndex">已选择</div>
</transition>
</div>
</div>
</template>
<script setup lang="ts">
import { Edit } from "@element-plus/icons-vue";
// 接受父组件传递过来的就诊人信息
defineProps(["user", "index", "currentIndex"]);
</script>
<style scoped lang="scss">
.visitor {
box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
.top {
height: 60px;
background: #e5e5e5;
display: flex;
justify-content: space-around;
align-items: center;
.left {
.free {
background: white;
padding: 5px;
font-size: 12px;
margin-right: 5px;
border-radius: 10px;
}
.username {
color: #7f7f7f;
}
}
}
.bottom {
position: relative;
padding-left: 50px;
p {
line-height: 40px;
}
.confirm {
position: absolute;
width: 200px;
height: 200px;
color: red;
border-radius: 50%;
border: 1px dashed red;
text-align: center;
line-height: 200px;
left: 20%;
top: 20%;
opacity: 0.5;
transform: rotate(35deg);
font-weight: 900;
}
.confirm-enter-from {
transform: scale(1);
}
.confirm-enter-active {
transition: all 0.3s;
}
.confirm-enter-to {
transform: scale(1.3);
}
}
}
</style>
18.4、医生挂号信息展示
接口
展示医生信息
18.5、已选择图的完成
利用子绝父相的方法
18.6、确定就诊人业务
当选择有东西时,才可以确定挂号
十九、会员中心
19.1小错误
里面应该用单引号,不能用双引号
19.2 会员中心路由跳转
19.3 确认挂号(接口似乎挂了,一直数据异常)
接口
确定挂号
完整代码
<template>
<div class="container">
<h1 class="tip">确认挂号信息</h1>
<!-- 卡片:展示就诊人信息 -->
<el-card class="box-card">
<!-- 卡片的头部 -->
<template #header>
<div class="card-header">
<span>请选择就诊人</span>
<el-button class="button" type="primary" size="default" :icon="User"
>添加就诊人</el-button
>
</div>
</template>
<!-- 卡片的身体部分展示就诊人信息 -->
<div class="user">
<!-- :user="user" :index="index" :currenIndex="index" 父组件传数据给子组件 -->
<Visitor
v-for="(user, index) in userArr"
:key="user.id"
class="item"
:user="user"
@click="changeIndex(index)"
:index="index"
:currentIndex="currentIndex"
/>
</div>
</el-card>
<!-- 底部展示医生的信息 -->
<el-card class="box-card">
<!-- 卡片头部 -->
<template #header>
<div class="card-header">
<span>挂号信息</span>
</div>
</template>
<!-- 卡片的身体部分:展示医生的信息-->
<el-descriptions class="margin-top" :column="2" border>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊日期:</div>
</template>
{{ docInfo.workDate }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊医院:</div>
</template>
{{ docInfo.param?.hosname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">就诊科室:</div>
</template>
{{ docInfo.param?.depname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生姓名:</div>
</template>
{{ docInfo?.docname }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生职称:</div>
</template>
{{ docInfo.title }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生专长:</div>
</template>
{{ docInfo.skill }}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">医生服务费:</div>
</template>
<span style="color: red">{{ docInfo.amount }}</span>
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 确定挂号按钮 -->
<div class="btn">
<el-button
type="primary"
size="default"
:disabled="currentIndex == -1 ? true : false"
@click="submitOrder"
>确定挂号</el-button
>
</div>
</div>
</template>
<script setup lang="ts">
import { User } from "@element-plus/icons-vue";
// 引入获取就诊人的信息接口
import { reqGetUser, reqDoctorInfo } from "@/api/hospital";
import { reqSubmitOrder } from "@/api/user";
import type { SubmitOrder } from "@/api/user/type";
import { onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import type {
UserResponseData,
UserArr,
DoctorInfoData,
} from "@/api/hospital/type";
import { ElMessage } from "element-plus";
// 就诊人组件
import Visitor from "./visitor.vue";
// 获取路由对象
let $route = useRoute();
// 获取路由器对象
let $router = useRouter();
// 存储全部就诊人信息
let userArr = ref<UserArr>([]);
// 存储医生的信息
let docInfo = ref<any>({});
// 存储用户确认就诊人索引值
let currentIndex = ref<number>(-1);
// 组件挂载完毕获取数据
onMounted(() => {
// 获取就诊人信息
fetchUserData();
// 获取医生信息
fetchInfo();
});
// 获取就诊人信息
const fetchUserData = async () => {
let result: UserResponseData = await reqGetUser();
// console.log(result);
if (result.code == 200) {
userArr.value = result.data;
}
};
// 获取医生信息
const fetchInfo = async () => {
let result: DoctorInfoData = await reqDoctorInfo(
$route.query.docId as string
);
if (result.code == 200) {
docInfo.value = result.data;
}
};
// 点击就诊人子组件的回调
const changeIndex = (index: number) => {
// console.log(index);
// 存储当前用户选中就诊人信息索引值
currentIndex.value = index;
};
//确定挂号按钮的回调
const submitOrder = async () => {
//医院编号
let hoscode = docInfo.value.hoscode;
//医生的ID
let scheduleId = docInfo.value.id;
//就诊人的ID
let patientId = userArr.value[currentIndex.value].id;
//提交订单
let result: SubmitOrder = await reqSubmitOrder(
hoscode,
scheduleId,
patientId
);
// console.log(result);
//提交订单成功
if (result.code == 200) {
$router.push({ path: "/user/order", query: { orderId: result.data } });
} else {
ElMessage({
type: "error",
message: result.message,
});
}
};
</script>
<style scoped lang="scss">
.container {
.tip {
font-weight: 900;
color: #7f7f7f;
font-size: 20px;
}
.box-card {
margin: 20px 0px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.user {
display: flex;
flex-wrap: wrap;
.item {
width: 32%;
margin: 5px;
}
}
}
.btn {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
}
</style>
19.4 订单详情
接口
19.5 取消预约
取消预约
19.6 支付二维码静态搭建
19.7 支付二维码获取
将字符串变为二维码插件---npm官网找qrcode插件
获取二维码
动态绑定图片
19.8 订单支付业务
接口
当二维码出现时,每隔两秒都要向服务器询问是否支付成功,当支付成功后,还要将定时器清除
清除定时器(窗口和右上角的叉子)
二十、实名认证静态搭建
完整代码
<template>
<!-- 实名认证结构 -->
<el-card class="box-card">
<!-- 卡片的头部 -->
<template #header>
<div class="card-header">
<h1>实名信息</h1>
</div>
</template>
<!-- 实名认证结构的提示部分 -->
<div class="tip" style="color: #7f7f7f">
<p>
<el-icon><InfoFilled /></el-icon>
完成实名认证后才能添加就诊人,正常进行挂号,为了不影响后续步骤,建议提前实名认证。
</p>
</div>
<!-- 卡片身体的底部:认证成功的结构、认证未成功的结构 -->
<el-descriptions
v-if="true"
class="margin-top"
:column="1"
border
style="margin: 20px auto"
size="small"
>
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">用户姓名</div>
</template>
kooriookami
</el-descriptions-item>
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">证件类型</div>
</template>
kooriookami
</el-descriptions-item>
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">证件号码</div>
</template>
kooriookami
</el-descriptions-item>
</el-descriptions>
<!-- 用户未认证的结构 -->
<el-form style="width: 60%; margin: 20px auto" label-width="80">
<el-form-item label="用户姓名">
<el-input placeholder="请输入用户的姓名"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select placeholder="请选择证件类型" style="width: 100%">
<el-option label="身份证"></el-option>
<el-option label="户口本"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号码">
<el-input placeholder="请输入证件号码"></el-input>
</el-form-item>
<el-form-item label="上传证件">
<el-upload list-type="picture-card">
<img
src="../../../assets/images/auth_example.png"
alt=""
style="width: 100%; height: 80%"
/>
</el-upload>
<el-dialog>
<img w-full alt="Preview Image" />
</el-dialog>
</el-form-item>
<el-form-item>
<el-button type="primary"> 提交</el-button>
<el-button> 重写</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script setup lang="ts">
import { InfoFilled } from "@element-plus/icons-vue";
</script>
<style scoped lang="scss">
.box-card {
.tip {
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
20.1 获取用户模块
接口
20.2 获取表单数据
上传图片功能
小错误
原因:没有判断是否有图片,也可能上传的是空数组
功能实现:
重写:
提交:
20.3 实名认证表单自定义校验
小bug:上传图片成功了,但是校验规则还在
解决:用了element plus自带的方法
自定义表单校验
校验函数;
// 自定义校验姓名规则
const validatorName = (rule: any, value: any, callBack: any) => {
// rule即为当前校验字段的校验规则对象 value即为当前校验字段的校验数据data
// console.log(rule);
// console.log(value);
const reg =
/^[\u00B7\u3007\u3400-\u4DBF\u4E00-\u9FFF\uE000-\uF8FF\uD840-\uD8C0\uDC00-\uDFFF\uF900-\uFAFF]+$/;
if (reg.test(value)) {
callBack();
} else {
callBack(new Error("请输入正确的中国人的名字"));
}
};
// 证件类型校验方法
const validatorType = (rule: any, value: any, callBack: any) => {
// console.log(value);
if (value == "10" || value == "20") {
callBack();
} else {
callBack(new Error("请选择证件类型"));
}
};
// 身份证号码
const validatorNo = (rule: any, value: any, callBack: any) => {
// console.log(111);
const reg =
/^([1-6][1-9]|50)\d{4}(18|19|20)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const hkb = /^\d{9}$/;
if (reg.test(value) || hkb.test(value)) {
callBack();
} else {
callBack(new Error("请输入正确的身份证号码或者户口本号码"));
}
};
// 证件照图片
const validatorUrl = (rule: any, value: any, callBack: any) => {
if (value.length) {
callBack();
} else {
callBack(new Error("请上传证件照"));
}
};