Vite
https://cn.vitejs.dev/guide/
创建项目
$ npm create vite@latest
选择VUE,选择TypeScript
指定默认路径
在文件vite.config.ts中配置
//引入node提供内置模块path:可以获取绝对路径
import path from "path";
export default defineConfig({
plugins: [vue()],
resolve:{
alias:{
"@":path.resolve(__dirname,'src')
}
}
})
在tsconfig.app.json文件中配置
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
清除默认样式
https://www.npmjs.com/package/reset-css?activeTab=code在该地址中找到reset.css
并将该内容复制到项目中(style.reset.scss)
/* http://meyerweb.com/eric/tools/css/reset/
v5.0.1 | 20191019
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
display: none;
}
body {
line-height: 1;
}
menu, ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
将复制后的内容引入的main.ts中
import '@/style/reset.scss'
创建组件
在main.ts中创建组件
import { createApp } from 'vue'
import App from '@/App.vue';
import '@/style/reset.scss'
const app = createApp(App);
//挂载
app.mount("#app");
拆分组件
删除components下的默认组件
并在该文件夹下创建组件HospitalTop、HospitalBottom,并将其引入到APP中
import HospitalTop from '@/components/hospital_top/index.vue';
// @ts-ignore
import HospitalBottom from '@/components/hospital_bottom/index.vue';
const app = createApp(App);
app.component('HospitalTop',HospitalTop)
app.component('HospitalBottom',HospitalBottom)
//挂载
app.mount("#app");
注意在挂载前组装组件
定义HospitalTop组件
<script setup lang="ts">
</script>
<template>
<div class="top">
<div class="content">
<!-- 左侧 -->
<div class="left">
<img src="../../assets/images/logo.png" alt="">
<p>尚医通 预约挂号统一平台</p>
</div>
<!-- 右侧 -->
<div class="right">
<p class="help">帮助中心</p>
<p class="login">登录/注册</p>
</div>
</div>
</div>
</template>
<style scoped>
.top{
position: fixed;
z-index: 999;
width: 100%;
height: 70px;
background: #fff;
display: flex;
justify-content: center;
.content{
width: 1200px;
height: 70px;
background: white;
display: flex;
justify-content: space-between;
.left{
display: flex;
justify-content: center;
align-items: center;
img{
width: 50px;
height: 50px;
margin-right: 10px;
}
p{
font-size: 20px;
color: #55a6fe;
}
}
.right{
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #bbb;
.help{
margin-right: 10px;
}
}
}
}
</style>
了解新样式
position: fixed;
z-index: 999;
display: flex;
justify-content: center;
弹性布局(Flex布局)是一种现代的CSS布局方式,通过使用display: flex属性来创建一个弹性容器,并在其中使用灵活的盒子模型来进行元素的排列和定位。
弹性布局具有以下特点:
- 主轴与交叉轴:弹性容器具有主轴(main axis)和交叉轴(cross axis)。默认情况下,主轴是水平方向,交叉轴是垂直方向。
- 弹性容器:通过将父元素的display属性设置为flex或inline-flex来创建弹性容器。
- 子元素的弹性项目:弹性容器中的每个子元素都成为弹性项目。子元素可以指定各自在主轴和交叉轴上的大小、顺序以及对齐方式等。
- 主轴对齐:弹性项目可以在主轴上按照一定比例分配空间,也可以使用justify-content属性定义主轴的对齐方式。
- 交叉轴对齐:弹性项目可以在交叉轴上进行对齐,包括顶部对齐、底部对齐、居中对齐等,使用align-items属性定义交叉轴对齐方式。
- 换行与自动调整:可控制弹性项目是否换行,并且具备自动调整元素大小的能力。
弹性布局简化了网页布局的开发过程,提供了更灵活、响应式的布局方式。它适用于各种屏幕尺寸和设备类型,并能够快速适应不同的布局需求。
容器的属性
justify-content
justify-content属性定义了项目在主轴上的对齐方式。
它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。
flex-start(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。align-items
align-items属性定义项目在交叉轴上如何对齐。
它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。
flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。flex-direction
决定主轴的方向,水平或者垂直
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。
flex-wrap
换行不换行以及换行的方向
nowrap(默认):不换行
wrap:换行,第一行在上方。
wrap-reverse:换行,第一行在下方。
flex-flow
以上两种的简写方式
.box {
flex-flow: || ;
}align-content
align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
flex-start:与交叉轴的起点对齐。
flex-end:与交叉轴的终点对齐。
center:与交叉轴的中点对齐。
space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
stretch(默认值):轴线占满整个交叉轴。order属性order属性
定义项目的排列顺序。数值越小,排列越靠前,默认为0。
flex-grow属性
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
flex-shrink属性
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
flex-basis属性
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
flex属性
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
align-self属性
align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
该属性可能取6个值,除了auto,其他都与align-items属性完全一致。
position一般分为三种,一种是相对定位relative,一种是绝对定位absolute,一种是固定定位fixed
a】position 定位
b】z-index 定位层级
c】opacity 透明度
定义HospitalBottom组件
<script setup lang="ts">
</script>
<template>
<div class="bottom">
<div class="content">
<div class="left">京ICP备 13018369号 电话挂号010-56253825</div>
<div class="right">
<span>联系我们</span>
<span>合作伙伴</span>
<span>用户协议</span>
<span>隐私协议</span>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.bottom{
width: 100%;
height: 50px;
background: #f0f2f5;
display: flex;
justify-content: center;
.content{
width: 1200px;
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
color: #aaa;
.right{
span{
margin: 0px 5px;
}
}
}
}
</style>
创建路由
安装路由 npm i vue-router
在src下创建router文件夹并创建index.ts
import {createRouter,createWebHistory} from "vue-router";
//createRouter方法,用于创建路由实例,可以管理多个路由
export default createRouter({
// 路由模式设置
history:createWebHistory(),
routes:[
{
path:'/home',
component:()=> import('@/pages/home/index.vue')
},
{
path:'/hospital',
component:()=>import('@/pages/hospital/index.vue')
},
{
path:'/',
redirect:'/home'
}
],
//滚动行为:控制滚动条的位置
scrollBehavior(){
return{
left:0,
top:0
}
}
})
并在main.ts中引入并使用
import router from "@/router";
app.use(router);
创建首页及医院详情页
在src下创建pages文件夹,并创建home及hospital文件夹,然后分别创建index.vue
syt/src/pages/home/index.vue
<script setup lang="ts">
</script>
<template>
<div>我是首页</div>
</template>
<style scoped lang="scss">
</style>
syt/src/pages/hospital/index.vue
<script setup lang="ts">
</script>
<template>
<div>我是医院详情</div>
</template>
<style scoped lang="scss">
</style>
首页设计
使用Element Plus 架构
https://element-plus.org/zh-CN/guide/quickstart.html
安装Element Plus
npm install element-plus --save
引入element-plus插件
在main.ts 中引入
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createApp } from 'vue'
import App from '@/App.vue';
import '@/style/reset.scss'
//引入element-plus插件
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import HospitalTop from '@/components/hospital_top/index.vue';
// @ts-ignore
import HospitalBottom from '@/components/hospital_bottom/index.vue';
//引入vue-router核心插件并安装
import router from "@/router";
const app = createApp(App);
app.component('HospitalTop',HospitalTop)
app.component('HospitalBottom',HospitalBottom)
//安装vue-router
app.use(router);
//安装element-plus插件
app.use(ElementPlus)
//挂载
app.mount("#app");
首页拆分为三部分
轮播图、搜索框、医院信息
创建轮播图
syt/src/pages/home/carousel/index.vue
<script setup lang="ts">
</script>
<template>
<div>
<el-carousel height="350px">
<el-carousel-item v-for="item in 4" :key="item">
<img src="../../../assets/images/web-banner1.png">
</el-carousel-item>
</el-carousel>
</div>
</template>
<style scoped lang="scss">
img{
width: 100%;
height: 350px;
}
</style>
首页添加轮播图
syt/src/pages/home/index.vue
<script setup lang="ts">
import Carousel from './carousel/index.vue'
</script>
<template>
<div>
<!-- 首页轮播图结构 -->
<Carousel/>
</div>
</template>
<style scoped lang="scss">
</style>
创建搜索框
syt/src/pages/home/search/index.vue
<script setup lang="ts">
import {Search} from "@element-plus/icons-vue";
</script>
<template>
<div class="search">
<el-autocomplete
clearable
class="inline-input w-50"
placeholder="请输入医院名称"
/>
<el-button type="primary" :icon="Search">搜索</el-button>
</div>
</template>
<style scoped lang="scss">
.search{
width: 100%;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
margin: 10px 0px;
//深度选择器
::v-deep(.el-input__wrapper){
margin-right: 10px;
}
}
</style>
首页添加搜索框
syt/src/pages/home/index.vue
<script setup lang="ts">
import Carousel from './carousel/index.vue'
</script>
<template>
<div>
<!-- 首页轮播图结构 -->
<Carousel/>
<!-- 首页搜索医院的表单区域 -->
<Search/>
</div>
</template>
<style scoped lang="scss">
</style>
设计医院结构
使用Element Plusd Layout布局
syt/src/pages/home/index.vue
<!-- 首页轮播图结构 -->
<Carousel/>
<!-- 首页搜索医院的表单区域 -->
<Search/>
<!-- 底部展示医院的结构 -->
<el-row gutter="20">
<el-col :span="20">
</el-col>
<el-col :span="4">456</el-col>
</el-row>
医院结构拆分为等级、地区、医院卡片
创建医院等级
syt/src/pages/home/level/index.vue
<script setup lang="ts">
</script>
<template>
<div class="level">
<h1>医院</h1>
<div class="content">
<div class="left">等级:</div>
<ul class="hospital">
<li class="active">全部</li>
<li>三级甲等</li>
<li>三级乙等</li>
<li>二级甲等</li>
<li>一级</li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
.level{
color:#7f7f7f;
h1{
font-weight: 900;
margin: 10px 0px;
}
.content{
display: flex;
.left{
margin-right: 10px;
}
.hospital{
display: flex;
li{
margin-right: 10px;
&.active{
color:#55a6fe;
}
}
li:hover{
color:#55a6fe;
cursor:pointer;
}
}
}
}
</style>
创建地区
syt/src/pages/home/region/index.vue
<script setup lang="ts">
</script>
<template>
<div class="region">
<div class="content">
<div class="left">地区:</div>
<ul>
<li class="active">全部</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li>朝阳区</li>
<li v-for="item in 10" :key="item">朝阳区</li>
</ul>
</div>
</div>
</template>
<style scoped lang="scss">
.region{
color:#7f7f7f;
margin-top: 10px;
.content{
display: flex;
.left{
margin-right: 10px;
width: 53px;
}
ul{
display: flex;
flex-wrap: wrap;
li{
margin-right: 10px;
margin-bottom: 10px;
&.active{
color:#55a6fe;
}
}
li:hover{
color:#55a6fe;
cursor:pointer;
}
}
}
}
</style>
创建医院卡片
syt/src/pages/home/card/index.vue
<script setup lang="ts">
</script>
<template>
<div>
<el-card class="box-card" shadow="hover">
<div class="content">
<div class="left">
<div class="hospital_name">医院名称</div>
<div class="tip">
<div class="level">
<svg t="1720662383862" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3224" width="16" height="16"><path d="M857.28 344.992h-264.832c12.576-44.256 18.944-83.584 18.944-118.208 0-78.56-71.808-153.792-140.544-143.808-60.608 8.8-89.536 59.904-89.536 125.536v59.296c0 76.064-58.208 140.928-132.224 148.064l-117.728-0.192A67.36 67.36 0 0 0 64 483.04V872c0 37.216 30.144 67.36 67.36 67.36h652.192a102.72 102.72 0 0 0 100.928-83.584l73.728-388.96a102.72 102.72 0 0 0-100.928-121.824zM128 872V483.04c0-1.856 1.504-3.36 3.36-3.36H208v395.68H131.36A3.36 3.36 0 0 1 128 872z m767.328-417.088l-73.728 388.96a38.72 38.72 0 0 1-38.048 31.488H272V476.864a213.312 213.312 0 0 0 173.312-209.088V208.512c0-37.568 12.064-58.912 34.72-62.176 27.04-3.936 67.36 38.336 67.36 80.48 0 37.312-9.504 84-28.864 139.712a32 32 0 0 0 30.24 42.496h308.512a38.72 38.72 0 0 1 38.048 45.888z" p-id="3225"></path></svg>
<span>三级甲等</span>
</div>
<div class="timer">
<svg t="1720662686849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4370" width="16" height="16"><path d="M511.913993 63.989249c-247.012263 0-447.924744 200.912481-447.924744 447.924744s200.912481 447.924744 447.924744 447.924744 447.924744-200.912481 447.924744-447.924744S758.926256 63.989249 511.913993 63.989249zM511.913993 895.677474c-211.577356 0-383.763481-172.186125-383.763481-383.763481 0-211.577356 172.014111-383.763481 383.763481-383.763481s383.763481 172.014111 383.763481 383.763481S723.491349 895.677474 511.913993 895.677474z" fill="#575B66" p-id="4371"></path><path d="M672.05913 511.913993l-159.973123 0L512.086007 288.123635c0-17.717453-14.277171-32.166639-31.994625-32.166639-17.717453 0-31.994625 14.449185-31.994625 32.166639l0 255.956996c0 17.717453 14.277171 31.994625 31.994625 31.994625l191.967747 0c17.717453 0 32.166639-14.277171 32.166639-31.994625C704.053754 526.191164 689.604569 511.913993 672.05913 511.913993z" fill="#575B66" p-id="4372"></path></svg>
<span>每天8:00放号</span>
</div>
</div>
</div>
<div class="right">
<img src="../../../assets/images/logo.png">
</div>
</div>
</el-card>
</div>
</template>
<style scoped lang="scss">
.content{
display: flex;
justify-content: space-between;
.right{
img{
width: 50px;
height: 50px;
}
}
.left{
width: 60%;
.tip{
color: #7f7f7f;
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.level{
display: flex;
align-items: center;
span{
margin-left: 5px;
}
}
.timer{
display: flex;
align-items: center;
span{
margin-left: 5px;
}
}
}
}
</style>
图标网址
https://www.iconfont.cn/collections/index?spm=a313x.home_index.i3.3.58a33a819QJkxd
医院结构组装
<script setup lang="ts">
import Search from './search/index.vue'
import Level from './level/index.vue'
import Region from './region/index.vue'
import Card from './card/index.vue'
</script>
<template>
<div>
<!-- 首页轮播图结构 -->
<Carousel/>
<!-- 首页搜索医院的表单区域 -->
<Search/>
<!-- 底部展示医院的结构 -->
<el-row gutter="20">
<el-col :span="20">
<level/>
<Region/>
<div class="hospital">
<Card class="item" v-for="(item,index) in 10" :key="index" />
</div>
</el-col>
<el-col :span="4">456</el-col>
</el-row>
</div>
</template>
<style scoped lang="scss">
.hospital{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item{
width: 48%;
margin: 10px 0px;
}
}
</style>
分页器
https://element-plus.org/zh-CN/component/pagination.html
<script setup lang="ts">
import { ref } from 'vue'
let currentPage = ref<number>(1)
let pageSize = ref<number>(10)
</script>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:background="true"
layout="prev, pager, next, jumper,->, sizes,total"
:total="13"
/>
Axios的二次封装与代理跨域配置 P10
创建syt/src/utils/request.ts
//对axios函数库进行二次封装
//目的1:利用axios请求、响应拦截器功能
//目的2:请求拦截器,一般可以在请求头总携带公共的参数:token
//目的3:响应拦截器,可以简化服务器返回的数据,处理http网络错误
import axios from "axios";
//利用axios.create方法创建一个axios实例:可以设置基础路径、超时的时间设置
const request = axios.create({
baseURL:'/api',
timeout:5000
});
//请求拦截器
request.interceptors.request.use((config)=>{
return config;
});
//响应拦截器
request.interceptors.response.use((response)=>{
return response.data;
},(error)=>{
return Promise.reject( new Error(error.message))
});
//对外暴露axios
export default request;
跨域配置
在syt/vite.config.ts文件中增加
server: {
proxy: {
'/api': {
// target: 'http://localhost',
target:'http://syt.atguigu.cn',
changeOrigin: true,
},
},
}
尚硅谷服务地址
服务器地址:http://syt.atguigu.cn
医院接口:http://139.198.34.216:8201/swagger-ui.html
公共数据接口:http://139.198.34.216:8202/swagger-ui.html
会员接口:http://139.198.34.216:8203/swagger-ui.html
短信验证码接口:http://139.198.34.216:8204/swagger-ui.htm1
订单接口:http://139.198.34.216:8206/swagger-ui.html
文件上传接口:http://139.198.34.216:8205/swagger-ui.html
后台用户接口:http://139.198.34.216:8212/swagger-ui.html
创建syt/src/api/home/index.ts
import request from "@/utils/request.ts";
enum API {
// HOSPITAL_URL = 'hosp/hospital/' //尚硅谷地址
HOSPITAL_URL = 'hosp/hospital/findHospitalList/' //本地地址
}
export const reqHospital = (page:number,limit:number)=>request.get(API.HOSPITAL_URL+`${page}/${limit}`)
展示已有医院数据并分页展示 P11
修改首页
<script setup lang="ts">
import {reqHospital} from "@/api/home";
import {onMounted} from "vue";
let hasHospitalArr = ref([]);
let total = ref(0);
onMounted(()=>{
getHospitalInfo();
})
const getHospitalInfo = async ()=>{
let result: any = await reqHospital(currentPage.value,pageSize.value);
hasHospitalArr.value = result.data.data.content;
total = result.data.data.totalElements
}
//分页器页码发生变化时回调
const currentPageChange = ()=>{
getHospitalInfo();
}
//分页器下拉菜单发生变化时回调
const pageSizeChange = ()=>{
getHospitalInfo();
}
</script>
<template>
<div>
<!-- 首页轮播图结构 -->
<Carousel/>
<!-- 首页搜索医院的表单区域 -->
<Search/>
<!-- 底部展示医院的结构 -->
<el-row gutter="20">
<el-col :span="20">
<level/>
<Region/>
<div class="hospital">
<Card class="item" v-for="(item,index) in hasHospitalArr" :key="item.id" :hospitalInfo="item"/>
</div>
</div>
<!-- 分页器 -->
<div>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
:background="true"
layout="prev, pager, next, jumper,->, sizes,total"
:total="total"
@current-change="currentPageChange"
@size-change="pageSizeChange"
/>
</div>
</el-col>
<el-col :span="4">456</el-col>
</el-row>
</div>
</template>
修改Card
<script setup lang="ts">
defineProps(['hospitalInfo'])
</script>
<template>
<div>
<el-card class="box-card" shadow="hover">
<div class="content">
<div class="left">
<div class="hospital_name">{{hospitalInfo.hosname}}</div>
<div class="tip">
<div class="level">
<svg t="1720662383862" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3224" width="16" height="16"><path d="M857.28 344.992h-264.832c12.576-44.256 18.944-83.584 18.944-118.208 0-78.56-71.808-153.792-140.544-143.808-60.608 8.8-89.536 59.904-89.536 125.536v59.296c0 76.064-58.208 140.928-132.224 148.064l-117.728-0.192A67.36 67.36 0 0 0 64 483.04V872c0 37.216 30.144 67.36 67.36 67.36h652.192a102.72 102.72 0 0 0 100.928-83.584l73.728-388.96a102.72 102.72 0 0 0-100.928-121.824zM128 872V483.04c0-1.856 1.504-3.36 3.36-3.36H208v395.68H131.36A3.36 3.36 0 0 1 128 872z m767.328-417.088l-73.728 388.96a38.72 38.72 0 0 1-38.048 31.488H272V476.864a213.312 213.312 0 0 0 173.312-209.088V208.512c0-37.568 12.064-58.912 34.72-62.176 27.04-3.936 67.36 38.336 67.36 80.48 0 37.312-9.504 84-28.864 139.712a32 32 0 0 0 30.24 42.496h308.512a38.72 38.72 0 0 1 38.048 45.888z" p-id="3225"></path></svg>
<span>{{hospitalInfo.param.hostypeString}}</span>
</div>
<div class="timer">
<svg t="1720662686849" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4370" width="16" height="16"><path d="M511.913993 63.989249c-247.012263 0-447.924744 200.912481-447.924744 447.924744s200.912481 447.924744 447.924744 447.924744 447.924744-200.912481 447.924744-447.924744S758.926256 63.989249 511.913993 63.989249zM511.913993 895.677474c-211.577356 0-383.763481-172.186125-383.763481-383.763481 0-211.577356 172.014111-383.763481 383.763481-383.763481s383.763481 172.014111 383.763481 383.763481S723.491349 895.677474 511.913993 895.677474z" fill="#575B66" p-id="4371"></path><path d="M672.05913 511.913993l-159.973123 0L512.086007 288.123635c0-17.717453-14.277171-32.166639-31.994625-32.166639-17.717453 0-31.994625 14.449185-31.994625 32.166639l0 255.956996c0 17.717453 14.277171 31.994625 31.994625 31.994625l191.967747 0c17.717453 0 32.166639-14.277171 32.166639-31.994625C704.053754 526.191164 689.604569 511.913993 672.05913 511.913993z" fill="#575B66" p-id="4372"></path></svg>
<span>每天{{hospitalInfo.bookingRule.releaseTime}}放号</span>
</div>
</div>
</div>
<div class="right">
<img :src="`data:image/jpeg;base64,${hospitalInfo.logoData}`"
:alt="`${hospitalInfo.hosname}`"
class="hospital-img">
<!-- <img src="../../../assets/images/logo.png">-->
</div>
</div>
</el-card>
</div>
</template>
<style scoped lang="scss">
.content{
display: flex;
justify-content: space-between;
.right{
img{
width: 50px;
height: 50px;
}
}
.left{
width: 60%;
.tip{
color: #7f7f7f;
margin-top: 20px;
display: flex;
justify-content: space-between;
}
.level{
display: flex;
align-items: center;
span{
margin-left: 5px;
}
}
.timer{
display: flex;
align-items: center;
span{
margin-left: 5px;
}
}
}
}
</style>
首页已有医院数据的TS类型定义 P12
创建syt/src/api/home/type.ts
//定义首页模块ts数据类型
export interface ResponseData{
code:number,
message:string,
ok:boolean
}
//代表已有的医院数据的ts类型
export interface Hospital{
"id": string,
"createTime": string,
"updateTime": string,
"isDeleted": number,
"param": {
"hostypeString": string,
"fullAddress": string
},
"hoscode": string,
"hosname": string,
"hostype": string,
"provinceCode": string,
"cityCode": string,
"districtCode": string,
"address": string,
"logoData": string,
"intro": string,
"route": string,
"status": number,
"bookingRule": {
"cycle": number,
"releaseTime": string,
"stopTime": string,
"quitDay": number,
"quitTime": string,
"rule": string[]
}
}
//存储全部已有医院的数组类型
export type Content = Hospital[]
//获取已有医院接口返回的数据ts类型
export interface HospitalResponseData extends ResponseData{
data:{
"content":Content,
"pageable": {
"sort": {
"unsorted": boolean,
"sorted": boolean,
"empty": boolean
},
"offset": number,
"pageSize": number,
"pageNumber": number,
"unpaged": boolean,
"paged": boolean
},
"last": boolean,
"totalPages": number,
"totalElements": number,
"number": number,
"size": number,
"sort": {
"unsorted": boolean,
"sorted": boolean,
"empty": boolean
},
"numberOfElements": number,
"first": boolean,
"empty": boolean
}
}
修改syt/src/api/home/index.ts
import {HospitalResponseData} from "@/api/home/type.ts";
export const reqHospital = (page:number,limit:number)=>request.get<any,HospitalResponseData>(API.HOSPITAL_URL+`${page}/${limit}`)
修改syt/src/pages/home/index.vue
import type {Content,HospitalResponseData} from "@/api/home/type.ts";
let hasHospitalArr = ref<Content>([]);
let total= ref<number>(0);
const getHospitalInfo = async ()=>{
let result: HospitalResponseData = await reqHospital(currentPage.value,pageSize.value);
if(result.code==200){
hasHospitalArr.value = result.data.content;
total = result.data.totalElements
}
}
首页医院等级与地区数据展示 P13
修改syt/src/api/home/type.ts
增加
export interface HospitalLevelAndRegion{
"id": number,
"createTime": string,
"updateTime": string,
"isDeleted": number,
"param": {},
"parentId": number,
"name": string,
"value": string,
"dictCode": string,
"hasChildren": boolean
}
export type HospitalLevelAndRegionArr = HospitalLevelAndRegion[];
//获取等级或医院地区接口返回数据类型
export interface HospitalLevelAndRegionResponseData extends ResponseData{
data:HospitalLevelAndRegionArr
}
修改syt/src/api/home/index.ts
import {HospitalLevelAndRegionResponseData, HospitalResponseData} from "@/api/home/type.ts";
enum API {
// HOSPITAL_URL = 'hosp/hospital/' //尚硅谷地址
HOSPITAL_URL = 'hosp/hospital/findHospitalList/', //本地地址
HOSPITALLEAVELANDREGION_URL='/cmn/dict/findByDictCode/'
}
export const reqHospitalLevelAndRegion = (dictCode:string)=>request.get<any,HospitalLevelAndRegionResponseData>(API.HOSPITALLEAVELANDREGION_URL+dictCode);
修改syt/src/pages/home/level/index.vue
<script setup lang="ts">
import {onMounted,ref} from "vue";
import {reqHospitalLevelAndRegion} from "@/api/home";
import {HospitalLevelAndRegionArr, HospitalLevelAndRegionResponseData} from "@/api/home/type.ts";
let levelArr = ref<HospitalLevelAndRegionArr>([]);
let activeFlag = ref<string>('');
onMounted(()=>{
getLevel();
})
const getLevel = async ()=>{
let result: HospitalLevelAndRegionResponseData = await reqHospitalLevelAndRegion('Hostype');
if(result.code==200){
levelArr.value = result.data;
}
}
const changeLevel = (level:string)=>{
activeFlag.value = level;
console.log(activeFlag)
}
</script>
<script lang="ts">
export default {
name:"Level"
}
template中修改
<ul class="hospital">
<li :class="{active:activeFlag==''}" @click="changeLevel('')">全部</li>
<li v-for="level in levelArr" :key="level.value" @click="changeLevel(level.value)" :class="{active:activeFlag==level.value}">{{level.name}}</li>
</ul>
修改syt/src/pages/home/region/index.vue
<script setup lang="ts">
import {HospitalLevelAndRegionArr, HospitalLevelAndRegionResponseData} from "@/api/home/type.ts";
import {reqHospitalLevelAndRegion} from "@/api/home";
import {onMounted, ref} from "vue";
let regionArr = ref<HospitalLevelAndRegionArr>([]);
let activeFlag = ref<string>('');
onMounted(()=>{
getRegion();
})
const getRegion = async ()=>{
let result: HospitalLevelAndRegionResponseData = await reqHospitalLevelAndRegion('Beijing');
if(result.code==200){
regionArr.value = result.data;
}
}
const changeRegion=(region:string)=>{
activeFlag.value = region;
}
</script>
<script lang="ts">
export default {
name:"Region"
}
</script>
template中修改
<ul>
<li :class="{active:activeFlag==''}" @click="changeRegion('')">全部</li>
<li v-for="item in regionArr" :key="item.value" @click="changeRegion(item.value)" :class="{active:activeFlag==item.value}">{{item.name}}</li>
</ul>
首页根据等级与地区筛选医院展示 P14
修改syt/src/api/home/index.ts
export const reqHospital = (page:number,limit:number,hostype='',districtCode='')=>request.get<any,HospitalResponseData>(API.HOSPITAL_URL+`${page}/${limit}?hostype=${hostype}&districtCode=${districtCode}`);
修改syt/src/pages/home/index.vue
let hostype= ref<string>('');
let districtCode= ref<string>('');
//子组件自定义事件:获取儿子给父组件传递过来的等级参数
const getLevel=(level:string)=>{
hostype.value = level;
getHospitalInfo();
}
//子组件自定义事件:获取儿子给父组件传递过来的地区参数
const getRegion=(region:string)=>{
districtCode.value = region;
getHospitalInfo();
}
<level @getLevel="getLevel"/>
<Region @getRegion="getRegion"/>
<div class="hospital" v-if="hashasHospitalArr.length>0">
<Card class="item" v-for="(item,index) in hashasHospitalArr" :key="item.id" :hospitalInfo="item"/>
</div>
<el-empty v-else description="暂无数据" />
空状态组件
https://element-plus.org/zh-CN/component/empty.html
修改syt/src/pages/home/level/index.vue
const changeLevel = (level:string)=>{
activeFlag.value = level;
//触发自定义事件:将医院等级参数回传给父组件
$emit('getLevel',level);
}
let $emit = defineEmits(['getLevel'])
修改syt/src/pages/home/region/index.vue
const changeRegion=(region:string)=>{
activeFlag.value = region;
//给父组件传递区域的参数
$emit('getRegion',region);
}
let $emit = defineEmits(['getRegion'])
首页根据关键字搜索医院P15
修改syt/src/api/home/index.ts
import {HospitalInfo, HospitalLevelAndRegionResponseData, HospitalResponseData} from "@/api/home/type";
HOSPITALINFO_URL='/hosp/hospital/findByHosname/'
//根据关键字获取医院的数据进行展示
export const reqHospitalInfo = (hosname:string)=>request.get<any,HospitalInfo>(API.HOSPITALINFO_URL+hosname);
修改syt/src/api/home/type.ts
export interface HospitalInfo extends ResponseData{
data:Content
}
修改syt/src/pages/home/search/index.vue
<script setup lang="ts">
import {Search} from "@element-plus/icons-vue";
import {ref} from "vue";
import {reqHospitalInfo} from "@/api/home";
import type {HospitalInfo,Content} from "@/api/home/type";
import {useRouter} from "vue-router";
//收集搜索的关键字(医院的名字)
let hosname=ref<string>('');
//创建路由对象
let $router = useRouter();
//顶部组件的回调
const fetchData= async (keyword:string,cb:any)=>{
let result: HospitalInfo = await reqHospitalInfo(keyword);
let showData = result.data.map(item=>{
return {
value:item.hosname,
hoscode:item.hoscode
}
})
cb(showData);
}
//点击某一个推荐项
const goDetail =(item:any)=>{
console.log(item);
//点击推荐项目进行人民医院详情页,将来需要携带query参数
$router.push({path:'/hospital'})
}
</script>
<script lang="ts">
export default {
name:"Search"
}
<template>
<div class="search">
<el-autocomplete
:trigger-on-focus="false"
clearable
class="inline-input w-50"
placeholder="请输入医院名称"
:v-model="hosname"
:fetchSuggestions="fetchData"
@select="goDetail"
/>
<el-button type="primary" :icon="Search">搜索</el-button>
</div>
</template>
修改syt/src/pages/home/card/index.vue
<script setup lang="ts">
let propos = defineProps(['hospitalInfo'])
import {useRouter} from "vue-router";
//创建路由对象
let $router = useRouter();
const goDetail = ()=>{
console.log(propos.hospitalInfo);
$router.push({path:'/hospital'})
}
</script>
<el-card class="box-card" shadow="hover" @click="goDetail">
修改syt/src/components/hospital_top/index.vue
<script setup lang="ts">
import {useRouter} from "vue-router";
let $router = useRouter();
const goHome = ()=>{
//编程式导航跳转到首页
$router.push({path:'/home'})
}
</script>
<template>
<div class="top">
<div class="content">
<!-- 左侧 -->
<div class="left" @click="goHome">
<img src="../../assets/images/logo.png" alt="">
<p>尚医通 预约挂号统一平台</p>
</div>
<!-- 右侧 -->
<div class="right">
<p class="help">帮助中心</p>
<p class="login">登录/注册</p>
</div>
</div>
</div>
</template>
首页常见科室静态搭建P16
创建syt/src/pages/home/search/index.vue
<script setup lang="ts">
</script>
<template>
<div class="tip">
<!-- 常见科室的结构 -->
<div class="department">
<div class="header">
<div class="left">
<svg t="1721686788883" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3781" width="16" height="16"><path d="M400.696008 129.093147c-17.248849 0-31.233352 13.984503-31.233352 31.233352l0 30.470989c0 17.248849 13.984503 31.233352 31.233352 31.233352s31.233352-13.984503 31.233352-31.233352l0-30.470989C431.92936 143.078673 417.944857 129.093147 400.696008 129.093147z" fill="#d81e06" p-id="3782"></path><path d="M623.647823 129.093147c-17.248849 0-31.233352 13.984503-31.233352 31.233352l0 30.470989c0 17.248849 13.985526 31.233352 31.233352 31.233352 17.248849 0 31.233352-13.984503 31.233352-31.233352l0-30.470989C654.881175 143.078673 640.896672 129.093147 623.647823 129.093147z" fill="#d81e06" p-id="3783"></path><path d="M425.695379 312.937269c11.209296 18.047028 41.974997 48.588625 86.152149 48.588625 43.958164 0 75.100442-30.308283 86.573751-48.223305 9.302877-14.528901 5.068436-33.846876-9.455349-43.149752-14.539134-9.307993-33.851992-5.063319-43.149752 9.455349-0.121773 0.198521-13.379729 19.449981-33.968649 19.449981-19.993357 0-32.428573-18.107403-33.271778-19.373233-9.17087-14.417361-28.28009-18.799158-42.829458-9.760295C421.089477 279.028994 416.591023 298.28557 425.695379 312.937269z" fill="#d81e06" p-id="3784"></path><path d="M564.242851 625.945145l-20.278859 0L543.963992 462.486306c0-17.253966-13.985526-31.233352-31.233352-31.233352-17.248849 0-31.233352 13.979386-31.233352 31.233352l0 163.457816-20.283975 0c-45.924959 0-83.289961 37.363979-83.289961 83.289961l0 103.024421c0 45.924959 37.363979 83.289961 83.289961 83.289961l103.029538 0c45.924959 0 83.289961-37.363979 83.289961-83.289961L647.532813 709.234083C647.532813 663.309124 610.168834 625.945145 564.242851 625.945145zM585.066109 812.258505c0 11.286044-9.537214 20.822235-20.822235 20.822235L461.214337 833.080739c-11.286044 0-20.822235-9.537214-20.822235-20.822235L440.392102 709.234083c0-11.286044 9.537214-20.822235 20.822235-20.822235l103.029538 0c11.286044 0 20.822235 9.537214 20.822235 20.822235L585.066109 812.258505z" fill="#d81e06" p-id="3785"></path><path d="M250.808256 625.945145l-17.482163 0 0-266.970354c0-35.483142 28.864398-64.35368 64.343447-64.35368 17.248849 0 31.233352-13.984503 31.233352-31.233352s-13.985526-31.233352-31.233352-31.233352c-69.924559 0-126.810151 56.890708-126.810151 126.820384l0 266.970354-23.079648 0c-45.924959 0-83.289961 37.363979-83.289961 83.289961l0 103.024421c0 45.924959 37.363979 83.289961 83.289961 83.289961l103.029538 0c45.924959 0 83.289961-37.363979 83.289961-83.289961L334.099241 709.234083C334.098217 663.309124 296.734238 625.945145 250.808256 625.945145zM271.630491 812.258505c0 11.286044-9.537214 20.822235-20.822235 20.822235L147.778718 833.080739c-11.286044 0-20.822235-9.537214-20.822235-20.822235L126.956484 709.234083c0-11.286044 9.537214-20.822235 20.822235-20.822235l103.029538 0c11.286044 0 20.822235 9.537214 20.822235 20.822235L271.630491 812.258505z" fill="#d81e06" p-id="3786"></path><path d="M876.565113 625.945145l-21.961174 0 0-266.970354c0-69.929676-56.890708-126.820384-126.815267-126.820384-17.248849 0-31.233352 13.985526-31.233352 31.233352s13.984503 31.233352 31.233352 31.233352c35.483142 0 64.348564 28.869514 64.348564 64.35368l0 266.970354-18.605753 0c-45.924959 0-83.289961 37.363979-83.289961 83.289961l0 103.024421c0 45.924959 37.363979 83.289961 83.289961 83.289961l103.034655 0c45.924959 0 83.289961-37.363979 83.289961-83.289961L959.856098 709.234083C959.854051 663.309124 922.490072 625.945145 876.565113 625.945145zM897.387347 812.258505c0 11.286044-9.537214 20.822235-20.822235 20.822235L773.530458 833.080739c-11.286044 0-20.822235-9.537214-20.822235-20.822235L752.708224 709.234083c0-11.286044 9.537214-20.822235 20.822235-20.822235l103.034655 0c11.286044 0 20.822235 9.537214 20.822235 20.822235L897.387347 812.258505z" fill="#d81e06" p-id="3787"></path></svg>
<span>常见科室</span>
</div>
<div class="right">
<span>全部</span>
<svg t="1721686869730" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4858" width="16" height="16"><path d="M357.957 167.176l-49.463 48.973 294.308 296.327-296.782 293.831 49.044 49.381 346.239-342.809z" fill="#848484" p-id="4859"></path></svg>
</div>
</div>
<div class="content">
<ul>
<li>神经内科</li>
<li>消化内科</li>
<li>呼吸内科</li>
<li>内科</li>
<li>神经外科</li>
<li>妇科</li>
<li>产科</li>
<li>儿科</li>
</ul>
</div>
</div>
<!-- 平台公告 -->
<div class="notice">
<div class="header">
<div class="left">
<svg t="1721687792291" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5901" width="16" height="16"><path d="M836.625965 926.809825a61.44 61.44 0 0 1-61.44-61.44 53.894737 53.894737 0 0 1 0.898246-10.778948l-83.357193-76.889824V682.666667h-68.266667v86.950175a61.44 61.44 0 1 1-66.290526 0V682.666667h-64.314386v35.929824a61.619649 61.619649 0 1 1-65.931228-1.077895V682.666667h-66.290527v94.854737l-45.451228 42.037894a58.026667 58.026667 0 0 1 1.077895 10.958597 61.619649 61.619649 0 1 1-61.619649-61.44 59.823158 59.823158 0 0 1 15.809123 2.335438l23.893333-22.276491v-69.70386a188.990877 188.990877 0 0 1-10.419649-369.178947 255.461053 255.461053 0 0 1 477.14807 0 189.170526 189.170526 0 0 1-4.670877 368.280702v70.602105l62.158596 56.948772a60.541754 60.541754 0 0 1 16.168421-2.155789 61.44 61.44 0 1 1 0 122.88z m-125.754386-157.013334l85.333333 78.865965-1.437193 5.389474a48.864561 48.864561 0 0 0-1.437193 11.317895 43.475088 43.475088 0 0 0 86.950176 0 44.014035 44.014035 0 0 0-44.373334-43.475088 40.780351 40.780351 0 0 0-14.910877 2.694737l-5.389473 1.796491-76.171229-69.70386v-93.417544l7.545264-1.257543a171.205614 171.205614 0 0 0 6.467368-335.764211l-4.850526-1.077895-1.616842-4.670877a237.49614 237.49614 0 0 0-446.787369 0l-1.616842 4.670877-4.850526 1.077895A171.205614 171.205614 0 0 0 305.403509 663.084912l7.545263 1.077895v92.878597l-37.367018 34.67228-5.030175-1.796491a49.762807 49.762807 0 0 0-15.270175-2.874386 43.834386 43.834386 0 1 0 41.858245 31.438597l-1.257544-5.209825 47.427369-43.475088V664.701754h102.220351v62.877193l-5.030176 2.515088a42.756491 42.756491 0 0 0-24.432281 38.98386 43.475088 43.475088 0 1 0 63.955088-38.624562l-4.491228-2.515087V664.701754h100.244211v115.514386l-4.670878 2.515088a43.475088 43.475088 0 1 0 39.882106 0l-4.850527-2.515088V664.701754h104.196492z m5.928421-134.916491H330.015439a141.204211 141.204211 0 0 1-17.964913-281.150877l9.521404-1.077895 2.515088-9.341754a207.674386 207.674386 0 0 1 398.821052 0l2.694737 9.341754 9.341754 1.077895a141.204211 141.204211 0 0 1-17.964912 281.150877zM523.497544 211.806316A190.607719 190.607719 0 0 0 341.333333 348.339649l-5.748772 20.659649-21.198596 2.515088a123.239298 123.239298 0 0 0 15.629474 245.400702h386.604912a123.239298 123.239298 0 0 0 16.168421-245.400702l-21.198597-2.515088-5.928421-20.659649a190.607719 190.607719 0 0 0-182.16421-136.533333z" fill="#1296db" p-id="5902"></path><path d="M646.736842 557.990175H387.862456a9.162105 9.162105 0 0 1-8.982456-8.982456 8.982456 8.982456 0 0 1 8.982456-8.982456H646.736842a8.982456 8.982456 0 0 1 8.982456 8.982456 8.982456 8.982456 0 0 1-8.982456 8.982456zM646.736842 490.621754h-129.706667a8.982456 8.982456 0 0 1-8.982456-8.982456 9.162105 9.162105 0 0 1 8.982456-8.982456H646.736842a8.982456 8.982456 0 0 1 8.982456 8.982456 8.982456 8.982456 0 0 1-8.982456 8.982456zM646.736842 423.432982h-129.706667a9.162105 9.162105 0 0 1-8.982456-8.982456 8.982456 8.982456 0 0 1 8.982456-8.982456H646.736842a8.982456 8.982456 0 0 1 8.982456 8.982456 8.982456 8.982456 0 0 1-8.982456 8.982456z" fill="#1296db" p-id="5903"></path><path d="M387.862456 410.857544m26.408421 0l32.87579 0q26.408421 0 26.408421 26.408421l0 32.875789q0 26.408421-26.408421 26.408421l-32.87579 0q-26.408421 0-26.408421-26.408421l0-32.875789q0-26.408421 26.408421-26.408421Z" fill="#1296db" p-id="5904"></path></svg>
<span>平台公告</span>
</div>
<div class="right">
<span>全部</span>
<svg t="1721686869730" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4858" width="16" height="16"><path d="M357.957 167.176l-49.463 48.973 294.308 296.327-296.782 293.831 49.044 49.381 346.239-342.809z" fill="#848484" p-id="4859"></path></svg>
</div>
</div>
<div class="content">
<ul>
<li>关于延长北京大学国际医院放假的通知</li>
<li>北京中医药大学东方医院部分科室医生门诊医</li>
<li>武警总医院号源暂停更新通知</li>
</ul>
</div>
</div>
<!-- 停诊公告 -->
<div class="notice">
<div class="header">
<div class="left">
<svg t="1721688425278" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6974" width="16" height="16"><path d="M736 768h-448a32 32 0 0 1 0-64h448a32 32 0 0 1 0 64z m-44.8-415.552l-147.669333-147.669333a61.994667 61.994667 0 0 1-62.848 0l-147.669334 147.669333a32.554667 32.554667 0 0 1-46.037333-46.037333l161.578667-161.578667a63.68 63.68 0 0 1 127.104 0l161.578666 161.578667a32.554667 32.554667 0 0 1-46.037333 46.037333zM565.333333 512a32 32 0 0 1 0 64h-277.333333a32 32 0 0 1 0-64h277.333333z" fill="#1296db" p-id="6975"></path><path d="M853.333333 981.333333H170.666667a85.333333 85.333333 0 0 1-85.333334-85.333333V384a85.333333 85.333333 0 0 1 85.333334-85.333333h682.666666a85.333333 85.333333 0 0 1 85.333334 85.333333v512a85.333333 85.333333 0 0 1-85.333334 85.333333z m21.333334-576a42.666667 42.666667 0 0 0-42.666667-42.666666H192a42.666667 42.666667 0 0 0-42.666667 42.666666v469.333334a42.666667 42.666667 0 0 0 42.666667 42.666666h640a42.666667 42.666667 0 0 0 42.666667-42.666666V405.333333z" fill="#1296db" p-id="6976"></path></svg>
<span>停诊公告</span>
</div>
<div class="right">
<span>全部</span>
<svg t="1721686869730" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4858" width="16" height="16"><path d="M357.957 167.176l-49.463 48.973 294.308 296.327-296.782 293.831 49.044 49.381 346.239-342.809z" fill="#848484" p-id="4859"></path></svg>
</div>
</div>
<div class="content">
<ul>
<li>中国人民解放军总医院第六医学中心(原海军总医院)呼吸内科门诊停诊公告</li>
<li>首都医科大学附属北京潞河医院老年医学科门诊停诊公告</li>
<li>中日友好医院中西医结合心内科门诊停诊公告</li>
</ul>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.tip{
color: #7f7f7f;
.department{
.header{
display: flex;
justify-content: space-between;
.left{
display:flex;
span{
margin-left:5px;
}
}
}
.content{
ul{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
li{
width: 40%;
margin-top: 20px;
}
}
}
}
.notice{
margin-top: 20px;
.header{
display: flex;
justify-content: space-between;
.left{
display:flex;
span{
margin-left:5px;
}
}
}
.content{
ul{
li{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-top:20px
}
}
}
}
}
</style>
修改syt/src/pages/home/index.vue
import Tip from './tip/index.vue'
<el-col :span="4"><Tip/></el-col>
医院详情菜单与子路由P17
创建syt/src/pages/hospital/close/index.vue
<script setup lang="ts">
</script>
<template>
<div>停诊信息</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/hospital/detail/index.vue
<script setup lang="ts">
</script>
<template>
<div>
医院详情
</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/hospital/notice/index.vue
<script setup lang="ts">
</script>
<template>
<div>
预约须知
</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/hospital/register/index.vue
<script setup lang="ts">
</script>
<template>
<div>
预约挂号
</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/hospital/search/index.vue
<script setup lang="ts">
</script>
<template>
<div>查询与取消</div>
</template>
<style scoped lang="scss">
</style>
修改syt/src/pages/hospital/index.vue
<script setup lang="ts">
import {
Document,
Menu as IconMenu,
InfoFilled,
Search,
HomeFilled,
Setting,
} from '@element-plus/icons-vue'
import {useRouter,useRoute} from "vue-router";
let $router = useRouter();
let $route = useRoute();
const changeActive = (path:string)=>{
$router.push({path});
}
</script>
<template>
<div class="hospital" >
<!-- 左侧导航区域 -->
<div class="menu">
<div class="top">
<el-icon><HomeFilled/></el-icon>
<span> / 医院信息</span>
</div>
<el-menu
class="el-menu-vertical-demo"
:default-active="$route.path"
>
<el-menu-item index="/hospital/register" @click="changeActive('/hospital/register')">
<el-icon><icon-menu /></el-icon>
<span>预约挂号</span>
</el-menu-item>
<el-menu-item index="/hospital/detail" @click="changeActive('/hospital/detail')">
<el-icon><document /></el-icon>
<span>医院详情</span>
</el-menu-item>
<el-menu-item index="/hospital/notice" @click="changeActive('/hospital/notice')">
<el-icon><setting /></el-icon>
<span>预约须知</span>
</el-menu-item>
<el-menu-item index="/hospital/close" @click="changeActive('/hospital/close')">
<el-icon><InfoFilled /></el-icon>
<span>停诊信息</span>
</el-menu-item>
<el-menu-item index="/hospital/search" @click="changeActive('/hospital/search')">
<el-icon><Search /></el-icon>
<span>查询与取消</span>
</el-menu-item>
</el-menu>
</div>
<!-- 右侧内容展示区域:路由组件展示位置 -->
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<style scoped lang="scss">
.hospital{
display: flex;
.menu{
flex: 2;
display: flex;
flex-direction: column;
align-items: center;
.top{
color: #7f7f7f;
}
}
.content{
flex: 8;
}
}
</style>
修改syt/src/router/index.ts
{
path:'/hospital',
component:()=>import((`@/pages/hospital/index.vue`)),
children:[
{
path:'register',
component:()=>import(('@/pages/hospital/register/index.vue'))
},
{
path:'detail',
component:()=>import(('@/pages/hospital/detail/index.vue'))
},
{
path:'notice',
component:()=>import(('@/pages/hospital/notice/index.vue'))
},
{
path:'close',
component:()=>import(('@/pages/hospital/close/index.vue'))
},
{
path:'search',
component:()=>import(('@/pages/hospital/search/index.vue'))
}
]
},
修改syt/src/pages/home/search/index.vue
//点击某一个推荐项
const goDetail =(item:any)=>{
console.log(item);
//点击推荐项目进行人民医院详情页,将来需要携带query参数
$router.push({path:'/hospital/register'})
}
修改syt/src/pages/home/card/index.vue
const goDetail = ()=>{
console.log(propos.hospitalInfo);
$router.push({path:'/hospital/register'})
}
Pinia仓库存储医院详情数据P18
创建syt/src/api/hospital/type.ts
//定义详情模块数据ts类型
export interface ResponseData{
code:number,
message:string,
ok:boolean
}
//代表医院详情的数据
export interface HospitalDetail{
"bookingRule": {
"cycle": number,
"releaseTime": string,
"stopTime": string,
"quitDay": number,
"quitTime": string,
"rule": string[]
},
"hospital": {
"id": string,
"createTime": string,
"updateTime": string,
"isDeleted": 0,
"param": {
"hostypeString": string,
"fullAddress": string
},
"hoscode": string,
"hosname": string,
"hostype": string,
"provinceCode": string,
"cityCode": string,
"districtCode": string,
"address": string,
"logoData": string,
"intro": string,
"route": string,
"status": number,
"bookingRule": null
}
}
//医院详情返回数据ts
export interface HospitalDetail extends ResponseData{
data:HospitalDetail
}
创建syt/src/api/hospital/index.ts
import request from "@/utils/request.ts";
import {HospitalDetail} from "@/api/hospital/type.ts";
enum API{
HOSPITALDETAIL_URl='/hosp/hosptal'
}
export const reqHospitalDetail = (hoscode:string)=>request.get<any,HospitalDetail>(API.HOSPITALDETAIL_URl+hoscode)
安装pinia
npm i pinia
修改syt/src/main.ts
//引入pinia仓库
import pinia from '@/store'
//安装pinia仓库
app.use(pinia);
创建syt/src/store/index.ts
import {createPinia} from "pinia";
export default createPinia();
创建syt/src/store/modules/hospitalDetail.ts
import {defineStore} from "pinia";
//pinia仓库写法:组合式API、选择式API写法
import {reqHospitalDetail} from "@/api/hospital/index.ts"
import type {HospitalDetail} from "@/api/hospital/type.ts";
const useDetailStore = defineStore('Detail',{
state:()=>{
return {
//医院详情数据
hospitalInfo:{},
}
},
actions:{
//获取医院详情的方法
async getHospital(hoscode:string){
let result:HospitalDetail = await reqHospitalDetail(hoscode);
if(result.code==200){
this.hospitalInfo = result.data
}
}
},
getters:{
}
})
export default useDetailStore
完成预约挂号业务P19
修改syt/src/store/modules/hospitalDetail.ts
import {DetailState} from "@/store/modules/interface";
state:():DetailState=>{
return {
//医院详情数据
hospitalInfo:({} as HospitalDetail),
}
},
创建syt/src/store/modules/interface/index.ts
import type {HospitalDetail} from "@/api/hospital/type.ts";
//定义仓库内部存储数据state的ts类型
export interface DetailState{
hospitalInfo:HospitalDetail
}
修改syt/src/pages/hospital/register/index.vue
<script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail.ts'
let hospitalStore = useDetailStore();
</script>
<template>
<div class="register">
<div class="top">
<div class="title">{{hospitalStore.hospitalInfo.hospital?.hosname}}</div>
<div class="level">
<svg t="1721959943048" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8112" width="16" height="16"><path d="M621.674667 408.021333c16.618667-74.24 28.224-127.936 34.837333-161.194666C673.152 163.093333 629.941333 85.333333 544.298667 85.333333c-77.226667 0-116.010667 38.378667-138.88 115.093334l-0.586667 2.24c-13.728 62.058667-34.72 110.165333-62.506667 144.586666a158.261333 158.261333 0 0 1-119.733333 58.965334l-21.909333 0.469333C148.437333 407.808 106.666667 450.816 106.666667 503.498667V821.333333c0 64.8 52.106667 117.333333 116.394666 117.333334h412.522667c84.736 0 160.373333-53.568 189.12-133.92l85.696-239.584c21.802667-60.96-9.536-128.202667-70.005333-150.186667a115.552 115.552 0 0 0-39.488-6.954667H621.674667zM544.256 149.333333c39.253333 0 59.498667 36.48 49.888 84.928-7.573333 38.144-21.984 104.426667-43.221333 198.666667-4.512 20.021333 10.56 39.093333 30.912 39.093333h218.666666c6.101333 0 12.16 1.066667 17.909334 3.168 27.445333 9.984 41.674667 40.554667 31.776 68.266667l-85.568 239.573333C744.981333 838.026667 693.301333 874.666667 635.402667 874.666667H223.498667C194.314667 874.666667 170.666667 850.784 170.666667 821.333333V503.498667c0-17.866667 14.144-32.448 31.829333-32.821334l21.866667-0.469333a221.12 221.12 0 0 0 167.381333-82.56c34.346667-42.602667 59.146667-99.306667 74.869333-169.877333C482.101333 166.336 499.552 149.333333 544.266667 149.333333z" fill="#d81e06" p-id="8113"></path></svg>
<span>{{hospitalStore.hospitalInfo.hospital?.param.hostypeString}}</span>
</div>
</div>
<!-- 展示内容区域 -->
<div class="content">
<div class="left">
<img :src="`data:image/jpeg;base64,${hospitalStore.hospitalInfo.hospital.logoData}`"
:alt="`${hospitalStore.hospitalInfo.hospital?.hosname}`"
class="hospital-img">
</div>
<div class="right">
<div>挂号规则</div>
<div class="time">
预约周期:10天 放号时间:{{ hospitalStore.hospitalInfo.bookingRule?.releaseTime}} 停挂时间:{{ hospitalStore.hospitalInfo.bookingRule?.stopTime}}
</div>
<div class="address">
具体位置:{{ hospitalStore.hospitalInfo.hospital?.param.fullAddress}}
</div>
<div class="route">
具体路线:{{ hospitalStore.hospitalInfo.hospital.route}}
</div>
<div class="releasetime">
退号时间:就诊前一个工作日{{ hospitalStore.hospitalInfo.bookingRule.quitTime}}前取消
</div>
<div class="rule"> 预约挂号规则</div>
<div class="ruleInfo" v-for="(item,index) in hospitalStore.hospitalInfo.bookingRule?.rule" :key="index">
{{item}}
</div>
</div>
</div>
<!-- 放置每一个医院的科室的数据 -->
</div>
</template>
<style scoped lang="scss">
.register{
.top{
display: flex;
.title{
font-size: 30px;
}
.level{
color: #7f7f7f;
margin-left: 10px;
height: 40px;
text-align: center;
line-height: 40px;
span{
margin-left: 5px;
}
}
}
.content{
display: flex;
margin-top: 20px;
.left{
width: 80px;
img{
width: 80px;
height: 80px;
border-radius: 50%;
}
}
.right{
flex: 1;
margin-left: 20px;
.time,.address,.route,.releasetime,.ruleInfo{
margin-top: 10px;
color: #7f7f7f;
}
.rule{
margin:10px 0px;
}
}
}
}
</style>
完成医院详情业务模块P20
修改syt/src/pages/hospital/detail/index.vue
<script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail.ts'
let hospitalStore = useDetailStore();
</script>
<template>
<div class="detail">
<!-- 医院名字与医院等级的结构 -->
<div class="top">
<div class="name">{{hospitalStore.hospitalInfo.hospital?.hosname}}</div>
<div class="level">
<svg t="1721959943048" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8112" width="16" height="16"><path d="M621.674667 408.021333c16.618667-74.24 28.224-127.936 34.837333-161.194666C673.152 163.093333 629.941333 85.333333 544.298667 85.333333c-77.226667 0-116.010667 38.378667-138.88 115.093334l-0.586667 2.24c-13.728 62.058667-34.72 110.165333-62.506667 144.586666a158.261333 158.261333 0 0 1-119.733333 58.965334l-21.909333 0.469333C148.437333 407.808 106.666667 450.816 106.666667 503.498667V821.333333c0 64.8 52.106667 117.333333 116.394666 117.333334h412.522667c84.736 0 160.373333-53.568 189.12-133.92l85.696-239.584c21.802667-60.96-9.536-128.202667-70.005333-150.186667a115.552 115.552 0 0 0-39.488-6.954667H621.674667zM544.256 149.333333c39.253333 0 59.498667 36.48 49.888 84.928-7.573333 38.144-21.984 104.426667-43.221333 198.666667-4.512 20.021333 10.56 39.093333 30.912 39.093333h218.666666c6.101333 0 12.16 1.066667 17.909334 3.168 27.445333 9.984 41.674667 40.554667 31.776 68.266667l-85.568 239.573333C744.981333 838.026667 693.301333 874.666667 635.402667 874.666667H223.498667C194.314667 874.666667 170.666667 850.784 170.666667 821.333333V503.498667c0-17.866667 14.144-32.448 31.829333-32.821334l21.866667-0.469333a221.12 221.12 0 0 0 167.381333-82.56c34.346667-42.602667 59.146667-99.306667 74.869333-169.877333C482.101333 166.336 499.552 149.333333 544.266667 149.333333z" fill="#d81e06" p-id="8113"></path></svg>
<span>{{hospitalStore.hospitalInfo.hospital?.param.hostypeString}}</span>
</div>
</div>
<!--医院的logo与路线-->
<div class="logo">
<div class="left">
<img :src="`data:image/jpeg;base64,${hospitalStore.hospitalInfo.hospital?.logoData}`"
:alt="`${hospitalStore.hospitalInfo.hospital?.hosname}`"
class="hospital-img">
</div>
<div class="right">
<div class="address">
<svg t="1722032845308" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9200" width="16" height="16"><path d="M504.448 519.552m-151.189333 0a151.189333 151.189333 0 1 0 302.378666 0 151.189333 151.189333 0 1 0-302.378666 0Z" fill="#111111" p-id="9201"></path><path d="M504.448 124.714667c-218.069333 0-394.858667 176.768-394.858667 394.837333s176.789333 394.858667 394.858667 394.858667 394.837333-176.789333 394.837333-394.858667S722.517333 124.714667 504.448 124.714667z m0 64c182.72 0 330.837333 148.117333 330.837333 330.837333s-148.117333 330.858667-330.837333 330.858667S173.589333 702.272 173.589333 519.552c0-182.72 148.138667-330.837333 330.858667-330.837333z" fill="#111111" p-id="9202"></path><path d="M519.552 10.666667a32 32 0 0 1 32 32v98.922666a32 32 0 1 1-64 0V42.666667a32 32 0 0 1 32-32zM519.552 850.410667a32 32 0 0 1 32 32V981.333333a32 32 0 0 1-64 0v-98.922666a32 32 0 0 1 32-32z" fill="#111111" p-id="9203"></path><path d="M10.666667 519.552a32 32 0 0 1 32-32h98.922666a32 32 0 1 1 0 64H42.666667a32 32 0 0 1-32-32zM850.410667 519.552a32 32 0 0 1 32-32H981.333333a32 32 0 0 1 0 64h-98.922666a32 32 0 0 1-32-32z" fill="#111111" p-id="9204"></path></svg>
<span>具体位置:{{ hospitalStore.hospitalInfo.hospital?.param.fullAddress}}</span>
</div>
<div class="route">
规划路线:{{ hospitalStore.hospitalInfo.hospital?.route}}
</div>
</div>
</div>
<!-- 医院介绍 -->
<div class="footer">
<h1>医院介绍</h1>
<p>
{{ hospitalStore.hospitalInfo.hospital.intro}}
</p>
</div>
</div>
</template>
<style scoped lang="scss">
.detail {
.top {
display: flex;
.name {
font-size: 30px;
font-weight: 900;
}
.level {
color: #7f7f7f;
margin-left: 10px;
height: 40px;
text-align: center;
line-height: 40px;
span {
margin-left: 5px;
}
}
}
.logo {
display: flex;
margin:10px 0px;
.left {
width: 80px;
img {
width: 80px;
height: 80px;
border-radius: 50%;
}
}
.right{
flex: 1;
margin-left: 10px;
color: #7f7f7f;
div{
margin :10px 0px;
}
.rule{
}
}
}
.footer{
color: #7f7f7f;
p{
margin-top: 10px;
line-height: 30px;
}
}
}
</style>
修改syt/src/pages/hospital/index.vue
const changeActive = (path:string)=>{
$router.push({path,query:{hoscode:$route.query.hoscode}});
}
完成预约通知业务P21
修改syt/src/pages/hospital/notice/index.vue
<script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail.ts'
let hospitalStore = useDetailStore();
</script>
<template>
<div class="notice">
<h1 class="hosname">{{hospitalStore.hospitalInfo.hospital?.hosname}}预约挂号须知</h1>
<div class="top">为方便您早日就医康复。请您认真阅读预约挂号须知:</div>
<div class="info">
<div class="tip">一、预约实名制:</div>
<div class="text">
统一平台电话预约和网上预约挂号均采取实名制注册预约,请您如实提供就诊人员的真实姓名、有效证件号(身份证、护照)、性别、手机号码、社保卡号等基本信息。
</div>
</div>
<div class="info">
<div class="tip">二、预约挂号</div>
<div class="text">
按照北京市卫健委统一平台要求,预约挂号规则如下:
<ul>
<li>在同一自然日,同一医院,同一科室,同一就诊单元,同一就诊人,可以预约最多1个号源;</li>
<li>在同一自然周,同一就诊人,可以预约最多8个号源;</li>
<li>在同一自然月,同一就诊人,可以预约最多12个号源;</li>
<li>在同一自然季度,同一就诊人,可以预约最多24个号源。</li>
</ul>
</div>
</div>
<div class="info">
<div class="tip">三、取消预约:</div>
<div class="text">已完成预约的号源,如需办理退号,至少在就诊前一工作日14:00前通过网站、微信公众号、114电话等平台预约渠道进行取消预约。
</div>
</div>
<div class="info">
<div class="tip">
四、爽约处理:
</div>
<div class="text">如预约成功后患者未能按时就诊且不办理取消预约号视为爽约,同一患者在自然年内爽约规则如下:
<ul>
<li>累计爽约3次,自3次爽约日起,90天内不允许通过114平台进行预约挂号;</li>
<li>累计爽约6次,自6次爽约日起,180天内不允许通过114平台进行预约挂号。</li>
</ul>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.notice{
.hosname{
text-align: center;
font-size: 30px;
}
.top{
color: #7f7f7f;
margin: 10px 0px;
}
.info{
.tip{
color: red;
font-weight: 900;
}
.text{
color: #7f7f7f;
margin: 10px 0px;
line-height: 30px;
}
}
}
</style>
修改syt/src/pages/hospital/close/index.vue
<script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail.ts'
let hospitalStore = useDetailStore();
</script>
<template>
<div>
<div class="info">
<h1>{{hospitalStore.hospitalInfo.hospital?.hosname}}停诊信息</h1>
<el-empty description="暂无信息"></el-empty>
</div>
</div>
</template>
<style scoped lang="scss">
.info{
h1{
text-align: center;
}
}
</style>
修改syt/src/pages/hospital/search/index.vue
<script setup lang="ts">
import useDetailStore from '@/store/modules/hospitalDetail.ts'
let hospitalStore = useDetailStore();
</script>
<template>
<div>
<div class="info">
<h1>{{hospitalStore.hospitalInfo.hospital?.hosname}}查询信息</h1>
<el-empty description="暂无信息"></el-empty>
</div>
</div>
</template>
<style scoped lang="scss">
.info{
h1{
text-align: center;
}
}
</style>
完成医院科室业务P22
修改syt/src/api/hospital/index.ts
import {DepartmentResponseData, HospitalDetail} from "@/api/hospital/type.ts";
enum API{
HOSPITALDETAIL_URl='/hosp/hospital/',
//获取某一个医院的科室数据
HOSPITALDEPARTMENT_URL='hosp/hospital/department/'
}
//获取医院科室的接口
export const reqHospitalDepartment = (hoscode:string)=>request.get<any,DepartmentResponseData>(API.HOSPITALDEPARTMENT_URL+hoscode);
修改syt/src/api/hospital/type.ts
//代表医院科室的数据
export interface Department{
"depcode": string,
"depname": string,
"children": Department[]
}
//代表存储科室数组类型
export type DepartmentArr = Department[]
//获取科室接口返回的数据类型
export interface DepartmentResponseData extends ResponseData{
data:DepartmentArr
}
修改syt/src/pages/hospital/index.vue
//获取某一个医院科室的数据
detailStore.getDepartment($route.query.hoscode as string);
修改syt/src/pages/hospital/register/index.vue
template
<!-- 放置每一个医院的科室的数据 -->
<div class="department">
<div class="leftNav">
<ul>
<li @click="changeIndex(index)" :class="{active:index===currentIndex}" v-for="(department,index) in hospitalStore.departmentArr" :key="department.depcode">
{{department.depname}}
</li>
</ul>
</div>
<div class="departmentInfo">
<!-- 用一个div代表:大科室与小科室 -->
<div class="showDepartment" v-for="(department,index) in hospitalStore.departmentArr" :key="department.depcode">
<h1 class="cur">{{department.depname}}</h1>
<!-- 每个大科室下的小科室 -->
<ul>
<li v-for="item in department.children" :key="item.depcode">{{item.depname}}</li>
</ul>
</div>
</div>
</div>
style
.department{
width: 100%;
height: 500px;
display: flex;
margin-top: 20px;
.leftNav{
width: 80px;
height: 100%;
ul{
width: 100%;
height: 100%;
background: rgb(248,248,248);
display: flex;
flex-direction: column;
li{
flex: 1;
text-align: center;
color: #7f7f7f;
font-size: 14px;
line-height: 40px;
&.active{
border-left: 1px solid red;
color: red;
background: white;
}
}
}
}
.departmentInfo{
flex: 1;
margin-left: 20px;
height: 100%;
overflow: auto;
&::-webkit-scrollbar{
display: none;
}
.showDepartment{
h1{
background: rgb(248,248,248);
color: #7f7f7f;
margin-top: 20px;
}
ul{
display: flex;
flex-wrap: wrap;
li{
color: #7f7f7f;
width: 33%;
margin-top: 20px;
}
}
}
}
}
script
//控制科室高亮的响应式数据
let currentIndex = ref<number>(0);
const changeIndex = (index:number)=>{
currentIndex.value = index;
//点击导航获取右侧科室(大科室的h1标题)
let allH1 = document.querySelectorAll('.cur');
//滚动到对应科室的位置
allH1[currentIndex.value].scrollIntoView({
behavior:'smooth',//过渡动画效果
block:'start'//滚动到位置,默认起始位置
})
完成login对话框的显示与隐藏P23
创建syt/src/components/login/index.vue
<script setup lang="ts">
import useUserStore from "@/store/modules/user.ts";
let userStore = useUserStore();
</script>
<script lang="ts">
export default {
name:'Login'
}
</script>
<template>
<div class="login_container">
<el-dialog title="用户登录" v-model="userStore.visible"></el-dialog>
</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/store/modules/user.ts
//定义用户相关的仓库
import {defineStore} from "pinia";
const useUserStore = defineStore('User',{
state:()=>{
return{
visible:false//用于控制登录组件的dialog显示与隐藏
}
},
actions:{
},
getters:{
}
})
export default useUserStore;
修改syt/src/components/hospital_top/index.vue
import useUserStore from "@/store/modules/user.ts";
let useStore = useUserStore();
const login = ()=>{
useStore.visible=true;
}
<p class="login" @click="login">登录/注册</p>
修改syt/src/pages/hospital/register/index.vue
import useUserStore from "@/store/modules/user.ts";
let useStore = useUserStore();
const showLogin=()=>{
useStore.visible=true;
}
<!-- 每个大科室下的小科室 -->
<ul>
<li v-for="item in department.children" :key="item.depcode" @click="showLogin">{{item.depname}}</li>
</ul>
登录组件静态搭建与场景的切换P24
修改syt/src/components/login/index.vue
<script setup lang="ts">
import useUserStore from "@/store/modules/user.ts";
import {User,Lock} from "@element-plus/icons-vue";
import {ref} from "vue";
let scene = ref<number>(0);//0代表手机号码登录 1代表微信扫码登录
//点击微信扫码登录|微信小图标切换为微信扫码登录
const changeScene = ()=>{
scene.value=1
}
let userStore = useUserStore();
</script>
<script lang="ts">
export default {
name:'Login'
}
</script>
<template>
<div class="login_container">
<el-dialog title="用户登录" v-model="userStore.visible">
<div class="content">
<el-row>
<el-col :span="12">
<div class="login">
<div v-show="scene==0">
<el-form>
<el-form-item>
<el-input placeholder="请输入手机号码" :prefix-icon="User"></el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入手机验证码" :prefix-icon="Lock"></el-input>
</el-form-item>
<el-form-item>
<el-button>获取验证码</el-button>
</el-form-item>
</el-form>
<el-button style="width: 100%" type="primary" size="default" >用户登录</el-button>
<div class="bottom" @click="changeScene">
<p>微信扫码登录</p>
<svg t="1722215745147" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10455" width="32" height="32"><path d="M337.387283 341.82659c-17.757225 0-35.514451 11.83815-35.514451 29.595375s17.757225 29.595376 35.514451 29.595376 29.595376-11.83815 29.595376-29.595376c0-18.49711-11.83815-29.595376-29.595376-29.595375zM577.849711 513.479769c-11.83815 0-22.936416 12.578035-22.936416 23.6763 0 12.578035 11.83815 23.676301 22.936416 23.676301 17.757225 0 29.595376-11.83815 29.595376-23.676301s-11.83815-23.676301-29.595376-23.6763zM501.641618 401.017341c17.757225 0 29.595376-12.578035 29.595376-29.595376 0-17.757225-11.83815-29.595376-29.595376-29.595375s-35.514451 11.83815-35.51445 29.595375 17.757225 29.595376 35.51445 29.595376zM706.589595 513.479769c-11.83815 0-22.936416 12.578035-22.936416 23.6763 0 12.578035 11.83815 23.676301 22.936416 23.676301 17.757225 0 29.595376-11.83815 29.595376-23.676301s-11.83815-23.676301-29.595376-23.6763z" fill="#28C445" p-id="10456"></path><path d="M510.520231 2.959538C228.624277 2.959538 0 231.583815 0 513.479769s228.624277 510.520231 510.520231 510.520231 510.520231-228.624277 510.520231-510.520231-228.624277-510.520231-510.520231-510.520231zM413.595376 644.439306c-29.595376 0-53.271676-5.919075-81.387284-12.578034l-81.387283 41.433526 22.936416-71.768786c-58.450867-41.433526-93.965318-95.445087-93.965317-159.815029 0-113.202312 105.803468-201.988439 233.803468-201.98844 114.682081 0 216.046243 71.028902 236.023121 166.473989-7.398844-0.739884-14.797688-1.479769-22.196532-1.479769-110.982659 1.479769-198.289017 85.086705-198.289017 188.67052 0 17.017341 2.959538 33.294798 7.398844 49.572255-7.398844 0.739884-15.537572 1.479769-22.936416 1.479768z m346.265896 82.867052l17.757225 59.190752-63.630058-35.514451c-22.936416 5.919075-46.612717 11.83815-70.289017 11.83815-111.722543 0-199.768786-76.947977-199.768786-172.393063-0.739884-94.705202 87.306358-171.653179 198.289017-171.65318 105.803468 0 199.028902 77.687861 199.028902 172.393064 0 53.271676-34.774566 100.624277-81.387283 136.138728z" fill="#28C445" p-id="10457"></path></svg>
</div>
</div>
<div class="webchat" v-show="scene==1">
</div>
</div>
</el-col>
<el-col :span="12">
<div class="leftContent">
<div class="top">
<div class="item">
<img src="../../assets/images/code_app.png" alt=""/>
<svg t="1722216391237" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10638" width="16" height="16"><path d="M0 0v1024h1024V0z m1004.307692 1004.307692H19.692308V19.692308h984.615384z" fill="#8a8a8a" p-id="10639"></path><path d="M415.113846 324.923077c-135.876923 24.024615-181.169231 177.624615-73.255384 248.123077 5.907692 3.544615 5.907692 3.150769-2.756924 29.538461l-7.876923 22.843077 27.569231-14.572307 27.569231-14.572308 14.178461 3.544615a244.184615 244.184615 0 0 0 48.049231 6.301539h7.483077l-2.756923-10.24c-21.267692-78.769231 52.381538-157.538462 148.48-157.538462h12.996923l-2.756923-9.058461a171.716923 171.716923 0 0 0-196.923077-105.55077z m-11.815384 68.923077a24.024615 24.024615 0 1 1-32.295385 6.695384 25.206154 25.206154 0 0 1 32.295385-6.695384z m115.790769 0a23.630769 23.630769 0 1 1-24.418462 0 26.781538 26.781538 0 0 1 24.418462 0z m52.381538 58.289231a124.849231 124.849231 0 0 0-111.458461 122.092307c3.544615 78.769231 95.704615 132.726154 184.32 108.307693l10.24-2.756923 22.055384 11.815384a175.261538 175.261538 0 0 0 22.449231 11.027692 96.886154 96.886154 0 0 0-5.12-18.904615c-7.089231-21.661538-7.089231-20.48 2.363077-27.175385 106.338462-76.406154 12.996923-229.612308-124.849231-204.406153z m-2.756923 63.40923a18.904615 18.904615 0 0 1 6.301539 20.873847 18.510769 18.510769 0 1 1-6.301539-20.873847z m94.129231 0a18.116923 18.116923 0 0 1-3.150769 29.538462 18.510769 18.510769 0 1 1 3.150769-29.538462z" fill="#8a8a8a" p-id="10640"></path></svg>
<p>微信扫一扫关注</p>
<p>快速预约挂号</p>
</div>
<div class="item">
<img src="../../assets/images/code_login_wechat.png" alt=""/>
<svg t="1722216639576" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11768" width="16" height="16"><path d="M736.653061 929.959184H287.346939c-45.97551 0-83.591837-37.616327-83.591837-83.591837V177.632653c0-45.97551 37.616327-83.591837 83.591837-83.591837h449.306122c45.97551 0 83.591837 37.616327 83.591837 83.591837v668.734694c0 45.97551-37.616327 83.591837-83.591837 83.591837zM287.346939 135.836735c-22.987755 0-41.795918 18.808163-41.795919 41.795918v668.734694c0 22.987755 18.808163 41.795918 41.795919 41.795918h449.306122c22.987755 0 41.795918-18.808163 41.795919-41.795918V177.632653c0-22.987755-18.808163-41.795918-41.795919-41.795918H287.346939z" fill="#333333" p-id="11769"></path><path d="M616.489796 815.020408H407.510204c-11.493878 0-20.897959-9.404082-20.897959-20.897959s9.404082-20.897959 20.897959-20.897959h208.979592c11.493878 0 20.897959 9.404082 20.897959 20.897959s-9.404082 20.897959-20.897959 20.897959z" fill="#333333" p-id="11770"></path></svg>
<p>扫一扫下载</p>
<p>预约挂号APP</p>
</div>
</div>
</div>
<p class="tip">尚医通官方指定平台</p>
<p class="tip">快速挂号 安全放心</p>
</el-col>
</el-row>
</div>
<template #footer>
<el-button type="primary" size="default">关闭窗口</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.login_container{
::v-deep(.el-dialog__body){
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.login{
padding: 20px;
border:1px solid #ccc;
}
.bottom{
display: flex;
flex-direction: column;
align-items: center;
p{
margin: 10px 0px;
}
}
.leftContent{
.top{
display: flex;
justify-content: space-around;
.item{
display: flex;
flex-direction: column;
align-items: center;
img{
width: 130px;
height: 130px;
}
p{
margin: 5px 0px;
}
}
}
}
.tip{
text-align: center;
margin: 20px 0px;
font-size: 20px;
font-weight: 900;
}
}
</style>
登录组件获取验证码P25
修改syt/src/api/hospital/index.ts
enum API{
HOSPITALDETAIL_URl='/hosp/hospital/',
//获取某一个医院的科室数据
HOSPITALDEPARTMENT_URL='/hosp/hospital/department/',
//获取验证码接口
GETUSERCODE_URL = '/sms/send/'
}
//获取验证码接口
export const reqCode=(phone:string)=>request.get(API.GETUSERCODE_URL+phone);
修改syt/src/store/modules/user.ts
将验证码存储仓库中
const useUserStore = defineStore('User',{
state:()=>{
return{
visible:false,//用于控制登录组件的dialog显示与隐藏
code:'',//存储用户验证码
}
},
actions:{
async getCode(phone:string){
let result:any = await reqCode(phone);
if(result.code==200){
this.code=result.data;
return 'ok';
}else{
return Promise.reject(new Error(result.message));
}
}
},
getters:{
}
})
修改syt/src/components/login/index.vue
template
<el-form-item>
<el-input placeholder="请输入手机号码" :prefix-icon="User" v-model="loginParam.phone"></el-input>
</el-form-item>
<el-form-item>
<el-input placeholder="请输入手机验证码" :prefix-icon="Lock" v-model="loginParam.code"></el-input>
</el-form-item>
<el-form-item>
<el-button :disabled="!isPhone?true:false" @click="getCode">获取验证码</el-button>
</el-form-item>
script
import {computed, reactive, ref} from "vue";
//收集表单数据
let loginParam = reactive({
phone:'',//收集手机号码
code:'' //收集验证码
});
//计算出当前表单元素收集的内容是否手机号码格式
let isPhone = computed(()=>{
const reg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/;
return reg.test(loginParam.phone);
});
const getCode= async()=>{
try {
await userStore.getCode(loginParam.phone);
loginParam.code = userStore.code;
}catch (error){
}
}
登录组件获取验证码倒计时业务P26
创建syt/src/components/countdaown/index.vue
<script setup lang="ts">
import {ref, watch} from "vue";
let time = ref<number>(5);
//接受父组件传递过来的props->flag:用于控制计数器组件显示与隐藏的
let props = defineProps(['flag']);
let $emit = defineEmits(['getFlag'])
watch(()=>props.flag,()=>{
let timer = setInterval(()=>{
time.value--;
if(time.value==0){
$emit('getFlag',false);
clearInterval(timer);
}
},1000)
},{
immediate:true,
})
</script>
<template>
<div>
<span>获取验证码({{time}}s)</span>
</div>
</template>
<style scoped lang="scss">
</style>
修改syt/src/components/login/index.vue
修改获取验证码按钮的显示:与倒计时组件切换显示
<el-form-item>
<el-button :disabled="!isPhone||flag?true:false">
<CountDown v-if="flag" :flag="flag" @getFlag="getFlag"/>
<span v-else @click="getCode">获取验证码</span>
</el-button>
</el-form-item>
增加计数器组件自定义事件
//计数器组件自定义事件
//当倒计时为零的时候,通知父组件倒计时组件隐藏
const getFlag = (val:boolean)=>{
//倒计时模式结束
flag.value = val;
}
完成登录业务P27
修改syt/src/components/login/index.vue
标签增加点击事件并加禁用条件
<el-button style="width: 100%" type="primary" size="default" @click="login" :disabled="!isPhone||loginParam.code.length<0?true:false">用户登录</el-button>
定义登录点击事件
import {ElMessage} from "element-plus";
const getCode= async()=>{
//解决element-plus按钮禁用还能点击的问题
if(!isPhone.value||flag.value){
return;
}
//开启倒计时模式,倒计时组件显示出来
flag.value=true;
try {
await userStore.getCode(loginParam.phone);
// loginParam.code = userStore.code;
}catch (error){
ElMessage({
type: 'error',
message: (error as Error).message
});
}
}
const login= async ()=>{
try{
await userStore.userLogin(loginParam);
userStore.visible=false
}catch (error) {
ElMessage({
type:'error',
message:(error as Error).message
})
}
}
修改syt/src/api/hospital/index.ts
增加登录路由
enum API{
HOSPITALDETAIL_URl='/hosp/hospital/',
//获取某一个医院的科室数据
HOSPITALDEPARTMENT_URL='/hosp/hospital/department/',
//获取验证码接口
// GETUSERCODE_URL = '/sms/send/',
GETUSERCODE_URL = '/msm/send/',//本地
//用户登录接口
UERLOGIN_URL = '/user/login'
}
//用户登录接口
export const reqUserLogin = (data:LoginData)=>request.post<any,UserLoginResponseData>(API.UERLOGIN_URL,data);
修改syt/src/api/hospital/type.ts
//用户登录接口需要携带参数类型
export interface LoginData{
phone:string,
code:string
}
//登录接口返回用户信息数据
export interface UserInfo{
name:string,
token:string
}
//登录接口返回的数据类型
export interface UserLoginResponseData extends ResponseData{
data:UserInfo
}
修改syt/src/store/modules/interface/index.ts
//用户仓库相关state数据ts类型定义
export interface UserState{
visible:boolean, //用于控制登录组件的dialog显示与隐藏
code:string, //存储用户的验证码
userInfo:UserInfo
}
创建syt/src/utils/user.ts
将用户信息存储本地
//本地存储操作用户信息
export const SET_TOKEN =(userInfo:string)=>{
localStorage.setItem('USERINFO',userInfo);
}
export const GET_TOKEN=()=>{
return localStorage.getItem('USERINFO')
}
修改syt/src/store/modules/user.ts
import {GET_TOKEN, SET_TOKEN} from "@/utils/user.ts";
state中增加userInfo
// userInfo:({} as UserInfo)
userInfo:JSON.parse(GET_TOKEN() as string)||{}
actions中增加登录方法
//用户手机号码登录方法
async userLogin(loginData:LoginData){
let result:UserLoginResponseData = await reqUserLogin(loginData);
if(result.code==200){
this.userInfo = result.data;
//本地存储持久化存储用户信息
SET_TOKEN(JSON.stringify(this.userInfo))
return 'ok';
}else {
return Promise.reject(new Error(result.message));
}
}
修改syt/src/components/hospital_top/index.vue
标签增加点击事件,登录成功显示用户名,并增加下拉菜单
<p class="login" @click="login" v-if="!useStore.userInfo?.name">登录/注册</p>
<el-dropdown v-else>
<span class="el-dropdown-link">
{{useStore.userInfo.name}}
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>实名认证</el-dropdown-item>
<el-dropdown-item>挂号订单</el-dropdown-item>
<el-dropdown-item>就诊人管理</el-dropdown-item>
<el-dropdown-item>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
点击登录显示登录弹出
const login = ()=>{
useStore.visible=true;
}
登录模块表单校验P28
修改syt/src/components/login/index.vue
Form增加model及rules属性
<el-form :model="loginParam" :rules="rules">
FormItem 增加prop属性
<el-form-item prop="phone">
<el-input placeholder="请输入手机号码" :prefix-icon="User" v-model="loginParam.phone"></el-input>
</el-form-item>
<el-form-item prop="code">
<el-input placeholder="请输入手机验证码" :prefix-icon="Lock" v-model="loginParam.code"></el-input>
</el-form-item>
定义rules
const rules = {
//手机号码校验规则
//required:代表当前字段务必进行校验
//message:代表的校验错误的提示信息
//trigger:代表表单校验触发的时机 change:文本发生变化的时候进行校验 blur:失却焦点的时候触发校验
//min:代表的是最小位数
//max:代表的是最大的位置
phone: [{required: true, message: "手机号码务必11位", trigger: "change", min: 11}],
code: [{required: true, message: "验证码务必6位", trigger: "blur", min: 6}]
}
登录模块表单自定义校验规则P29
修改syt/src/components/login/index.vue
rules修改为自定义校验规则
const rules = {
phone: [{ validator: validatorPhone, trigger: 'change' }],
code: [{ validator: validatorCode, trigger: 'blur' }],
}
自定义校验规则
//自定义校验规则:手机号码自定义校验规则
const validatorPhone = (rule: any, value: any, callback: any) => {
//rule:表单校验规则对象
//value:当前文本内容
//callback:回调函数
const reg = /^1(3[0-9]|4[01456879]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\d{8}$/;
if(reg.test(value)){
callback();
}else{
callback(new Error('请输入正确的手机号码格式'))
}
}
const validatorCode = (rule: any, value: any, callback: any) => {
if(/^\d{6}$/.test(value)){
callback();
}else{
callback(new Error('请数据正确的验证码格式'))
}
}
表单校验不通过验证
//获取表单组件
let form = ref<any>();
<el-form :model="loginParam" :rules="rules" ref="form">
登录前进行全表单验证
const login = async () => {
await form.value.validate();
try {
await userStore.userLogin(loginParam);
userStore.visible = false
} catch (error) {
ElMessage({
type: 'error',
message: (error as Error).message
})
}
}
登录模块清空数据与表单校验结果P30
修改syt/src/components/login/index.vue
清空数据
方法1:
在dialog标签增加close属性
<el-dialog title="用户登录" v-model="userStore.visible" @close="close">
定义close方法,在close中将数据清空
const close=()=>{
//清空收集的数据
Object.assign(loginParam,{phone:'',code:''});
//清除上一次校验的结果
form.value.resetFields();
}
弹窗关闭按钮实现
<el-button type="primary" size="default" @click="closeDialog">关闭窗口</el-button>
const closeDialog =()=>{
close();
userStore.visible = false
}
方法2:销毁登录组件
修改syt/src/App.vue
<script setup lang="ts">
import useUserStore from "@/store/modules/user.ts";
let userStore = useUserStore();
</script>
<!-- 登录组件 -->
<Login v-if="userStore.visible"/>
退出登录业务P31
修改syt/src/components/hospital_top/index.vue
退出登录标签增加点击事件
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>实名认证</el-dropdown-item>
<el-dropdown-item>挂号订单</el-dropdown-item>
<el-dropdown-item>就诊人管理</el-dropdown-item>
<el-dropdown-item @click="logout">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
定义退出登录方法
const logout =()=>{
useStore.userLogout();
$router.push({path:'/home'})
}
修改syt/src/store/modules/user.ts
定义退出登录,将仓库及本地用户信息清除
//用户退出登录
async userLogout(){
this.userInfo = {name:'',token:''};
REMOVE_TOKEN();
}
修改syt/src/utils/user.ts
定义清除本地用户信息
export const REMOVE_TOKEN=()=>{
return localStorage.removeItem('USERINFO')
}
微信扫码登录P34
修改syt/index.html
<!-- 引入微信扫码登录需要核心插件wxlogin.js -->
<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
修改syt/src/api/hospital/type.ts
//定义微信扫码登录返回的数据的ts类型
export interface WXLogin {
"redirectUri": string,
"appid": string,
"scope": string,
"state": string
}
export interface WXLoginResponseData extends ResponseData{
data:WXLogin
}
修改syt/src/api/hospital/index.ts
enum API{
HOSPITALDETAIL_URl='/hosp/hospital/',
//获取某一个医院的科室数据
HOSPITALDEPARTMENT_URL='/hosp/hospital/department/',
//获取验证码接口
// GETUSERCODE_URL = '/sms/send/',
GETUSERCODE_URL = '/msm/send/',//本地
//用户登录接口
UERLOGIN_URL = '/user/login',
//获取微信扫码登录需要参数
// WXLOGIN_URL='/user/weixin/getLoginParam'
WXLOGIN_URL='/ucenter/wx/getLoginParam' //本地
}
//获取微信扫码登录生成二维码需要参数接口
export const reqWxLogin = (wxRedirectUri:string)=>request.get<any,WXLoginResponseData>(API.WXLOGIN_URL+`?wxRedirectUri`+wxRedirectUri);
修改syt/src/components/login/index.vue
div
<div class="webchat" v-show="scene==1">
<div id="login_container"></div>
<div class="phone" @click="handler">
<p>手机短信验证码登录</p>
<svg t="1722590738044" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2633" width="16" height="16"><path d="M820.409449 797.228346q0 25.19685-10.07874 46.866142t-27.716535 38.299213-41.322835 26.204724-50.897638 9.574803l-357.795276 0q-27.212598 0-50.897638-9.574803t-41.322835-26.204724-27.716535-38.299213-10.07874-46.866142l0-675.275591q0-25.19685 10.07874-47.370079t27.716535-38.80315 41.322835-26.204724 50.897638-9.574803l357.795276 0q27.212598 0 50.897638 9.574803t41.322835 26.204724 27.716535 38.80315 10.07874 47.370079l0 675.275591zM738.771654 170.330709l-455.559055 0 0 577.511811 455.559055 0 0-577.511811zM510.992126 776.062992q-21.165354 0-36.787402 15.11811t-15.622047 37.291339q0 21.165354 15.622047 36.787402t36.787402 15.622047q22.173228 0 37.291339-15.622047t15.11811-36.787402q0-22.173228-15.11811-37.291339t-37.291339-15.11811zM591.622047 84.661417q0-8.062992-5.03937-12.598425t-11.086614-4.535433l-128 0q-5.03937 0-10.582677 4.535433t-5.543307 12.598425 5.03937 12.598425 11.086614 4.535433l128 0q6.047244 0 11.086614-4.535433t5.03937-12.598425z" p-id="2634"></path></svg>
</div>
</div>
</div>
ts
//点击微信扫码登录|微信小图标切换为微信扫码登录
const changeScene = async () => {
scene.value = 1
let redirect_URL = encodeURIComponent(window.location.origin+'/wxlogin')
let result:WXLoginResponseData = await reqWxLogin(redirect_URL);
//生产微信扫码二维码管理页面
let obj = new WxLogin({
self_redirect:true,
id: 'login_container', // 需要显示的容器id
appid: result.data.appid, // 公众号appid wx*******
scope: result.data.scope, // 网页默认即可
redirect_uri: result.data.redirectUri, // 授权成功后回调的url
state: result.data.state, // 可设置为简单的随机数加session用来校验
style: 'black', // 提供"black"、"white"可选。二维码的样式
href: '' // 外部css文件url,需要https
})
}
scss
.login {
padding: 20px;
border: 1px solid #ccc;
.webchat{
display: flex;
flex-direction: column;
align-items: center;
.phone{
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
p{
margin: 10px 0px;
}
}
}
}
完成微信扫码登录业务P35
创建syt/src/pages/wxlogin/index.vue
<script setup lang="ts">
import {useRoute} from "vue-router";
import {SET_TOKEN} from "@/utils/user.ts";
let $route = useRoute();
//持久化存储用户信息
SET_TOKEN(JSON.stringify($route.query));
let html:any = document.querySelector('html');
html.style.display='none';
</script>
<template>
<div></div>
</template>
<style scoped lang="scss">
</style>
修改syt/src/router/index.ts
增加微信登录成功重定向路由
{
path:'/wxlogin',
component:()=>import(('@/pages/wxlogin/index.vue'))
},
修改syt/src/components/login/index.vue
增加监听
import {computed, reactive, ref, watch} from "vue";
//监听场景的数据
watch(()=>scene.value,(val:number)=>{
if(val===1){
userStore.queryState();
}
})
修改syt/src/store/modules/user.ts
//查询微信扫码的接口(看本地存储是否拥有用户信息)
queryState(){
let timer = setInterval(()=>{
if( GET_TOKEN()){
this.visible=false;
this.userInfo=JSON.parse(GET_TOKEN() as string);
clearInterval(timer);
}
},1000)
}
预约挂号的路由与静态的搭建P36
创建syt/src/pages/hospital/register/register_step1.vue
<script setup lang="ts">
</script>
<template>
<div class="wrap">
<div class="top">
<div class="hosname">北京XX医院</div>
<div class="line"></div>
<div>专科</div>
<div class="dot">.</div>
<div class="hosdepatment">多发性硬化专科门诊</div>
</div>
<dv class="center">
<h1 class="time">2023年6月3日</h1>
<div class="container">
<div class="item" v-for="item in 6" :key="item">
<div class="item_top">123</div>
<div class="item_bottom">456</div>
</div>
</div>
<el-pagination layout="prev,pager,next" :total="50"/>
</dv>
</div>
</template>
<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;
.item{
font-weight: 900;
}
.container{
display: flex;
width: 100%;
margin: 30px 0px;
.item{
flex: 1;
border:1px solid skyblue;
margin: 0px 5px;
display: flex;
flex-direction: column;
align-items: center;
.item_top{
background: #e8f2ff;
height: 30px;
width: 100%;
text-align: center;
line-height: 30px;
}
.item_bottom{
width: 100%;
height: 60px;
text-align: center;
line-height: 60px;
}
}
}
}
}
</style>
修改syt/src/router/index.ts
{
path:'register_step1',
component:()=>import(('@/pages/hospital/register/register_step1.vue'))
}
修改syt/src/pages/hospital/register/index.vue
<!-- 每个大科室下的小科室 -->
<ul>
<li v-for="item in department.children" :key="item.depcode" @click="showLogin(item)">{{item.depname}}</li>
</ul>
const showLogin=(item:any)=>{
// useStore.visible=true;
$router.push({path:'/hospital/register_step1',query:{hoscode:$route.query.hoscode,depcode:item.depcode}})
}
展示预约挂号数据P37
修改syt/src/api/hospital/index.ts
enum API{
HOSPITALDETAIL_URl='/hosp/hospital/',
//获取某一个医院的科室数据
HOSPITALDEPARTMENT_URL='/hosp/hospital/department/',
//获取验证码接口
// GETUSERCODE_URL = '/sms/send/',
GETUSERCODE_URL = '/msm/send/',//本地
//用户登录接口
UERLOGIN_URL = '/user/login',
//获取微信扫码登录需要参数
// WXLOGIN_URL='/user/weixin/getLoginParam'
WXLOGIN_URL='/ucenter/wx/getLoginParam', //本地
HOSPITALWORK_URL='/hosp/hospital/auth/getBookingScheduleRule/',
}
//获取预约挂号的数据
export const reqHosptalWork=(page:number,limit:number,hoscode:string,depcode:string)=>request.get<any,HospitalWorkData>(API.HOSPITALWORK_URL+`${page}/${limit}/${hoscode}/${depcode}`)
修改syt/src/api/hospital/type.ts
export interface BaseMap{
"workDateString": string,
"releaseTime": string,
"bigname": string,
"stopTime": string,
"depname": string,
"hosname": string
}
export interface WorkData{
"workDate": string,
"workDateMd": string,
"dayOfWeek": string,
"docCount": number,
"reservedNumber": null,
"availableNumber": number,
"status": number
}
export type BookingScheduleList = WorkData[]
export interface HospitalWorkData extends ResponseData{
data:{
total:number,
bookingScheduleList:BookingScheduleList,
baseMap:BaseMap
}
}
修改syt/src/pages/hospital/register/register_step1.vue
<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="hosdepatment">{{workData.baseMap?.depname}}</div>
</div>
<dv 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="item_top">{{ item.workDate }}-{{item.dayOfWeek}}</div>
<div class="item_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"/>
</dv>
</div>
</template>
<script setup lang="ts">
import {reqHosptalWork} from "@/api/hospital";
import {onMounted, ref} from "vue";
import {useRoute} from "vue-router";
import {HospitalWorkData} from "@/api/hospital/type.ts";
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 reqHosptalWork(pageNo.value,limit.value,$route.query.hoscode as string,$route.query.depcode as string);
if(result.code===200){
workData.value = result.data;
}
}
</script>
scss
.item{
flex: 1;
border:1px solid skyblue;
margin: 0px 5px;
display: flex;
flex-direction: column;
align-items: center;
&.active{
border: 1px solid #ccc;
color: #7f7f7f;
.item_top{
background: #ccc;
}
}
预约挂号底部上下午医生结构的搭建P38
修改syt/src/pages/hospital/register/register_step1.vue
templa
<div class="bottom">
<!-- 展示即将放号的时间 -->
<div class="will">
<span class="time">2023年6月3日08:30</span>
<span class="willText">放号</span>
</div>
<!-- 展示医生的结构 -->
<div class="doctor">
<div class="morning">
<!-- 顶部文字提示 -->
<div class="tip">
<svg t="1722869133535" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3749" width="32" height="16"><path d="M904.101037 676.946871H118.670712c-15.461511 0-28.105271-12.535384-28.105271-28.033021 0-15.461511 12.643759-28.105271 28.105271-28.10527h785.430325c15.461511 0 28.105271 12.643759 28.105271 28.10527s-12.607634 28.033021-28.105271 28.033021z m-56.029916-324.980738c-11.018133 10.945883-28.719396 10.945883-39.665279 0-11.018133-10.945883-11.018133-28.755521 0-39.665279l39.665279-39.665279a27.97522 27.97522 0 0 1 39.593029 0 27.93187 27.93187 0 0 1 0 39.665279l-39.593029 39.665279z m-62.279545 212.776406c-26.082269-128.027094-138.683977-224.444789-274.441826-224.44479-135.649474 0-248.287307 96.417696-274.369576 224.44479H177.915755c21.602766-158.263741 162.454244-280.583081 333.433995-280.583081 171.088125 0 311.903478 122.31934 333.506244 280.583081h-59.064418z m-274.441826-336.612997c-15.461511 0-28.033021-12.535384-28.033021-28.10527V143.95823c0-15.461511 12.535384-28.105271 28.033021-28.10527 15.569886 0 28.105271 12.643759 28.10527 28.10527v56.029916c0 15.606011-12.535384 28.141396-28.10527 28.141396zM174.700628 351.966133L135.035349 312.264729c-10.945883-10.945883-10.945883-28.755521 0-39.665279 11.018133-10.945883 28.719396-10.945883 39.665279 0l39.665279 39.665279a27.93187 27.93187 0 0 1 0 39.665279c-10.909758 10.982008-28.647146 10.982008-39.665279 0.036125z m-28.033021 381.119029h729.40041c15.497636 0 28.033021 12.535384 28.03302 28.03302a28.033021 28.033021 0 0 1-28.03302 28.033021H146.667607c-15.497636 0-28.033021-12.535384-28.03302-28.033021 0.036125-15.497636 12.571509-28.033021 28.03302-28.03302z m84.171312 112.168207h561.093911c15.497636 0 28.033021 12.535384 28.033021 28.033021 0 15.569886-12.535384 28.105271-28.033021 28.10527H230.838919c-15.497636 0-28.033021-12.535384-28.03302-28.10527 0-15.497636 12.535384-28.033021 28.03302-28.033021z" fill="#1296db" p-id="3750"></path></svg>
<span class="text">上午号源</span>
</div>
<!-- 每一个医生的信息 -->
<div class="doc_info">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>副主任医生</span>
<span>|</span>
<span>贾成豪</span>
</div>
<div class="skill">骨质疏松和骨代谢疾病、糖尿病、甲状腺疾病</div>
</div>
<!-- 右侧区域展示挂号的费用 -->
<div class="right">
<div class="money">¥100</div>
<el-button type="primary" size="default">100</el-button>
</div>
</div>
<div class="doc_info">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>副主任医生</span>
<span>|</span>
<span>贾成豪</span>
</div>
<div class="skill">骨质疏松和骨代谢疾病、糖尿病、甲状腺疾病</div>
</div>
<!-- 右侧区域展示挂号的费用 -->
<div class="right">
<div class="money">¥100</div>
<el-button type="primary" size="default">100</el-button>
</div>
</div>
</div>
<div class="morning">
<!-- 顶部文字提示 -->
<div class="tip">
<svg t="1722870959935" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5200" width="32" height="32"><path d="M356.9664 562.7392H762.88a61.44 61.44 0 0 1 61.44 61.44v153.6H295.7824v-153.6a61.44 61.44 0 0 1 61.184-61.44z" fill="#FFAECF" p-id="5201"></path><path d="M431.872 383.6416h256a61.44 61.44 0 0 1 61.44 61.44v117.76H370.6368v-117.76a61.44 61.44 0 0 1 61.2352-61.44z" fill="#FFAECF" p-id="5202"></path><path d="M945.152 831.5392H78.848a15.36 15.36 0 0 1 0-30.72h866.304a15.36 15.36 0 0 1 0 30.72z" fill="#8E5178" p-id="5203"></path><path d="M661.1456 998.4H362.8544a15.36 15.36 0 0 1-15.36-15.36v-35.0208a32.9728 32.9728 0 0 1 32.9216-32.9216 82.3808 82.3808 0 0 0 82.2784-82.2784v-16.384a15.36 15.36 0 0 1 15.36-15.36h67.8912a15.36 15.36 0 0 1 15.36 15.36v16.384a82.3808 82.3808 0 0 0 82.2784 82.2784 32.9728 32.9728 0 0 1 32.9216 32.9216V983.04a15.36 15.36 0 0 1-15.36 15.36z m-282.9312-30.72h267.5712v-19.6608a2.2016 2.2016 0 0 0-2.2016-2.2016 113.1008 113.1008 0 0 1-112.9984-112.9984v-1.024h-37.1712v1.024a113.1008 113.1008 0 0 1-112.9984 112.7424 2.2016 2.2016 0 0 0-2.2016 2.2016z" fill="#8E5178" p-id="5204"></path><path d="M776.0384 831.5392H247.9616a15.36 15.36 0 0 1-15.36-15.36v-153.6a76.8 76.8 0 0 1 76.8-76.8h405.6064a76.8 76.8 0 0 1 76.8 76.8v153.6a15.36 15.36 0 0 1-15.7696 15.36z m-512.7168-30.72h497.3568v-138.24a46.08 46.08 0 0 0-46.08-46.08H309.1968a46.08 46.08 0 0 0-46.08 46.08z" fill="#8E5178" p-id="5205"></path><path d="M701.1328 616.5504H322.8672a15.36 15.36 0 0 1-15.36-15.36v-117.76a76.8 76.8 0 0 1 76.8-76.8h256a76.8 76.8 0 0 1 76.8 76.8v117.76a15.36 15.36 0 0 1-15.9744 15.36z m-362.9056-30.72h347.5456v-102.4a46.08 46.08 0 0 0-46.08-46.08H384a46.08 46.08 0 0 0-46.08 46.08z" fill="#8E5178" p-id="5206"></path><path d="M641.3312 437.4528H382.6688a15.36 15.36 0 0 1-15.36-15.36V336.2304a76.8 76.8 0 0 1 76.8-76.8h136.192a76.8 76.8 0 0 1 76.8 76.8v85.8624a15.36 15.36 0 0 1-15.7696 15.36z m-243.3024-30.72h227.9424V336.2304a46.08 46.08 0 0 0-46.08-46.08H443.904a46.08 46.08 0 0 0-46.08 46.08z" fill="#8E5178" p-id="5207"></path><path d="M845.5168 831.5392H178.4832a15.36 15.36 0 0 1-15.36-15.36V374.7328a348.8768 348.8768 0 0 1 697.7536 0v441.4464a15.36 15.36 0 0 1-15.36 15.36z m-651.6736-30.72h636.3136V374.7328a318.1568 318.1568 0 0 0-636.3136 0z" fill="#8E5178" p-id="5208"></path><path d="M512 290.3552a15.36 15.36 0 0 1-10.24-3.6352c-2.1504-1.8944-53.504-46.6432-74.6496-77.5168a69.5808 69.5808 0 0 1-5.9392-71.68 58.368 58.368 0 0 1 52.5824-31.1296 51.2 51.2 0 0 1 38.0416 15.36 51.2 51.2 0 0 1 38.0416-15.36 58.2144 58.2144 0 0 1 52.5312 31.1296 69.4272 69.4272 0 0 1-5.888 71.68c-21.1456 30.72-72.4992 75.6224-74.6496 77.5168a15.36 15.36 0 0 1-9.8304 3.6352z m-38.0416-153.2416a27.392 27.392 0 0 0-25.6 14.7456 38.9632 38.9632 0 0 0 4.096 39.8848c13.6704 19.968 43.6736 48.3328 59.392 62.6688 15.7184-14.336 45.7216-42.7008 59.392-62.6688a38.9632 38.9632 0 0 0 4.096-39.8848 27.392 27.392 0 0 0-25.6-14.7456 22.9376 22.9376 0 0 0-22.9888 18.8416 15.36 15.36 0 0 1-15.36 12.2368 15.36 15.36 0 0 1-14.9504-12.4416 22.8864 22.8864 0 0 0-22.4768-18.6368zM600.576 537.1392a44.9536 44.9536 0 0 1-32.5632-14.1312c-5.12-4.6592-6.9632-6.144-11.8784-6.144s-6.7584 1.4848-11.8272 6.144a44.5952 44.5952 0 0 1-65.1264 0c-5.12-4.6592-6.9632-6.144-11.8784-6.144s-6.7584 1.4848-11.8272 6.144a44.5952 44.5952 0 0 1-65.1264 0c-5.12-4.6592-6.9632-6.144-11.8784-6.144a15.36 15.36 0 0 1 0-30.72 44.9536 44.9536 0 0 1 32.5632 14.1312c5.12 4.6592 6.9632 6.144 11.8784 6.144s6.7584-1.4848 11.8272-6.144a44.5952 44.5952 0 0 1 65.1264 0c5.12 4.6592 6.9632 6.144 11.8784 6.144s6.7584-1.4848 11.8272-6.144a44.5952 44.5952 0 0 1 65.1264 0c5.12 4.6592 6.9632 6.144 11.8784 6.144s6.7584-1.4848 11.8784-6.144A44.8512 44.8512 0 0 1 645.12 486.4a15.36 15.36 0 0 1 0 30.72c-5.12 0-6.7584 1.4848-11.8784 6.144a44.8512 44.8512 0 0 1-32.6656 13.8752z" fill="#8E5178" p-id="5209"></path></svg>
<span class="text">下午号源</span>
</div>
<!-- 每一个医生的信息 -->
<div class="doc_info">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>副主任医生</span>
<span>|</span>
<span>贾成豪</span>
</div>
<div class="skill">骨质疏松和骨代谢疾病、糖尿病、甲状腺疾病</div>
</div>
<!-- 右侧区域展示挂号的费用 -->
<div class="right">
<div class="money">¥100</div>
<el-button type="primary" size="default">100</el-button>
</div>
</div>
<div class="doc_info">
<!-- 展示医生的名字|技能 -->
<div class="left">
<div class="info">
<span>副主任医生</span>
<span>|</span>
<span>贾成豪</span>
</div>
<div class="skill">骨质疏松和骨代谢疾病、糖尿病、甲状腺疾病</div>
</div>
<!-- 右侧区域展示挂号的费用 -->
<div class="right">
<div class="money">¥100</div>
<el-button type="primary" size="default">100</el-button>
</div>
</div>
</div>
</div>
</div>
scss
.bottom{
.will{
text-align: center;
font-size: 30px;
font-weight: 900;
.time{
color: red;
}
.willText{
color: skyblue;
}
}
.doctor{
.morning{
.tip{
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{
width: 150px;
display: flex;
justify-content: space-between;
align-items: center;
.money{
color: #7f7f7f;
font-weigt: 900;
}
}
}
}
}
}
预约挂号底部医生排班业务P39
修改syt/src/api/hospital/index.ts
//获取医院某一个科室某一天相应医生排班的数据
HOSPITALDOCTOR_URL = '/hosp/hospital/auth/findScheduleList/'
//获取医生排班的数据
export const reqHospitalDoctor = (hoscode:string,depcode:string,workDate:string)=>request.get<any,DoctorResponseData>(API.HOSPITALDOCTOR_URL+`${hoscode}/${depcode}/${workDate}`)
修改syt/src/api/hospital/type.ts
//代表的是一个医生的数据
export interface Doctor{
"id": string,
"createTime": string,
"updateTime": string,
"isDeleted": 0,
"param": {
"dayOfWeek": string,
"depname": string,
"hosname": string
},
"hoscode": string,
"depcode": string,
"title": string,
"docname": string,
"skill": string,
"workDate": string,
"workTime": number,
"reservedNumber": number,
"availableNumber": number,
"amount": number,
"status": number,
"hosScheduleId": string
}
//数组包含全部医生
export type DocArr = Doctor[];
//获取医生排班接口返回的数据
export interface DoctorResponseData extends ResponseData{
data:DocArr
}
修改syt/src/pages/hospital/register/register_step1.vue
ts
import {computed, onMounted, ref} from "vue";
import {DocArr, Doctor, DoctorResponseData, HospitalWorkData} from "@/api/hospital/type.ts";
let workData=ref<any>({});
let workTime :any = ref<any>({})
let docArr = ref<DocArr>();
const fetchWorkData = async ()=>{
let result:HospitalWorkData = await reqHosptalWork(pageNo.value,limit.value,$route.query.hoscode as string,$route.query.depcode as string);
if(result.code===200){
workData.value = result.data;
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);
if(result.code===200){
docArr.value = result.data;
}
}
const changeTime = (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
})
})
templa
<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="item_top">{{ item.workDate }}-{{item.dayOfWeek}}</div>
<div class="item_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 class="will" v-if="workTime.status==1">
<span class="time">2023年6月3日08:30</span>
<span class="willText">放号</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" size="default">{{doctor.availableNumber}}</el-button>
</div>
</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" size="default">{{doctor.availableNumber}}</el-button>
</div>
</div>
scss
&.active{
border: 1px solid #ccc;
color: #7f7f7f;
.item_top{
background: #ccc;
}
}
&.cur{
transform: scale(1.1);
}
预约挂号就诊人静态搭建P40
创建syt/src/pages/hospital/register/register_step2.vue
<script setup lang="ts">
import {User} from '@element-plus/icons-vue'
import Visiter from "@/pages/hospital/register/visiter.vue";
</script>
<template>
<div class="container">
<h1 class="tip">确认挂号信息</h1>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>Card name</span>
<el-button size="default" type="primary" :icon="User">添加就诊人</el-button>
</div>
</template>
<div class="user">
<Visiter v-for="item in 4" :key="item" class="item"/>
</div>
</el-card>
</div>
</template>
<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;
}
}
}
}
</style>
创建syt/src/pages/hospital/register/visiter.vue
<script setup lang="ts">
import {Edit} from "@element-plus/icons-vue";
</script>
<template>
<div class="visitor">
<div class="top">
<div class="left">
<span class="free">医保</span>
<span class="username">贾成豪</span>
</div>
<div class="right">
<el-button type="primary" size="default" :icon="Edit"></el-button>
</div>
</div>
<div class="bottom">
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
<p>证件类型:身份证</p>
</div>
</div>
</template>
<style scoped lang="scss">
.visitor{
.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: 10px;
border-radius: 10px;
}
.username{
color: #7f7f7f;
}
}
}
.bottom{
padding: 20px;
p{
line-height: 40px;
}
}
}
</style>
修改syt/src/router/index.ts
{
path:'register_step2',
component:()=>import(('@/pages/hospital/register/register_step2.vue'))
}
修改syt/src/pages/hospital/register/register_step1.vue
//上午号源按钮
<el-button type="primary" size="default" @click="goStep2(doctor)">{{doctor.availableNumber}}</el-button>
//下午号源按钮
<el-button type="primary" size="default" @click="goStep2(doctor)">{{doctor.availableNumber}}</el-button>
import {useRoute, useRouter} from "vue-router";
let $router = useRouter();
// 路由跳转进入就诊人页面
const goStep2 =(doctor:Doctor)=>{
$router.push({path:'/hospital/register_step2',query:{docId:doctor.id}})
}
预约挂号就诊人静态搭建下P41
修改syt/src/pages/hospital/register/register_step2.vue
<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>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊医院:
</div>
</template>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊科室:
</div>
</template>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生姓名:
</div>
</template>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生职称:
</div>
</template>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生专长:
</div>
</template>
xxxxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生服务费:
</div>
</template>
<span style="color: red">xxxxxx</span>
</el-descriptions-item>
</el-descriptions>
</el-card>
<div class="btn">
<el-button type="primary" size="default">确认挂号</el-button>
</div>
.btn{
display: flex;
justify-content: center;
margin: 10px 0px;
}
获取当前账号下就诊人信息P42
修改syt/src/api/hospital/type.ts
export interface Patient{
address:string,
birthdate:string,
cardNo:null,
certificatesNo:string,
certificatesType:string,
cityCode:string,
contactsCertificatesNo:string,
contactsCertificatesType:string,
contactsName:string,
contactsPhone:string,
createTime:string,
districtCode:string,
id:number,
isDeleted:number,
isInsure:number,
isMarry:number,
name:string,
param:{
certificatesTypeString:string,
contactsCertificatesTypeString:string,
cityString:string,
fullAddress:string,
districtString:string,
provinceString:string,
}
phone:string,
provinceCode:string,
sex:number
status:string,
updateTime:string,
userId:number
}
export type PatientArr = Patient[];
//获取就诊人接口返回的数据
export interface PatientResponseData extends ResponseData{
data:PatientArr
}
修改syt/src/api/hospital/index.ts
PATIENTS_URL = '/user/patient/auth/findAll'
//获取就诊人数据
export const reqPatient = ()=>request.get<any,PatientResponseData>(API.PATIENTS_URL)
修改syt/src/pages/hospital/register/register_step2.vue
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>请选择就诊人</span>
<el-button size="default" type="primary" :icon="User">添加就诊人</el-button>
</div>
</template>
<div class="user">
<Visiter v-for="patient in patientArr" :key="patient.id" class="item" :patient="patient"/>
</div>
</el-card>
<script setup lang="ts">
import {User} from '@element-plus/icons-vue'
import Visiter from "@/pages/hospital/register/visiter.vue";
import {onMounted, ref} from "vue";
import {reqPatient} from "@/api/hospital";
import {PatientArr, PatientResponseData} from "@/api/hospital/type.ts";
let patientArr = ref<PatientArr>();
onMounted(()=>{
fetchPatietnt();
})
const fetchPatietnt= async ()=>{
let result:PatientResponseData = await reqPatient();
if(result.code==200){
patientArr.value=result.data;
}
}
</script>
修改syt/src/pages/hospital/register/visiter.vue
<template>
<div class="visitor">
<div class="top">
<div class="left">
<span class="free">{{patient.isInsure==1?'医保':'自费'}}</span>
<span class="username">{{patient.name}}</span>
</div>
<div class="right">
<el-button type="primary" size="default" :icon="Edit"></el-button>
</div>
</div>
<div class="bottom">
<p>证件类型:{{patient.param.certificatesTypeString}}</p>
<p>证件号码:{{patient.certificatesNo}}</p>
<p>用户性别:{{patient.sex==0?'女生':'男士'}}</p>
<p>出生日期:{{patient.birthdate}}</p>
<p>手机号码:{{patient.phone}}</p>
<p>婚姻状况:{{patient.isMarry==0?'未婚':'已婚'}}</p>
<p>当前住址:{{patient.param.cityString}}</p>
<p>详细地址:{{patient.param.fullAddress}}</p>
</div>
</div>
</template>
<script setup lang="ts">
import {Edit} from "@element-plus/icons-vue";
//获取父组件信息
defineProps(['patient'])
</script>
获取挂号医生的信息展示P43
修改syt/src/api/hospital/index.ts
//获取医生信息
SCHEDULE_URL = '/hosp/hospital/getScheduleById/'
export const reqSchedule = (scheduleId:string)=>request.get<any,ScheduleResponseData>(API.SCHEDULE_URL+`${scheduleId}`)
修改syt/src/api/hospital/type.ts
export interface ScheduleResponseData extends ResponseData{
data:Doctor
}
修改syt/src/pages/hospital/register/register_step2.vue
<el-descriptions
class="margin-top"
:column="2"
border
>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊日期:
</div>
</template>
{{schedule?.workDate}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊医院:
</div>
</template>
{{schedule?.param?.hosname}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊科室:
</div>
</template>
{{schedule?.param?.depname}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生姓名:
</div>
</template>
{{schedule?.docname}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生职称:
</div>
</template>
{{schedule?.title}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生专长:
</div>
</template>
{{schedule?.skill}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生服务费:
</div>
</template>
<span style="color: red">{{schedule?.amount}}</span>
</el-descriptions-item>
</el-descriptions>
ts
import {reqPatient, reqSchedule} from "@/api/hospital";
import {Doctor, PatientArr, PatientResponseData, ScheduleResponseData} from "@/api/hospital/type.ts";
let schedule = ref<Doctor>();
onMounted(()=>{
fetchPatietnt();
fetchSchedule();
})
const fetchSchedule= async ()=>{
let $useRoute = useRoute();
let scheduleId = $useRoute.query.docId;
let result:ScheduleResponseData = await reqSchedule(scheduleId as string);
console.log(result)
if(result.code==200){
schedule.value=result.data;
}
}
确定就诊人业务完成P44
添加已选择标识
修改syt/src/pages/hospital/register/visiter.vue
ts
defineProps(['patient','index','currentIndex'])
template
<div class="bottom">
<p>证件类型:{{patient.param.certificatesTypeString}}</p>
<p>证件号码:{{patient.certificatesNo}}</p>
<p>用户性别:{{patient.sex==0?'女生':'男士'}}</p>
<p>出生日期:{{patient.birthdate}}</p>
<p>手机号码:{{patient.phone}}</p>
<p>婚姻状况:{{patient.isMarry==0?'未婚':'已婚'}}</p>
<p>当前住址:{{patient.param.cityString}}</p>
<p>详细地址:{{patient.param.fullAddress}}</p>
<transition name="confirm">
<div class="confirm" v-if="index===currentIndex">已选择</div>
</transition>
</div>
scss
.bottom{
padding-left: 50px;
position: relative;
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;
}
.confirm-enter-to{
transform: scale(1.2);
}
}
修改syt/src/pages/hospital/register/register_step2.vue
父组件增加点击事件,并将当前选中就诊人索引传递给子组件
let currentIndex = ref<number>(-1);
const changeIndex = (index:number)=>{
currentIndex.value=index;
}
<div class="user">
<Visiter @click="changeIndex(index)" v-for="(patient,index) in patientArr" :key="patient.id" class="item" :patient="patient" :index="index" :currentIndex="currentIndex"/>
</div>
//禁用确认挂号按钮
<div class="btn">
<el-button type="primary" size="default" :disabled="currentIndex===-1">确认挂号</el-button>
</div>
完成会员中心路由P45
创建syt/src/pages/user/index.vue
<script setup lang="ts">
import {HomeFilled,Document,
Menu as IconMenu,
Location,
Setting} from "@element-plus/icons-vue";
import {useRoute, useRouter} from "vue-router";
let $router = useRouter();
let $route = useRoute();
const goUser = (path:string)=>{
$router.push({path:path});
}
</script>
<template>
<div class="user">
<div class="menu">
<div class="top">
<el-icon><HomeFilled/></el-icon>
<span> / 会员中心</span>
</div>
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
>
<el-menu-item index="/user/certification" @click="goUser('/user/certification')">
<el-icon><location /></el-icon>
<span>实名认证</span>
</el-menu-item>
<el-menu-item index="/user/order" @click="goUser('/user/order')">
<el-icon><icon-menu /></el-icon>
<span>挂号订单</span>
</el-menu-item>
<el-menu-item index="/user/patient" @click="goUser('/user/patient')">
<el-icon><document /></el-icon>
<span>就诊人管理</span>
</el-menu-item>
<el-menu-item index="/user/profile" @click="goUser('/user/profile')">
<el-icon><setting /></el-icon>
<span>账号信息</span>
</el-menu-item>
<el-menu-item index="/user/feedback" @click="goUser('/user/feedback')">
<el-icon><setting /></el-icon>
<span>意见反馈</span>
</el-menu-item>
</el-menu>
</div>
<div class="content">
<router-view></router-view>
</div>
</div>
</template>
<style scoped lang="scss">
.user{
display: flex;
.menu{
flex: 2;
display: flex;
flex-direction: column;
align-items: center;
}
.content{
flex: 8;
}
}
</style>
修改syt/src/router/index.ts
{
path:'/user',
component:()=>import(('@/pages/user/index.vue')),
children:[
{
path:'certification',
component:()=>import(('@/pages/user/certification/index.vue'))
},
{
path:'feedback',
component:()=>import(('@/pages/user/feedback/index.vue'))
},
{
path:'order',
component:()=>import(('@/pages/user/order/index.vue'))
},
{
path:'patient',
component:()=>import(('@/pages/user/patient/index.vue'))
},
{
path:'profile',
component:()=>import(('@/pages/user/profile/index.vue'))
},
]
创建syt/src/pages/user/certification/index.vue
<script setup lang="ts">
</script>
<template>
<div>实名认证</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/user/feedback/index.vue
<script setup lang="ts">
</script>
<template>
<div>意见反馈</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/user/order/index.vue
<script setup lang="ts">
</script>
<template>
<div> 挂号订单</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/user/patient/index.vue
<script setup lang="ts">
</script>
<template>
<div>就诊人管理</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/user/profile/index.vue
<script setup lang="ts">
</script>
<template>
<div>账号信息</div>
</template>
<style scoped lang="scss">
</style>
完成确定挂号业务P46
创建syt/src/api/user/index.ts
import request from "@/utils/request.ts";
import { SubmitOrderResponseData} from "@/api/user/type.ts";
enum API {
SUBMITORDER_URL='/order/orderInfo/auth/submitOrder/'
}
// export const reqSubmitOrder = (hoscode :string,scheduleId:string,patientId:string)=>request.post<any,OrderResponseData>(API.SUBMITORDER_URL+`${hoscode}/${scheduleId}/${patientId}`)
export const reqSubmitOrder = (scheduleId:string,patientId:string)=>request.post<any,SubmitOrderResponseData>(API.SUBMITORDER_URL+`${scheduleId}/${patientId}`);//本地接口
创建syt/src/api/user/type.ts
export interface ResponseData{
code:number,
message:string,
ok:boolean
}
export interface SubmitOrderResponseData extends ResponseData{
data:number
}
修改syt/src/pages/hospital/register/register_step2.vue
template
<div class="btn">
<el-button type="primary" size="default" :disabled="currentIndex===-1" @click="goUser()">确认挂号</el-button>
</div>
ts
import {useRoute, useRouter} from "vue-router";
let $router = useRouter();
const goUser = async ()=>{
let patientId = patientArr.value?. [ currentIndex.value].id;
let scheduleId = schedule.value?.id;
let hoscode = schedule.value?.hoscode;
console.log(hoscode);
// let result:OrderResponseData = await reqSubmitOrder(hoscode,scheduleId,patientId);
let result:SubmitOrderResponseData = await reqSubmitOrder(scheduleId as string,patientId as string);//本地接口
if(result.code==200){
$router.push({path:`/user/order?orderId=${result.data}`})
}else{
ElMessage({
type:'error',
message:result.message,
})
}
}
创建syt/src/pages/user/order/allOrder/index.vue
<script setup lang="ts">
</script>
<template>
<div>全部订单</div>
</template>
<style scoped lang="scss">
</style>
创建syt/src/pages/user/order/detail/index.vue
<script setup lang="ts">
</script>
<template>
<div>订单详情</div>
</template>
<style scoped lang="scss">
</style>
修改syt/src/pages/user/order/index.vue
<script setup lang="ts">
import {useRoute} from "vue-router";
import Detail from '@/pages/user/order/detail/index.vue'
import AllOrder from '@/pages/user/order/allOrder/index.vue'
let $route = useRoute();
</script>
<template>
<div>
<div v-if="$route.query.orderId">
<Detail/>
</div>
<div v-else>
<AllOrder/>
</div>
</div>
</template>
<style scoped lang="scss">
</style>
完成订单详情静态的搭建P47
修改syt/src/pages/user/order/detail/index.vue
<script setup lang="ts">
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="detail">挂号详情</div>
</template>
<div class="top">
<el-tag class="ml-2" type="success">
<div class="tag">
<svg t="1723598883339" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6216" width="16" height="16"><path d="M414.245926 751.028148l437.57037-453.12c7.300741-7.49037 7.111111-19.531852-0.474074-26.832593-7.49037-7.300741-19.531852-7.111111-26.832593 0.474074L398.506667 712.722963 199.111111 513.327407c-7.395556-7.395556-19.437037-7.395556-26.832593 0l0 0c-7.395556 7.395556-7.395556 19.437037 0 26.832593l212.574815 212.574815c6.731852 6.731852 17.161481 7.300741 24.557037 1.896296C411.211852 753.682963 412.823704 752.545185 414.245926 751.028148z" fill="#1afa29" p-id="6217"></path></svg>
<span>预约成功,待支付</span>
</div>
</el-tag>
<div class="right_info">
<img src="@/assets/images/code_app.png">
<div>
<p>微信 关注“北京114预约挂号”</p>
<p>快速挂号,轻松就医</p>
</div>
</div>
</div>
<div class="bottom">
<div class="left">
<el-descriptions
class="margin-top"
:column="1"
border
>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊人信息:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊日期:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊医院:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊科室:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生职称:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医事服务费:
</div>
</template>
<span style="color: red">100元</span>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
挂号单号:
</div>
</template>
xxxx
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
挂号时间:
</div>
</template>
xxxx
</el-descriptions-item>
</el-descriptions>
<div class="btn">
<el-button>取消预约</el-button>
<el-button type="primary" size="default">支付</el-button>
</div>
</div>
<div class="right">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>注意事项</span>
</div>
</template>
<p>1、请确认就诊人信息是否准确,若填写错误将无法取号就诊,损失由本人承担;</p>
<p style="color:red">2、【取号】就诊当天需在xxxx在医院取号,未取号视为爽约,该号不退不换;</p>
<p> 3、【退号】在xxx前可在线退号 ,逾期将不可办理退号退费;</p>
<p> 4、北京114预约挂号支持自费患者使用身份证预约,同时支持北京市医保患者使用北京社保卡在平台预约挂号。请于就诊当日,携带预约挂号所使用的有效身份证件到院取号;</p>
<p> 5、请注意北京市医保患者在住院期间不能使用社保卡在门诊取号。</p>
</el-card>
</div>
</div>
</el-card>
</template>
<style scoped lang="scss">
.box-card{
.detail{
color:#7f7f7f;
font-weight: 900;
}
.top{
display: flex;
justify-content: space-between;
border-bottom: 1px solid #ccc;
padding: 10px;
.tag{
display: flex;
justify-content: center;
align-items: center;
span{
margin-left: 5px;
}
}
.right_info{
display: flex;
justify-content: space-between;
img{
width: 40px;
height: 40px;
}
p{
font-size: 12px;
line-height: 20px;
}
}
}
.bottom{
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.left{
flex: 4;
.btn{
margin: 10px 0px;
}
}
.right{
flex: 6;
margin-left: 20px;
p{
line-height: 20px;
}
}
}
</style>
获取订单详情的数据P48
修改syt/src/api/user/type.ts
export interface OrderInfo {
"id": number,
"createTime": string,
"updateTime": string,
"isDeleted": number,
"param": {
"orderStatusString": string
},
"userId": number,
"outTradeNo": string,
"hoscode": string,
"hosname": string,
"depcode": string,
"depname": string,
"scheduleId": string,
"title": string,
"reserveDate":string,
"reserveTime": number,
"patientId": number,
"patientName": string,
"patientPhone": string,
"hosRecordId": string,
"number": number,
"fetchTime": string,
"fetchAddress": string,
"amount": number,
"quitTime": string,
"orderStatus": number
}
export interface OrderInfoResponseData extends ResponseData{
data:OrderInfo
}
修改syt/src/api/user/index.ts
enum API {
SUBMITORDER_URL='/order/orderInfo/auth/submitOrder/',
GETORDERINFO_URL = 'order/orderInfo/auth/getOrders/',
}
export const reqOrderInfo = (id:string)=>request.get<any,OrderInfoResponseData>(API.GETORDERINFO_URL+id);
修改syt/src/pages/user/order/detail/index.vue
<script setup lang="ts">
import {onMounted, ref} from "vue";
import {useRoute} from "vue-router";
import {reqOrderInfo} from "@/api/user";
import {OrderInfo, OrderInfoResponseData} from "@/api/user/type.ts";
let $route = useRoute();
let orderInfo = ref<OrderInfo>(<OrderInfo>{});
onMounted(()=>{
getOrderInfo();
})
const getOrderInfo = async ()=>{
let result:OrderInfoResponseData = await reqOrderInfo($route.query.orderId as string);
if(result.code==200){
orderInfo.value = result.data;
}
}
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="detail">挂号详情</div>
</template>
<div class="top">
<el-tag class="ml-2" type="success">
<div class="tag">
<svg t="1723598883339" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6216" width="16" height="16"><path d="M414.245926 751.028148l437.57037-453.12c7.300741-7.49037 7.111111-19.531852-0.474074-26.832593-7.49037-7.300741-19.531852-7.111111-26.832593 0.474074L398.506667 712.722963 199.111111 513.327407c-7.395556-7.395556-19.437037-7.395556-26.832593 0l0 0c-7.395556 7.395556-7.395556 19.437037 0 26.832593l212.574815 212.574815c6.731852 6.731852 17.161481 7.300741 24.557037 1.896296C411.211852 753.682963 412.823704 752.545185 414.245926 751.028148z" fill="#1afa29" p-id="6217"></path></svg>
<span>{{orderInfo.param?.orderStatusString}}</span>
</div>
</el-tag>
<div class="right_info">
<img src="@/assets/images/code_app.png">
<div>
<p>微信 关注“北京114预约挂号”</p>
<p>快速挂号,轻松就医</p>
</div>
</div>
</div>
<div class="bottom">
<div class="left">
<el-descriptions
class="margin-top"
:column="1"
border
>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊人信息:
</div>
</template>
{{orderInfo.patientName}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊日期:
</div>
</template>
{{orderInfo.reserveDate}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊医院:
</div>
</template>
{{orderInfo.hosname}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
就诊科室:
</div>
</template>
{{orderInfo.depname}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医生职称:
</div>
</template>
{{orderInfo.title}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
医事服务费:
</div>
</template>
<span style="color: red">{{orderInfo.amount}}元</span>
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
挂号单号:
</div>
</template>
{{orderInfo.outTradeNo}}
</el-descriptions-item>
<el-descriptions-item>
<template #label>
<div class="cell-item">
挂号时间:
</div>
</template>
{{orderInfo.createTime}}
</el-descriptions-item>
</el-descriptions>
<div class="btn">
<el-button>取消预约</el-button>
<el-button type="primary" size="default">支付</el-button>
</div>
</div>
<div class="right">
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>注意事项</span>
</div>
</template>
<p>1、请确认就诊人信息是否准确,若填写错误将无法取号就诊,损失由本人承担;</p>
<p style="color:red">2、【取号】就诊当天需在{{orderInfo.fetchTime}}在医院取号,未取号视为爽约,该号不退不换;</p>
<p> 3、【退号】在{{orderInfo.quitTime}}前可在线退号 ,逾期将不可办理退号退费;</p>
<p> 4、北京114预约挂号支持自费患者使用身份证预约,同时支持北京市医保患者使用北京社保卡在平台预约挂号。请于就诊当日,携带预约挂号所使用的有效身份证件到院取号;</p>
<p> 5、请注意北京市医保患者在住院期间不能使用社保卡在门诊取号。</p>
</el-card>
</div>
</div>
</el-card>
</template>
完成取消预约业务P49
修改syt/src/api/user/index.ts
修改syt/src/pages/user/order/detail/index.vue
ts
import {reqCancelOrderInfo, reqOrderInfo} from "@/api/user";
import {ElMessage} from "element-plus";
const cancel = async ()=>{
try{
let result = await reqCancelOrderInfo(orderInfo.value.id as string);
console.log(result);
getOrderInfo();
}catch (error) {
ElMessage({
type:'error',
message:'取消预约失败'
})
}
}
template
<div class="btn" v-if="orderInfo.orderStatus==0||orderInfo.orderStatus==1">
<el-popconfirm title="确认取消预约吗?" @confirm="cancel" >
<template #reference>
<el-button>取消预约</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="default" v-if="orderInfo.orderStatus==0">支付</el-button>
</div>
完成支付对话框静态搭建P50
修改syt/src/pages/user/order/detail/index.vue
ts
let dialogVisible = ref<boolean>(false)
const openDialog=()=>{
dialogVisible.value=true;
}
const closeDialog=()=>{
dialogVisible.value=false;
}
template
<el-button type="primary" size="default" v-if="orderInfo.orderStatus==0" @click="openDialog">支付</el-button>
<div class="paymentDialog">
<el-dialog v-model="dialogVisible" title="微信支付" width="400">
<div class="qrcode">
<img src="@/assets/images/code_app.png">
<p>请使用微信扫一扫</p>
<p>扫码二维码支付</p>
</div>
<template #footer>
<el-button size="default" @click="closeDialog">关闭窗口</el-button>
</template>
</el-dialog>
</div>
scss
.paymentDialog{
::v-deep(.el-dialog__body){
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
.qrcode{
display: flex;
flex-direction: column;
align-items: center;
p{
line-height: 30px;
}
}
}
利用qrcode插件获取支付二维码P51
安装qrcode
npm i qrcode
修改syt/src/api/user/index.ts
enum API {
//提交订单
SUBMITORDER_URL='/order/orderInfo/auth/submitOrder/',
//获取订单信息
GETORDERINFO_URL = 'order/orderInfo/auth/getOrders/',
//取消订单
CANCELORDER_URl = '/order/orderInfo/auth/cancelOrder/',
QRCODE_URl = '/order/weixin/createNative/',
}
export const reqQrCode = (orderId:string) => request.get<any,QrCode>(API.QRCODE_URl+orderId);
修改syt/src/api/user/type.ts
export interface PayInfo{
"codeUrl": string,
"orderId": number,
"totalFee": number,
"resultCode": string
}
export interface QrCode extends ResponseData{
data:PayInfo
}
修改syt/src/pages/user/order/detail/index.vue
引入插件
import QRCode from 'qrcode'
获取支付二维码
let imgUrl = ref<string>('');
const openDialog= async ()=>{
dialogVisible.value=true;
let result : QrCode = await reqQrCode($route.query.orderId as string);
imgUrl.value= await QRCode.toDataURL(result.data.codeUrl);
}
<div class="qrcode">
<img :src="imgUrl">
<p>请使用微信扫一扫</p>
<p>扫码二维码支付</p>
</div>
完成订单微信支付的业务P52
修改syt/src/api/user/index.ts
//查询微信支付状态
PAYSTATUS_URl = '/order/weixin/queryPayStatus/',
export const reqQueryPayStatus = (orderId:string) => request.get<any,PayStatus>(API.PAYSTATUS_URl+orderId);
修改syt/src/api/user/type.ts
export interface PayStatus extends ResponseData{
data:boolean
}
修改syt/src/pages/user/order/detail/index.vue
ts
let timer = ref();
const openDialog= async ()=>{
dialogVisible.value=true;
let result : QrCode = await reqQrCode($route.query.orderId as string);
imgUrl.value= await QRCode.toDataURL(result.data.codeUrl);
timer.value = setInterval(async ()=>{
let payStatus:PayStatus = await reqQueryPayStatus($route.query.orderId as string);
console.log(payStatus.data)
if(payStatus.data){
clearInterval(timer.value);
getOrderInfo();
}
},2000)
}
const closeDialog=()=>{
dialogVisible.value=false;
clearInterval(timer.value)
}
const close =()=>{
clearInterval(timer.value);
}
template
关闭弹窗回调:清除定时器
<el-dialog v-model="dialogVisible" title="微信支付" width="400" @close="close">
实名认证模块静态搭建P53
修改syt/src/pages/user/certification/index.vue
<script setup lang="ts">
import {InfoFilled,Plus} from "@element-plus/icons-vue";
</script>
<template>
<!-- 实名认证组件的结构 -->
<el-card class="box-card">
<!-- 卡片的头部 -->
<template #header>
<div class="card-header">
<span>实名信息</span>
<el-button class="button" text></el-button>
</div>
</template>
<!-- 实名认证结果的提示部分 -->
<div class="tip" style="color: #7f7f7f">
<p><el-icon><InfoFilled></InfoFilled></el-icon>完成实名认证后才能添加就诊人,正常进行挂号,为了不影响后续步骤,建议提前实名认证</p>
</div>
<!-- 卡片身体的底部:认证成功的结构、认证未成功的结构 -->
<el-descriptions v-if="false"
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>
贾成豪
</el-descriptions-item >
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">
证件类型
</div>
</template>
身份证
</el-descriptions-item>
<el-descriptions-item label-align="center" width="20px">
<template #label>
<div class="cell-item">
证件号
</div>
</template>
23018218871011
</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 style="width: 100%" placeholder="请选择证件类型">
<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"
>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog>
<img w-full alt="Preview Image" />
</el-dialog>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default">提交</el-button>
<el-button type="primary" size="default">重写</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<style scoped lang="scss">
.box-card{
.tip{
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
实名认证模块获取用户信息P54
修改syt/src/api/user/type.ts
export interface UserInfo{
"id": number,
"createTime": string,
"updateTime": string,
"isDeleted": number,
"param": {
},
"openid": string,
"nickName": string,
"phone": string,
"name": string,
"certificatesType": string,
"certificatesNo": string,
"certificatesUrl": string,
"authStatus": number,
"status": number
}
export interface UserInfoResponseData extends ResponseData{
data:UserInfo;
}
修改syt/src/api/user/index.ts
enum API {
//提交订单
SUBMITORDER_URL='/order/orderInfo/auth/submitOrder/',
//获取订单信息
GETORDERINFO_URL = 'order/orderInfo/auth/getOrders/',
//取消订单
CANCELORDER_URl = '/order/orderInfo/auth/cancelOrder/',
QRCODE_URl = '/order/weixin/createNative/',
//查询微信支付状态
PAYSTATUS_URl = '/order/weixin/queryPayStatus/',
GETUSERINFO_URL='/user/auth/getUserInfo/'
}
export const reqGetUserinfo = ()=>request.get<any,UserInfoResponseData>(API.GETUSERINFO_URL);
修改syt/src/pages/user/certification/index.vue
ts
<script setup lang="ts">
import {InfoFilled,Plus} from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {reqGetUserinfo} from "@/api/user";
import {UserInfoResponseData} from "@/api/user/type.ts";
let userInfo:any = ref<any>({});
onMounted(()=>{
getUserInfo();
})
const getUserInfo= async ()=>{
let result:UserInfoResponseData = await reqGetUserinfo();
if(result.code==200){
userInfo.value = result.data;
console.log(userInfo.status)
}
}
</script>
template
<el-descriptions v-if="userInfo.authStatus!=0"
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>
{{userInfo.name}}
</el-descriptions-item >
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">
证件类型
</div>
</template>
{{userInfo.certificatesType}}
</el-descriptions-item>
<el-descriptions-item label-align="center" width="20px">
<template #label>
<div class="cell-item">
证件号
</div>
</template>
{{userInfo.certificatesNo}}
</el-descriptions-item>
</el-descriptions>
<!-- 用户未认证的结构 -->
<el-form style="width: 60%;margin: 20px auto" label-width="80" v-if="userInfo.authStatus==0">
获取实名认证证件类型数据P55
修改syt/src/api/user/type.ts
export interface UserParams{
"certificatesNo": string,
"certificatesType": string,
"certificatesUrl": string,
"name": string
}
修改syt/src/api/user/index.ts
//获取证件类型的接口地址
CERTOFOCATOONTYPE_URL='/cmn/dict/findByDictCode/',
//用户认证的结构
USERAUTH_URL = '/user/auth/userAuth'
export const reqCertificatesType = (CertificatesType='CertificatesType')=>request.get<any,CertificationTypeResponseData>(API.CERTOFOCATOONTYPE_URL+CertificatesType);
export const reqUserAuth = (data:UserParams)=>request.post<any,any>(API.USERAUTH_URL,data);
修改syt/src/pages/user/certification/index.vue
ts
let arrType = ref<CertificationArr>([])
onMounted(()=>{
getUserInfo();
getType();
})
const getType = async ()=>{
let result:CertificationTypeResponseData = await reqCertificatesType();
if(result.code==200){
arrType.value = result.data;
}
}
template
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型">
<el-option :label="item.name" :value="item.value" v-for="(item,index) in arrType" :key="index"></el-option>
</el-select>
</el-form-item>
收集表单数据完成实名认证业务P56
修改syt/src/pages/user/certification/index.vue
ts
<script setup lang="ts">
import {InfoFilled,Plus} from "@element-plus/icons-vue";
import {onMounted, reactive, ref} from "vue";
import {reqCertificatesType, reqGetUserinfo, reqUserAuth} from "@/api/user";
import {CertificationArr, CertificationTypeResponseData, UserInfoResponseData, UserParams} from "@/api/user/type.ts";
import {ElMessage} from "element-plus";
let upload=ref();
let userInfo:any = ref<any>({});
let arrType = ref<CertificationArr>([])
let params = reactive<UserParams>({
certificatesNo: '',
certificatesType: '',
certificatesUrl: '',
name: ''
});
let dialogVisible = ref(false);
onMounted(()=>{
getUserInfo();
getType();
})
const getUserInfo= async ()=>{
let result:UserInfoResponseData = await reqGetUserinfo();
if(result.code==200){
userInfo.value = result.data;
}
}
const getType = async ()=>{
let result:CertificationTypeResponseData = await reqCertificatesType();
if(result.code==200){
arrType.value = result.data;
}
}
const exceedhandler = ()=>{
ElMessage({
type:'error',
message:'只能上传一张图片'
})
}
const successhandler =(response:any,uploadFile:any,uploadFiles:any)=>{
params.certificatesUrl=response.data;
}
const handlerPictureCardPreview = ()=>{
dialogVisible.value=true;
}
const handlerRemove =()=>{
params.certificatesUrl='';
}
const reset =()=>{
upload.value.clearFiles();
Object.assign(params,{
certificatesNo: '',
certificatesType: '',
certificatesUrl: '',
name: ''
});
}
const submit =async ()=>{
try{
await reqUserAuth(params);
getUserInfo();
ElMessage({
type:'success',
message:'认证成功'
})
}catch (error){
ElMessage({
type:'error',
message:'认证失败'
})
}
}
</script>
template
<el-form style="width: 60%;margin: 20px auto" label-width="80" v-if="userInfo.authStatus==0">
<el-form-item label="用户姓名">
<el-input placeholder="请输入用户姓名" v-model="params.name"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型" v-model="params.certificatesType">
<el-option :label="item.name" :value="item.value" v-for="(item,index) in arrType" :key="index"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号" >
<el-input placeholder="请输入证件号" v-model="params.certificatesNo"></el-input>
</el-form-item>
<el-form-item label="上传证件">
<el-upload
ref="upload"
list-type="picture-card"
action="/api/oss/file/fileUpload"
limit="1"
:on-exceed="exceedhandler"
:on-success="successhandler"
:on-preview="handlerPictureCardPreview"
:on-remove="handlerRemove"
>
<img style="width: 100%;height: 100%" src="//img.114yygh.com/static/web/auth_example.png">
<!-- <el-icon><Plus /></el-icon>-->
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full v-if="params.certificatesUrl" style="width: 100%;height: 100%" alt="Preview Image" :src="params.certificatesUrl" />
</el-dialog>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default" @click="submit">提交</el-button>
<el-button type="primary" size="default" @click="reset">重写</el-button>
</el-form-item>
</el-form>
实名认证表单自定义校验P57
修改syt/src/pages/user/certification/index.vue
<script setup lang="ts">
import {InfoFilled,Plus} from "@element-plus/icons-vue";
import {onMounted, reactive, ref} from "vue";
import {reqCertificatesType, reqGetUserinfo, reqUserAuth} from "@/api/user";
import {CertificationArr, CertificationTypeResponseData, UserInfoResponseData, UserParams} from "@/api/user/type.ts";
import {ElMessage} from "element-plus";
let upload=ref();
let form = ref();
let userInfo:any = ref<any>({});
let arrType = ref<CertificationArr>([])
let params = reactive<UserParams>({
certificatesNo: '',
certificatesType: '',
certificatesUrl: '',
name: ''
});
let dialogVisible = ref(false);
onMounted(()=>{
getType();
getUserInfo();
})
const getUserInfo= async ()=>{
let result:UserInfoResponseData = await reqGetUserinfo();
if(result.code==200){
userInfo.value = result.data;
}
}
const getType = async ()=>{
let result:CertificationTypeResponseData = await reqCertificatesType();
if(result.code==200){
arrType.value = result.data;
}
}
const exceedhandler = ()=>{
ElMessage({
type:'error',
message:'只能上传一张图片'
})
}
const successhandler =(response:any,uploadFile:any,uploadFiles:any)=>{
params.certificatesUrl=response.data;
form.value.clearValidate('certificatesUrl')
}
const handlerPictureCardPreview = ()=>{
dialogVisible.value=true;
}
const handlerRemove =()=>{
params.certificatesUrl='';
}
const reset =()=>{
upload.value.clearFiles();
Object.assign(params,{
certificatesNo: '',
certificatesType: '',
certificatesUrl: '',
name: ''
});
}
const submit =async ()=>{
let result = await form.value.validate();
try{
await reqUserAuth(params);
getUserInfo();
ElMessage({
type:'success',
message:'认证成功'
})
}catch (error){
ElMessage({
type:'error',
message:'认证失败'
})
}
}
//自定义姓名校验规则方法
const validatorName =(rule:any,value:any,callBack:any)=>{
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)=>{
if(value=='10'||value=='20'){
callBack();
}else{
callBack(new Error('请选择正确的证件类型'))
}
}
const validatorUrl =(rule:any,value:any,callBack:any)=>{
if(value.length){
callBack();
}else{
callBack(new Error('请上传证件照'))
}
}
const validatorNo =(rule:any,value:any,callBack:any)=>{
const sfz = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const hkb=/(^\d{15}$)/;
if(sfz.test(value)||hkb.test(value)){
callBack();
}else{
callBack(new Error('请输入正确的身份证或户口本号'))
}
}
const rules ={
name: [{
required:true,
validator:validatorName
}],
certificatesNo: [{
required:true,
validator:validatorNo
}],
certificatesType: [{
required:true,
validator:validatorType
}],
certificatesUrl: [{
required:true,
validator:validatorUrl
}],
}
</script>
<template>
<!-- 实名认证组件的结构 -->
<el-card class="box-card">
<!-- 卡片的头部 -->
<template #header>
<div class="card-header">
<span>实名信息</span>
<el-button class="button" text></el-button>
</div>
</template>
<!-- 实名认证结果的提示部分 -->
<div class="tip" style="color: #7f7f7f">
<p><el-icon><InfoFilled></InfoFilled></el-icon>完成实名认证后才能添加就诊人,正常进行挂号,为了不影响后续步骤,建议提前实名认证</p>
</div>
<!-- 卡片身体的底部:认证成功的结构、认证未成功的结构 -->
<el-descriptions v-if="userInfo.authStatus==1"
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>
{{userInfo.name}}
</el-descriptions-item >
<el-descriptions-item label-align="center" :width="20">
<template #label>
<div class="cell-item">
证件类型
</div>
</template>
{{userInfo.certificatesType}}
</el-descriptions-item>
<el-descriptions-item label-align="center" width="20px">
<template #label>
<div class="cell-item">
证件号
</div>
</template>
{{userInfo.certificatesNo}}
</el-descriptions-item>
</el-descriptions>
<!-- 用户未认证的结构 -->
<el-form style="width: 60%;margin: 20px auto" label-width="80" v-if="userInfo.authStatus==0" :model="params" :rules="rules" ref="form">
<el-form-item label="用户姓名" prop="name">
<el-input placeholder="请输入用户姓名" v-model="params.name"></el-input>
</el-form-item>
<el-form-item label="证件类型" prop="certificatesType">
<el-select style="width: 100%" placeholder="请选择证件类型" v-model="params.certificatesType">
<el-option :label="item.name" :value="item.value" v-for="(item,index) in arrType" :key="index"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号" prop="certificatesNo">
<el-input placeholder="请输入证件号" v-model="params.certificatesNo"></el-input>
</el-form-item>
<el-form-item label="上传证件" prop="certificatesUrl">
<el-upload
ref="upload"
list-type="picture-card"
action="/api/oss/file/fileUpload"
limit="1"
:on-exceed="exceedhandler"
:on-success="successhandler"
:on-preview="handlerPictureCardPreview"
:on-remove="handlerRemove"
>
<img style="width: 100%;height: 100%" src="//img.114yygh.com/static/web/auth_example.png">
<!-- <el-icon><Plus /></el-icon>-->
</el-upload>
<el-dialog v-model="dialogVisible">
<img w-full v-if="params.certificatesUrl" style="width: 100%;height: 100%" alt="Preview Image" :src="params.certificatesUrl" />
</el-dialog>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default" @click="submit">提交</el-button>
<el-button type="primary" size="default" @click="reset">重写</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<style scoped lang="scss">
.box-card{
.tip{
display: flex;
justify-content: center;
align-items: center;
}
}
</style>
挂号订单模块静态搭建P58
修改syt/src/pages/user/order/allOrder/index.vue
<script setup lang="ts">
import {ref} from "vue";
let pageNo = ref<number>(1);
let pageSize = ref<number>(10);
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>挂号订单</span>
</div>
</template>
<el-form :inline="true">
<el-form-item label="就诊人" style="width: 30%">
<el-select placeholder="请选择就诊人">
<el-option label="111"></el-option>
<el-option label="222"></el-option>
<el-option label="333"></el-option>
<el-option label="444"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单状态" style="width: 30%">
<el-select placeholder="请选择订单状态">
<el-option label="111"></el-option>
<el-option label="222"></el-option>
<el-option label="333"></el-option>
<el-option label="444"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-table border style="margin: 10px 0px">
<el-table-column label="就诊时间"></el-table-column>
<el-table-column label="医院"></el-table-column>
<el-table-column label="科室"></el-table-column>
<el-table-column label="医生"></el-table-column>
<el-table-column label="服务费"></el-table-column>
<el-table-column label="就诊人"></el-table-column>
<el-table-column label="订单状态"></el-table-column>
<el-table-column label="操作"></el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
class="pagination"
layout="prev, pager, next,jumper,->,sizes,total"
:total="400"
:page-sizes="[10,20,30]"
>
</el-pagination>
</el-card>
</template>
<style scoped lang="scss">
</style>
挂号订单模块API书写与TS类型定义P59
修改syt/src/api/user/type.ts
export type records = OrderInfo[];
export interface UserOrderInfoResponseData extends ResponseData{
data:{
records:records,
total:number,
size:number,
current:number,
orders:[],
hitCount:boolean,
searchCount:boolean,
page:number
}
}
修改syt/src/api/user/index.ts
USERORDERINFO_URL='/order/orderInfo/auth/'
export const reqUserOrderInfo = (page:number,limit:number,patientId:string,orderStatus:string)=>request.get<any,UserOrderInfoResponseData>(API.USERORDERINFO_URL+`${page}/${limit}?patientId=${patientId}&orderStatus=${orderStatus}`);
获取挂号订单数据P60
修改syt/src/pages/user/order/allOrder/index.vue
<script setup lang="ts">
import {onMounted, ref} from "vue";
import {reqUserOrderInfo} from "@/api/user";
import type {Records, UserOrderInfoResponseData} from "@/api/user/type.ts";
import {useRouter} from "vue-router";
let pageNo = ref<number>(1);
let pageSize = ref<number>(10);
let orderStatus = ref<String>('');
let patientId = ref<String>('');
let allOrderArr = ref<Records>([]);
let total = ref<number>(0)
let $router = useRouter();
onMounted(()=>{
getOrderInfo();
})
const getOrderInfo = async ()=>{
let result:UserOrderInfoResponseData = await reqUserOrderInfo(pageNo.value,pageSize.value,patientId.value,orderStatus.value);
if(result.code==200){
allOrderArr.value = result.data.records;
total.value = result.data.total;
}
}
const goDetail=(row)=>{
$router.push({path:'/user/order',query:{orderId:row.id}})
}
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>挂号订单</span>
</div>
</template>
<el-form :inline="true">
<el-form-item label="就诊人" style="width: 30%">
<el-select placeholder="请选择就诊人">
<el-option label="111"></el-option>
<el-option label="222"></el-option>
<el-option label="333"></el-option>
<el-option label="444"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单状态" style="width: 30%">
<el-select placeholder="请选择订单状态">
<el-option label="111"></el-option>
<el-option label="222"></el-option>
<el-option label="333"></el-option>
<el-option label="444"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-table border style="margin: 10px 0px" :data="allOrderArr">
<el-table-column label="就诊时间" prop="reserveDate"></el-table-column>
<el-table-column label="医院" prop="hosname"></el-table-column>
<el-table-column label="科室" prop="depname"></el-table-column>
<el-table-column label="医生" prop="title"></el-table-column>
<el-table-column label="服务费" prop="amount"></el-table-column>
<el-table-column label="就诊人" prop="patientName"></el-table-column>
<el-table-column label="订单状态" prop="param.orderStatusString"></el-table-column>
<el-table-column label="操作" >
<template #="{row}">
<el-button type="text" @click="goDetail(row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="pageNo"
v-model:page-size="pageSize"
class="pagination"
layout="prev, pager, next,jumper,->,sizes,total"
:total="total"
:page-sizes="[10,20,30,40]"
@current-change="getOrderInfo"
@size-change="getOrderInfo"
>
</el-pagination>
</el-card>
</template>
<style scoped lang="scss">
</style>
获取就诊人与订单状态API完成P61
就诊人信息在syt/src/api/hospital/index.ts中已定义,直接使用
修改syt/src/api/user/type.ts
export interface OrderState{
"comment":string,
"status":number
}
export type AllOrderState = OrderState[];
export interface AllOrderStateResponseData extends ResponseData{
data:AllOrderState
}
修改syt/src/api/user/index.ts
ORDERSTATE_URL = '/order/orderInfo/auth/getStatusList'
export const reqOrderState = ()=>request.get<any,AllOrderStateResponseData>(API.ORDERSTATE_URL);
完成挂号订单模块P62
修改syt/src/pages/user/order/allOrder/index.vue
ts
import type {AllOrderStateResponseData, Records, UserOrderInfoResponseData} from "@/api/user/type.ts";
import type {PatientResponseData} from "@/api/hospital/type.ts";
import type {PatientArr} from "@/api/hospital/type.ts";
import type {AllOrderState} from "@/api/user/type.ts";
let allUsr = ref<PatientArr>([]);
let allOrderState = ref<AllOrderState>([])
let allUsr = ref<PatientArr>([]);
let allOrderState = ref<AllOrderState>([])
onMounted(()=>{
getOrderInfo();
getData();
})
const getData = async ()=>{
const result:PatientResponseData = await reqPatient();
const orderStateResult:AllOrderStateResponseData = await reqOrderState();
allUsr.value = result.data;
allOrderState.value = orderStateResult.data;
}
template
<el-form :inline="true">
<el-form-item label="就诊人" style="width: 30%">
<el-select placeholder="请选择就诊人" v-model="patientId" @change="changeUser">
<el-option :label="item.name" v-for="item in allUsr":key="item.id" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单状态" style="width: 30%">
<el-select placeholder="请选择订单状态" v-model="orderStatus" @change="changeOrderState">
<el-option label="全部订单" value=""></el-option>
<el-option :label="item.comment" v-for="item in allOrderState":key="item.status" :value="item.status"></el-option>
</el-select>
</el-form-item>
就诊人管理模块展示全部就诊人信息P63
将就诊人组件提到组件模块
syt/src/pages/hospital/register/visiter.vue=》syt/src/components/visiter/visiter.vue
修改syt/src/components/visiter/visiter.vue
import {Delete, Edit} from "@element-plus/icons-vue";
import {useRoute} from "vue-router";
let $useRoute = useRoute();
<div class="right">
<el-button type="primary" size="default" :icon="Edit"></el-button>
<!-- <el-button v-if="$useRoute.path=='/user/patient'" type="danger" size="default" :icon="Delete"></el-button>-->
<slot name="otherBtn" > </slot>
</div>
修改syt/src/main.ts
import Visiter from "@/components/visiter/visiter.vue";
app.component('Visiter',Visiter)
修改syt/src/pages/hospital/register/register_step2.vue
删除该行代码
import Visiter from "@/pages/hospital/register/visiter.vue";
修改syt/src/pages/user/patient/index.vue
<script setup lang="ts">
import {Delete, User} from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {PatientArr, PatientResponseData} from "@/api/hospital/type.ts";
import {reqPatient} from "@/api/hospital";
let patientArr = ref<PatientArr>();
onMounted(()=>{
fetchPatietnt();
})
const fetchPatietnt= async ()=>{
let result:PatientResponseData = await reqPatient();
if(result.code==200){
patientArr.value=result.data;
}
}
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>就诊人管理</span>
<el-button size="default" type="primary" :icon="User">添加就诊人</el-button>
</div>
</template>
<div class="visiter">
<Visiter v-for="(patient,index) in patientArr" :key="patient.id" class="item" :patient="patient" :index="index" >
<template #otherBtn>
<el-button type="danger" size="default" :icon="Delete"></el-button>
</template>
</Visiter>
</div>
</el-card>
</template>
<style scoped lang="scss">
.box-card{
margin: 20px 0px;
.card-header{
display: flex;
justify-content: space-between;
align-items: center;
}
.visiter{
display: flex;
flex-wrap: wrap;
.item{
width: 32%;
margin: 5px;
border: solid 1px #ccc;
}
}
}
</style>
完成添加与修改就诊人静态搭建P64
修改syt/src/components/visiter/visiter.vue
let $emit = defineEmits(['changeScene']);
<el-button type="primary" size="default" :icon="Edit" @click="$emit('changeScene')"></el-button>
修改syt/src/pages/user/patient/index.vue
<script setup lang="ts">
import {Delete, User} from "@element-plus/icons-vue";
import {onMounted, ref} from "vue";
import {PatientArr, PatientResponseData} from "@/api/hospital/type.ts";
import {reqPatient} from "@/api/hospital";
let patientArr = ref<PatientArr>();
let scene = ref<number>(0);
onMounted(()=>{
fetchPatietnt();
})
const fetchPatietnt= async ()=>{
let result:PatientResponseData = await reqPatient();
if(result.code==200){
patientArr.value=result.data;
}
}
const addPatient =()=>{
scene.value=1;
}
const changeScene =()=>{
scene.value=1;
}
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>就诊人管理</span>
<el-button size="default" type="primary" :icon="User" @click="addPatient">添加就诊人</el-button>
</div>
</template>
<div class="visiter" v-if="scene==0">
<Visiter @changeScene="changeScene" v-for="(patient,index) in patientArr" :key="patient.id" class="item" :patient="patient" :index="index" >
<template #otherBtn>
<el-button type="danger" size="default" :icon="Delete"></el-button>
</template>
</Visiter>
</div>
<div class="form" v-if="scene==1">
<el-divider content-position="left">就诊人信息</el-divider>
<el-form style="width: 60%;margin: 10px auto">
<el-form-item label="用户姓名">
<el-input placeholder="请你输入用户姓名"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型">
<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-radio-group>
<el-radio :label="1">男</el-radio>
<el-radio :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出生日期">
<el-date-picker type="date" placeholder="请你选择日期"/>
</el-form-item>
<el-form-item label="手机号码">
<el-input placeholder="请你输入用户手机号码"></el-input>
</el-form-item>
</el-form>
<el-divider content-position="left">建档信息(完善后部分医院首次就诊不排队建档)</el-divider>
<el-form style="width: 60%;margin: 10px auto">
<el-form-item label="婚姻状况">
<el-radio-group>
<el-radio :label="1">已婚</el-radio>
<el-radio :label="0">未婚</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="自费/医保">
<el-radio-group>
<el-radio :label="1">自费</el-radio>
<el-radio :label="0">医保</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="当前住址">
<el-select style="width: 100%" placeholder="请选择用户地址">
<el-option v-for="item in 10" :label="item"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-divider content-position="left">联系人信息(选填)</el-divider>
<el-form style="width: 60%;margin: 10px auto" label-width="80px">
<el-form-item label="用户姓名">
<el-input placeholder="请你输入用户姓名"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型">
<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-input placeholder="请你输入用户手机号码"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default">提交</el-button>
<el-button type="primary" size="default">重写</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
</template>
<style scoped lang="scss">
.box-card{
margin: 20px 0px;
.card-header{
display: flex;
justify-content: space-between;
align-items: center;
}
.visiter{
display: flex;
flex-wrap: wrap;
.item{
width: 32%;
margin: 5px;
border: solid 1px #ccc;
}
}
}
</style>
获取证件类型及级联选择器展示地址P65
修改syt/src/api/user/index.ts
// CITY_URL='/cmn/dict/findByParentId/',
CITY_URL='/cmn/dict/findChildData/',//本地
export const reqCity = (parentId:string)=>request.get<any,any>(API.CITY_URL+parentId)
修改syt/src/pages/user/patient/index.vue
import type {CertificationArr, CertificationTypeResponseData} from "@/api/user/type.ts";
let certificationArr = ref<CertificationArr>([]);
onMounted(()=>{
fetchPatietnt();
getCertificationType();
})
const getCertificationType=async ()=>{
let result :CertificationTypeResponseData = await reqCertificatesType();
if(result.code==200){
certificationArr.value = result.data;
}
}
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型">
<el-option label="身份证" v-for="item in certificationArr" :key="item.id" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型">
<el-option label="身份证" v-for="item in certificationArr" :key="item.id" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
import {CascaderProps} from "element-plus";
const props:CascaderProps ={
lazy:true,
async lazyLoad(node:any,resolve:any){
let result:any = await reqCity(node.data.id||'86');
let showData = result.data.map((item:any)=>{
return{
id:item.id,
label:item.name,
value:item.value,
leaf:!item.hasChildren
}
})
resolve(showData);
}
}
<el-form-item label="当前住址">
<el-cascader :props="props"/>
</el-form-item>
添加就诊人与更新就诊人的API书写66
修改syt/src/api/user/index.ts
import request from "@/utils/request.ts";
import {
AddOrUpdateUser,
AllOrderStateResponseData,
CertificationTypeResponseData,
OrderInfoResponseData,
PayStatus,
QrCode,
SubmitOrderResponseData,
UserInfoResponseData, UserOrderInfoResponseData, UserParams
} from "@/api/user/type.ts";
ADDUSER_URL = '/user/patient/auth/save/',
UPDATEUSER_URL = '/user/patient/auth/update/',
export const reqAddOrUpdateUser = (data:AddOrUpdateUser)=>{
if(!data.id){
return request.post(API.ADDUSER_URL,data)
}else{
return request.post(API.UPDATEUSER_URL,data);
}
}
修改syt/src/api/user/type.ts
export interface AddOrUpdateUser {
address:string,
addressSelected: [],
birthdate:string,
certificatesNo:string,
certificatesType:string,
cityCode:string,
contactsCertificatesNo:string,
contactsCertificatesType:string,
contactsName:string,
contactsPhone:string,
districtCode:string,
isDeleted:number,
isInsure:number,
isMarry:number,
name:string,
phone:string,
provinceCode:string,
sex:number,
id:number
}
完成就诊人添加的业务P67
修改syt/src/pages/hospital/register/register_step2.vue
const addPatient = ()=>{
$router.push({path:'/user/patient',query:{type:'add'}});
}
<div class="card-header">
<span>请选择就诊人</span>
<el-button size="default" type="primary" :icon="User" @click="addPatient">添加就诊人</el-button>
</div>
修改syt/src/pages/user/patient/index.vue
<script setup lang="ts">
import {Delete, User} from "@element-plus/icons-vue";
import {onMounted, reactive, ref} from "vue";
import {PatientArr, PatientResponseData} from "@/api/hospital/type.ts";
import {reqPatient} from "@/api/hospital";
import {reqAddOrUpdateUser, reqCertificatesType, reqCity} from "@/api/user";
import {CascaderProps, ElMessage} from "element-plus";
import type {AddOrUpdateUser, CertificationArr, CertificationTypeResponseData} from "@/api/user/type.ts";
import {useRoute, useRouter} from "vue-router";
let patientArr = ref<PatientArr>();
let scene = ref<number>(0);
let certificationArr = ref<CertificationArr>([]);
let patientParams = reactive<AddOrUpdateUser>({
id: 0,
address:'',
addressSelected: [],
birthdate:'',
certificatesNo:'',
certificatesType:'',
cityCode:'',
contactsCertificatesNo:'',
contactsCertificatesType:'',
contactsName:'',
contactsPhone:'',
districtCode:'',
isDeleted:0,
isInsure:0,
isMarry:0,
name:'',
phone:'',
provinceCode:'',
sex:0,
})
let $route = useRoute();
let $router = useRouter();
onMounted(()=>{
fetchPatient();
getCertificationType();
if($route.query.type=='add'){
scene.value=1;
}
})
const fetchPatient= async ()=>{
let result:PatientResponseData = await reqPatient();
if(result.code==200){
patientArr.value=result.data;
}
}
const addPatient =()=>{
scene.value=1;
reset();
}
const changeScene =()=>{
scene.value=1;
}
const getCertificationType=async ()=>{
let result :CertificationTypeResponseData = await reqCertificatesType();
if(result.code==200){
certificationArr.value = result.data;
}
}
const props:CascaderProps ={
lazy:true,
async lazyLoad(node:any,resolve:any){
let result:any = await reqCity(node.data.id||'86');
let showData = result.data.map((item:any)=>{
return{
id:item.id,
label:item.name,
value:item.value,
leaf:!item.hasChildren
}
})
resolve(showData);
}
}
const submit = async ()=>{
try{
//地址处理
patientParams.provinceCode = patientParams.addressSelected[0] || ''
patientParams.cityCode =patientParams.addressSelected[1] || ''
patientParams.districtCode = patientParams.addressSelected[2] || ''
let result:any = await reqAddOrUpdateUser(patientParams);
if(result.code==200){
ElMessage({
type:'success',
message:patientParams.id?'更新成功':'添加成功'
})
}else{
ElMessage({
type:'error',
message:result.message
})
}
if($route.query.type){
$router.back();
}else{
scene.value=0;
}
}catch (error){
ElMessage({
type:'error',
message:patientParams.id?'更新失败':'添加失败'
})
}
}
const reset = ()=>{
Object.assign(patientParams,{
address:'',
addressSelected: [],
birthdate:'',
certificatesNo:'',
certificatesType:'',
cityCode:'',
contactsCertificatesNo:'',
contactsCertificatesType:'',
contactsName:'',
contactsPhone:'',
districtCode:'',
isDeleted:0,
isInsure:0,
isMarry:0,
name:'',
phone:'',
provinceCode:'',
sex:0,
})
}
</script>
<template>
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>就诊人管理</span>
<el-button size="default" type="primary" :icon="User" @click="addPatient">添加就诊人</el-button>
</div>
</template>
<div class="visiter" v-if="scene==0">
<Visiter @changeScene="changeScene" v-for="(patient,index) in patientArr" :key="patient.id" class="item" :patient="patient" :index="index" >
<template #otherBtn>
<el-button type="danger" size="default" :icon="Delete"></el-button>
</template>
</Visiter>
</div>
<div class="form" v-if="scene==1">
<el-divider content-position="left">就诊人信息</el-divider>
<el-form style="width: 60%;margin: 10px auto">
<el-form-item label="用户姓名" >
<el-input placeholder="请你输入用户姓名" v-model="patientParams.name"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型" v-model="patientParams.certificatesType">
<el-option label="身份证" v-for="item in certificationArr" :key="item.id" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号码">
<el-input placeholder="请你输入证件号码" v-model="patientParams.certificatesNo"></el-input>
</el-form-item>
<el-form-item label="用户性别">
<el-radio-group v-model="patientParams.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="0">女</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="出生日期">
<el-date-picker type="date" placeholder="请你选择日期" value-format="YYYY-MM-DD" v-model="patientParams.birthdate"/>
</el-form-item>
<el-form-item label="手机号码">
<el-input placeholder="请你输入用户手机号码" v-model="patientParams.phone"></el-input>
</el-form-item>
</el-form>
<el-divider content-position="left">建档信息(完善后部分医院首次就诊不排队建档)</el-divider>
<el-form style="width: 60%;margin: 10px auto">
<el-form-item label="婚姻状况">
<el-radio-group v-model="patientParams.isMarry">
<el-radio :label="1">已婚</el-radio>
<el-radio :label="0">未婚</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="自费/医保">
<el-radio-group v-model="patientParams.isInsure">
<el-radio :label="1">自费</el-radio>
<el-radio :label="0">医保</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="当前住址">
<el-cascader :props="props" v-model="patientParams.addressSelected"/>
</el-form-item>
<el-form-item label="详细地址:">
<el-input v-model="patientParams.address" placeholder="应公安机关要求,请填写现真实住址"/>
</el-form-item>
</el-form>
<el-divider content-position="left">联系人信息(选填)</el-divider>
<el-form style="width: 60%;margin: 10px auto" label-width="80px">
<el-form-item label="用户姓名">
<el-input placeholder="请你输入用户姓名" v-model="patientParams.contactsName"></el-input>
</el-form-item>
<el-form-item label="证件类型">
<el-select style="width: 100%" placeholder="请选择证件类型" v-model="patientParams.contactsCertificatesType">
<el-option label="身份证" v-for="item in certificationArr" :key="item.id" :label="item.name" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号码">
<el-input placeholder="请你输入证件号码" v-model="patientParams.contactsCertificatesNo"></el-input>
</el-form-item>
<el-form-item label="手机号码">
<el-input placeholder="请你输入用户手机号码" v-model="patientParams.contactsPhone"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" size="default" @click="submit">提交</el-button>
<el-button type="primary" size="default" @click="reset">重写</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
</template>
完成更新已有就诊人业务P68
修改syt/src/components/visiter/visiter.vue
<el-button type="primary" size="default" :icon="Edit" @click="handler"></el-button>
<script setup lang="ts">
import {Delete, Edit} from "@element-plus/icons-vue";
import {useRoute, useRouter} from "vue-router";
let $useRoute = useRoute();
let $useRouter = useRouter();
let props = defineProps(['patient', 'index', 'currentIndex'])
let $emit = defineEmits(['changeScene']);
const handler = ()=>{
if($useRoute.path=='/user/patient'){
$emit('changeScene',props.patient)
}else{
$useRouter.push({path:'/user/patient',query:{type:'edit',id:props.patient.id}})
}
}
</script>
修改syt/src/pages/user/patient/index.vue
import {onMounted, reactive, ref, watch} from "vue";
onMounted(()=>{
fetchPatient();
getCertificationType();
if($route.query.type=='add'){
scene.value=1;
}
if($route.query.type=='edit'){
scene.value=1;
}
})
const changeScene =(user:AddOrUpdateUser)=>{
scene.value=1;
Object.assign(patientParams,user);
}
watch(()=>patientArr.value,()=>{
let patient = patientArr.value?.find((item:any)=>{
return item.id == $route.query.id
})
Object.assign(patientParams,patient)
})
完成删除就诊人信息业务P69
修改syt/src/api/user/index.ts
DELETEUSER_URL='/user/patient/auth/remove/'
export const reqRemoveUser = (id:number)=>request.delete(API.DELETEUSER_URL+id);
修改syt/src/components/visiter/visiter.vue
template
<div class="right">
<el-button type="primary" size="default" :icon="Edit" @click="handler"></el-button>
<el-popconfirm @confirm="removeUser" :title="`你确定要删除${patient.name}吗?`" width="200px">
<template #reference>
<el-button v-if="$useRoute.path=='/user/patient'" type="danger" size="default" :icon="Delete"></el-button>
</template>
</el-popconfirm>
</div>
ts
import {useRoute, useRouter} from "vue-router";
import {reqRemoveUser} from "@/api/user";
import {ElMessage} from "element-plus";
let $emit = defineEmits(['changeScene','removeUser']);
const removeUser=async ()=>{
try{
await reqRemoveUser(props.patient.id);
$emit('removeUser')
ElMessage({
type:'success',
message:'删除成功'
})
}catch (error){
ElMessage({
type:'error',
message:'删除失败'
})
}
}
修改syt/src/pages/user/patient/index.vue
const removeUser =()=>{
fetchPatient();
}
<Visiter @changeScene="changeScene" v-for="(patient,index) in patientArr" :key="patient.id" class="item" :patient="patient" :index="index"
@removeUser="removeUser"
>
路由鉴权之进度条与网页标题设置P70
安装进度条
npm i nprogress
创建syt/src/permisstion.ts
import router from "@/router";
//路由鉴权:就是路由能不能被访问到授权设置-》全局守卫完成
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
Nprogress.configure({showSpinner:false})
router.beforeEach((to,from,next)=>{
//访问路由组件之前,进度条开始动
Nprogress.start();
document.title = `尚医通-${to.meta.title}`
next();
});
router.afterEach((to,from)=>{
//访问路由组件成功,进度条消失
Nprogress.done();
})
修改syt/src/main.ts
import '@/permisstion'
修改syt/src/router/index.ts
import {createRouter,createWebHistory} from "vue-router";
//createRouter方法,用于创建路由实例,可以管理多个路由
export default createRouter({
// 路由模式设置
history:createWebHistory(),
routes:[
{
path:'/home',
component:()=> import((`@/pages/home/index.vue`)),
meta:{
title:'首页'
}
},
{
path:'/hospital',
component:()=>import((`@/pages/hospital/index.vue`)),
meta:{
title:'医院信息'
},
children:[
{
path:'register',
component:()=>import(('@/pages/hospital/register/index.vue')),
meta:{
title:'预约挂号'
},
},
{
path:'detail',
component:()=>import(('@/pages/hospital/detail/index.vue')),
meta:{
title:'医院详情'
},
},
{
path:'notice',
component:()=>import(('@/pages/hospital/notice/index.vue')),
meta:{
title:'预约须知'
},
},
{
path:'close',
component:()=>import(('@/pages/hospital/close/index.vue')),
meta:{
title:'停诊信息'
},
},
{
path:'search',
component:()=>import(('@/pages/hospital/search/index.vue')),
meta:{
title:'查询与取消'
},
},
{
path:'register_step1',
component:()=>import(('@/pages/hospital/register/register_step1.vue')),
meta:{
title:'预约挂号'
},
},
{
path:'register_step2',
component:()=>import(('@/pages/hospital/register/register_step2.vue')),
meta:{
title:'预约挂号'
},
}
]
},
{
path:'/wxlogin',
component:()=>import(('@/pages/wxlogin/index.vue')),
},
{
path:'/user',
component:()=>import(('@/pages/user/index.vue')),
meta:{
title:'会员中心'
},
children:[
{
path:'certification',
component:()=>import(('@/pages/user/certification/index.vue')),
meta:{
title:'实名认证'
},
},
{
path:'feedback',
component:()=>import(('@/pages/user/feedback/index.vue')),
meta:{
title:'意见反馈'
},
},
{
path:'order',
component:()=>import(('@/pages/user/order/index.vue')),
meta:{
title:'挂号订单'
},
},
{
path:'patient',
component:()=>import(('@/pages/user/patient/index.vue')),
meta:{
title:'就诊人管理'
},
},
{
path:'profile',
component:()=>import(('@/pages/user/profile/index.vue')),
meta:{
title:'账号信息'
},
},
]
},
{
path:'/',
redirect:'/home'
}
],
//滚动行为:控制滚动条的位置
scrollBehavior(){
return{
left:0,
top:0
}
}
})
路由鉴权完成P71
修改syt/src/permisstion.ts
import router from "@/router";
//路由鉴权:就是路由能不能被访问到授权设置-》全局守卫完成
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import userUserStore from '@/store/modules/user'
import pinia from '@/store'
Nprogress.configure({showSpinner: false})
let userStore = userUserStore(pinia);
let whiteList = ['/home', 'hospital/register', '/hospital/detail', '/hospital/notice', '/hospital/close', '/hospital/search'];
router.beforeEach((to, from, next) => {
//访问路由组件之前,进度条开始动
Nprogress.start();
document.title = `尚医通-${to.meta.title}`;
let token = userStore.userInfo.token;
if (token) {
next();
} else {
if (whiteList.includes(to.path)) {
next();
} else {
if(to.path.indexOf('/wxlogin')==-1){
userStore.visible = true;
}else{
next();
}
next({path: '/home', query: {redirect: to.fullPath}});
}
}
});
router.afterEach((to, from) => {
//访问路由组件成功,进度条消失
Nprogress.done();
})
修改syt/src/components/login/index.vue
let $route = useRoute();
let $router = useRouter();
const login = async () => {
await form.value.validate();
try {
await userStore.userLogin(loginParam);
userStore.visible = false
let redirect = $route.query.redirect;
if(redirect){
$router.push(redirect as string)
}else{
$router.push('/home')
}
} catch (error) {
ElMessage({
type: 'error',
message: (error as Error).message
})
}
}
项目路径https://gitee.com/lyfengluo/syt