Teng's blog Teng's blog
首页
Java
H5前端
GitHub (opens new window)
首页
Java
H5前端
GitHub (opens new window)
  • 01.项目介绍
  • 02.后台系统-搭建项目
  • 03.后台系统-医院设置模块
  • 04.后台系统-统一异常处理
  • 05.后台系统-统一日志处理
  • 06.后台系统-搭建管理后台前端
  • 07.后台系统-医院设置前端
  • 08.后台系统-数据字典
  • 09.SpringCache+Redis缓存数据
  • 10.集成与配置Nginx
  • 11.启动医院接口模拟系统
  • 12.后台系统-上传医院信息
  • 13.后台系统-上传科室信息
  • 14.后台系统-上传排班信息
  • 15.搭建服务注册中心Nacos
  • 16.后台系统-医院管理
  • 17.后台系统-排班管理
  • 18.搭建服务网关Gateway
  • 19.前台系统-搭建前端环境
  • 20.前台系统-首页
  • 21.前台系统-医院详情页
    • 页面效果
    • 预约挂号功能后端实现
      • 修改controller
      • 修改service
    • 预约挂号功能前端实现
      • 封装api
      • 关于Nuxt路由
      • 创建hospital/_hoscode.vue页面组件
    • 医院详情功能前端实现
    • 预约须知功能前端实现
  • 22.前台系统-用户登录
  • 23.后台系统-短信服务
  • 24.用户认证与网关整合
  • 25.前台系统-微信登录
  • 26.前台系统-实名认证
  • 27.前台系统-就诊人管理
  • 28.后台系统-平台用户管理
  • 29.前台系统-预约挂号详情
  • 30.前台系统-预约确认
  • 31.前台系统-预约下单
  • 32.前台系统-订单管理
  • 33.后台系统-订单管理
  • 34.前台系统-微信支付
  • 35.前台系统-取消预约
  • 36.前台系统-就医提醒
  • 37.后台系统-预约统计
  • 38.小结
  • 附录:医院接口模拟系统说明
  • 附录:在线预约挂号API接口文档
  • Project-尚医通
Shetengteng
2021-12-11

21.前台系统-医院详情页

# 页面效果

# 预约挂号功能后端实现

需要获取医院信息(医院基本信息、预约信息)和科室信息

# 修改controller

在 service-hosp的 HospitalApiController 中添加如下接口

package com.stt.yygh.hosp.controller;
...
@Api(tags = "医院管理接口")
@RestController
@RequestMapping("/api/hosp/hospital")
public class HospitalApiController {

    @Autowired
    private HospitalService service;

    @Autowired
    private DepartmentService departmentService;

  ...

    @ApiOperation(value = "获取科室列表")
    @GetMapping("department/{hoscode}")
    public Result department(@ApiParam(name = "hoscode", value = "医院code", required = true)
                             @PathVariable String hoscode) {
        return Result.ok(departmentService.findDeptTree(hoscode));
    }

    @ApiOperation(value = "医院预约挂号详情")
    @GetMapping("{hoscode}")
    public Result detail(@ApiParam(name = "hoscode", value = "医院code", required = true)
                         @PathVariable String hoscode) {
        return Result.ok(service.detail(hoscode));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 修改service

在 service-hosp 的HospitalService 中添加方法

package com.stt.yygh.hosp.service;
...
public interface HospitalService {
...

    Map<String, Object> detail(String hoscode);
}
1
2
3
4
5
6
7

在 service-hosp 的HospitalServiceImpl 中添加实现

package com.stt.yygh.hosp.service.impl;
...
@Slf4j
@Service
public class HospitalServiceImpl implements HospitalService {
...
    @Override
    public Map<String, Object> detail(String hoscode) {
        Map<String, Object> re = new HashMap<>();
        //医院详情
        Hospital hospital = this.packHospital(this.getByHoscode(hoscode));
        re.put("hospital", hospital);
        //预约规则
        re.put("bookingRule", hospital.getBookingRule());
        //不需要重复返回
        hospital.setBookingRule(null);
    	// 虽然返回map扩展性很强,但实际工作中还是建议返回相应的VO,易于维护,字段信息清晰
        return re;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 预约挂号功能前端实现

# 封装api

在yygh-site项目中,修改api/hosp/hosp.js文件,添加如下方法

export function detail(hoscode) {
  return request({
    url: `${api_name}/${hoscode}`,
    method: 'get'
  })
}

export function findDepartment(hoscode) {
  return request({
    url: `${api_name}/department/${hoscode}`,
    method: 'get'
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 关于Nuxt路由

# 固定路由

每次跳转路由一样,没有传递参数,可以使用如下方式跳转

window.location.href = '/demo'
1

然后在pages文件夹下创建demo/index.vue文件,Nuxt框架会自动寻找pages/demo/index.vue路径作为路由

# 动态路由

在跳转的同时传递参数,如

window.location.href = '/demo/'+id
1

id是一个变量名称,那么需要在pages中创建pages/demo/_id.vue文件,Nuxt会找到路径作为路由

  • Nuxt中动态路由的vue文件命名规范:_参数名称.vue

在vue页面中,通过如下方式获取参数

const id = this.$route.params.id
1

# 创建hospital/_hoscode.vue页面组件

创建/pages/hospital/_hoscode.vue组件

<template>
  <!-- header -->
  <div class="nav-container page-component">
    <!--左侧导航 #start -->
    <div class="nav left-nav">
      <div class="nav-item selected">
        <span class="v-link selected dark" :onclick="'javascript:window.location=\'/hospital/'+hospital.hoscode+'\''">预约挂号 </span>
      </div>
      <div class="nav-item ">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/detail/'+hospital.hoscode+'\''"> 医院详情 </span>
      </div>
      <div class="nav-item">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/notice/'+hospital.hoscode+'\''"> 预约须知 </span>
      </div>
      <div class="nav-item "><span class="v-link clickable dark"> 停诊信息 </span></div>
      <div class="nav-item "><span class="v-link clickable dark"> 查询/取消 </span></div>
    </div>
    <!-- 左侧导航 #end -->

    <!-- 右侧内容 #start -->
    <div class="page-container">
      <div class="hospital-home">
        <div class="common-header">
          <div class="title-wrapper">
            <span class="hospital-title">{{ hospital.hosname }}</span>
            <div class="icon-wrapper"><span class="iconfont"></span>{{ hospital.param.hostypeString }}</div>
          </div>
        </div>
        <div class="info-wrapper">
          <img class="hospital-img" :src="'data:image/jpeg;base64,'+hospital.logoData" :alt="hospital.hosname">
          <div class="content-wrapper">
            <div> 挂号规则</div>
            <div class="line">
              <div><span class="label">预约周期:</span><span>{{ bookingRule.cycle }}天</span></div>
              <div class="space"><span class="label">放号时间:</span><span>{{ bookingRule.releaseTime }}</span></div>
              <div class="space"><span class="label">停挂时间:</span><span>{{ bookingRule.stopTime }}</span></div>
            </div>
            <div class="line">
              <span class="label">退号时间:</span>
              <span v-if="bookingRule.quitDay == -1">就诊前一工作日{{ bookingRule.quitTime }}前取消</span>
              <span v-if="bookingRule.quitDay == 0">就诊前当天{{ bookingRule.quitTime }}前取消</span>
            </div>
            <div style="margin-top:20px"> 医院预约规则</div>
            <div class="rule-wrapper">
              <ol>
                <li v-for="item in bookingRule.rule" :key="item">{{ item }}</li>
              </ol>
            </div>
          </div>
        </div>
        <div class="title select-title"> 选择科室</div>
        <div class="select-dept-wrapper">
          <div class="department-wrapper">
            <div class="hospital-department">
              <div class="dept-list-wrapper el-scrollbar" style="height: 100%;">
                <div class="dept-list el-scrollbar__wrap" style="margin-bottom: -17px; margin-right: -17px;">
                  <div class="el-scrollbar__view">
                    <div class="sub-item" v-for="(item,index) in departmentVoList" :key="item.id"
                         :class="index == activeIndex ? 'selected' : ''" @click="move(index,item.depcode)">
                      {{ item.depname }}
                    </div>
                  </div>
                </div>
                <div class="el-scrollbar__bar is-horizontal">
                  <div class="el-scrollbar__thumb" style="transform: translateX(0%);"></div>
                </div>
                <div class="el-scrollbar__bar is-vertical">
                  <div class="el-scrollbar__thumb" style="transform: translateY(0%); height: 91.4761%;"></div>
                </div>
              </div>
            </div>
          </div>
          <div class="sub-dept-container">
            <div v-for="(item,index) in departmentVoList" :key="item.id" :class="index == 0 ? 'selected' : ''"
                 class="sub-dept-wrapper" :id="item.depcode">
              <div class="sub-title">
                <div class="block selected"></div>
                {{ item.depname }}
              </div>
              <div class="sub-item-wrapper">
                <div v-for="it in item.children" :key="it.id" class="sub-item" @click="schedule(it.depcode)">
                  <span class="v-link clickable">{{ it.depname }} </span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 右侧内容 #end -->
  </div>
  <!-- footer -->
</template>

<script>
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'

import { detail, findDepartment } from '~/api/hosp/hosp'

export default {
  data() {
    return {
      hoscode: null,
      activeIndex: 0,

      hospital: {
        param: {}
      },
      bookingRule: {},
      departmentVoList: []
    }
  },
  created() {
    this.hoscode = this.$route.params.hoscode
    this.init()
  },

  methods: {
    init() {

      detail(this.hoscode).then(res => {
        this.hospital = res.data.hospital
        this.bookingRule = res.data.bookingRule
      })

      findDepartment(this.hoscode).then(res => {
        this.departmentVoList = res.data
      })
    },

    move(index, depcode) {
      this.activeIndex = index
      document.getElementById(depcode).scrollIntoView()
    },

    schedule(depcode) {
      window.location.href = '/hospital/schedule?hoscode=' + this.hoscode + '&depcode=' + depcode
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

# 医院详情功能前端实现

创建/pages/hospital/detail/_hoscode.vue组件

<template>
  <!-- header -->
  <div class="nav-container page-component">
    <!--左侧导航 #start -->
    <div class="nav left-nav">
      <div class="nav-item ">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/'+hospital.hoscode+'\''">预约挂号 </span>
      </div>
      <div class="nav-item selected">
        <span class="v-link selected dark"
              :onclick="'javascript:window.location=\'/hospital/detail/'+hospital.hoscode+'\''"> 医院详情 </span>
      </div>
      <div class="nav-item">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/notice/'+hospital.hoscode+'\''"> 预约须知 </span>
      </div>
      <div class="nav-item "><span class="v-link clickable dark"> 停诊信息 </span></div>
      <div class="nav-item "><span class="v-link clickable dark"> 查询/取消 </span></div>
    </div>
    <!-- 左侧导航 #end -->

    <!-- 右侧内容 #start -->
    <div class="page-container">
      <div class="hospital-detail">
        <div class="common-header">
          <div class="title-wrapper"><span class="hospital-title">{{ hospital.hosname }}</span>
            <div class="icon-wrapper"><span class="iconfont"></span> {{ hospital.param.hostypeString }}</div>
          </div>
        </div>
        <div class="info-wrapper">
          <img :src="'data:image/jpeg;base64,'+hospital.logoData" :alt="hospital.hosname"
               style="width: 80px; height: 80px;">
          <div class="content-wrapper">
            <div class="icon-text-wrapper">
              <span class="iconfont prefix-icon"></span>
              <span class="text"><p>{{ hospital.route }}</p></span>
              <span class="iconfont right-icon"></span>
            </div>
          </div>
        </div>
        <div class="title mt40">医院介绍</div>
        <div class="detail-content mt40"><p>{{ hospital.intro }}</p></div>
      </div>
    </div>
    <!-- 右侧内容 #end -->
  </div>
  <!-- footer -->
</template>

<script>
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'
import { detail } from '~/api/hosp/hosp'

export default {
  data() {
    return {
      hoscode: null,
      hospital: {
        param: {}
      }
    }
  },
  created() {
    this.hoscode = this.$route.params.hoscode
    this.init()
  },
  methods: {
    init() {
      detail(this.hoscode).then(res => {
        this.hospital = res.data.hospital
      })
    }
  }
}
</script>
<style scoped>
.hospital-detail .info-wrapper {
  width: 100%;
  padding-left: 0;
  padding-top: 0;
  margin-top: 0;
  flex-direction: inherit;
}

.hospital-detail .info-wrapper .text {
  font-size: 14px;
}

.hospital-detail .content-wrapper p {
  text-indent: 0;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

# 预约须知功能前端实现

创建/pages/hospital/notice/_hoscode.vue组件

<template>
  <!-- header -->
  <div class="nav-container page-component">
    <!--左侧导航 #start -->
    <div class="nav left-nav">
      <div class="nav-item ">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/'+hospital.hoscode+'\''">预约挂号 </span>
      </div>
      <div class="nav-item ">
        <span class="v-link clickable dark"
              :onclick="'javascript:window.location=\'/hospital/detail/'+hospital.hoscode+'\''"> 医院详情 </span>
      </div>
      <div class="nav-item selected">
        <span class="v-link selected dark"
              :onclick="'javascript:window.location=\'/hospital/notice/'+hospital.hoscode+'\''"> 预约须知 </span>
      </div>
      <div class="nav-item "><span class="v-link clickable dark"> 停诊信息 </span></div>
      <div class="nav-item "><span class="v-link clickable dark"> 查询/取消 </span></div>
    </div>
    <!-- 左侧导航 #end -->

    <!-- 右侧内容 #start -->
    <div class="page-container">
      <div class="hospital-notice">
        <div class="content">
          <h2>{{ hospital.hosname }}预约挂号须知</h2>
          <p>为方便您早日就医康复,请您认真阅读预约挂号须知:</p>
          <h4 id="一、预约实名制:">一、预约实名制:</h4>
          <p>统一平台电话预约和网上预约挂号均采取实名制注册预约,请您如实提供就诊人员的真实姓名、有效证件号(身份证、护照)、性别、手机号码、社保卡号等基本信息。</p>
          <h4 id="二、预约挂号:">二、预约挂号:</h4>
          <p>按照北京市卫健委统一平台要求,预约挂号规则如下:</p>
          <ul>
            <li>在同一自然日,同一医院,同一科室,同一就诊单元,同一就诊人,可以预约最多1个号源;</li>
            <li>在同一自然周,同一就诊人,可以预约最多8个号源;</li>
            <li>在同一自然月,同一就诊人,可以预约最多12个号源;</li>
            <li>在同一自然季度,同一就诊人,可以预约最多24个号源。</li>
          </ul>
          <h4 id="三、取消预约:">三、取消预约:</h4>
          <p>已完成预约的号源,如需办理退号,至少在就诊前一工作日14:00前通过网站、微信公众号、114电话等平台预约渠道进行取消预约。</p>
          <h4 id="四、爽约处理:">四、爽约处理:</h4>
          <p>如预约成功后患者未能按时就诊且不办理取消预约号视为爽约,同一患者在自然年内爽约规则如下:</p>
          <ul>
            <li>累计爽约3次,自3次爽约日起,90天内不允许通过114平台进行预约挂号;</li>
            <li>累计爽约6次,自6次爽约日起,180天内不允许通过114平台进行预约挂号。</li>
          </ul>
        </div>
      </div>
    </div>
    <!-- 右侧内容 #end -->
  </div>
  <!-- footer -->
</template>

<script>
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'
import { detail } from '~/api/hosp/hosp'

export default {
  data() {
    return {
      hoscode: null,
      hospital: {
        param: {}
      }
    }
  },
  created() {
    this.hoscode = this.$route.params.hoscode
    this.init()
  },
  methods: {
    init() {
      detail(this.hoscode).then(response => {
        this.hospital = response.data.hospital
      })
    }
  }
}
</script>
<style scoped>
.hospital-detail .info-wrapper {
  width: 100%;
  padding-left: 0;
  padding-top: 0;
  margin-top: 0;
  flex-direction: inherit;
}

.hospital-detail .info-wrapper .text {
  font-size: 14px;
}

.hospital-detail .content-wrapper p {
  text-indent: 0;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
Last Updated: 2022/01/16, 11:29:51
20.前台系统-首页
22.前台系统-用户登录

← 20.前台系统-首页 22.前台系统-用户登录→

Theme by Vdoing | Copyright © 2021-2022 Shetengteng | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式