2023年最新尚硅谷的尚医通项目(中)

1,186 阅读5分钟

image.png 尚医通是一个网上预约挂号系统,包含后台管理系统和前台用户系统,采用前后端分离开发模式。

尚医通是采用Vue3全家桶、TypeScript、Vite、Pinia、Element-plus等技术栈开发的在线医疗服务平台,集成了多家医院的挂号信息,提供全程跟踪服务,用户可以随时了解自己的挂号状态。

前端整体功能,已经学习完了,功能均已实现,在这里记录一下技术重点.....

gitee尚医通 gitee.com/wujingde/sh…

十八、预约挂号

18.1 小错误

image.png

错误原因:workData刚开始有可能是空对象,所以{}.hosname 肯定报错undefined

image.png image.png

修改: 加个?变成可选参数 image.png

18.1 页面展示

点击某个科室进入

image.png image.png

image.png

image.png

image.png

接口:

image.png

image.png

接口类型

image.png

image.png

渲染页面

image.png

切换分页获取数据 image.png

完整代码

<template>
  <div class="wrap">
    <!-- 顶部结构 -->
    <div class="top">
      <div class="hosname">{{ workData.baseMap?.hosname }}</div>
      <div class="line">|</div>
      <div>{{ workData.baseMap?.bigname }}</div>
      <div class="dot">.</div>
      <div class="hosdeparment">{{ workData.baseMap?.depname }}</div>
    </div>
    <div class="center">
      <h1 class="time">{{ workData.baseMap?.workDateString }}</h1>
      <div class="container">
        <div
          class="item"
          :class="{ active: item.status == -1 || item.availableNumber == -1 }"
          v-for="item in workData.bookingScheduleList"
          :key="item"
        >
          <div class="top2">{{ item.workDate }}-{{ item.dayOfWeek }}</div>
          <div class="bottom">
            <div v-if="item.status == -1">停止挂号</div>
            <div v-if="item.status == 0">
              {{
                item.availableNumber == -1
                  ? `约满了`
                  : `有号(${item.availableNumber})`
              }}
            </div>
            <div v-if="item.status == 1">即将放号</div>
          </div>
        </div>
      </div>
      <el-pagination
        v-model:current-page="pageNo"
        layout="prev, pager, next"
        :total="workData.total"
        @current-change="fetchWorkData"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";

import { reqHospitalWork } from "@/api/hospital";

import type { HospitalWorkData } from "@/api/hospital/type";

import { useRoute } from "vue-router";
// 获取路由对象
let $route = useRoute();

// 分页器当前页码
let pageNo = ref<number>(1);
// 每一页展示几条数据
let limit = ref<number>(6);

// 存储医院科室挂号的数据
let workData = ref<any>({});

// 组件挂载完毕发一次请求
onMounted(() => {
  fetchWorkData();
});

// 获取挂号的数据
const fetchWorkData = async () => {
  let result: HospitalWorkData = await reqHospitalWork(
    pageNo.value,
    limit.value,
    $route.query.hoscode as string,
    $route.query.depcode as string
  );
  console.log(result);
  if (result.code == 200) {
    workData.value = result.data;
  }
};
</script>

<style scoped lang="scss">
.wrap {
  .top {
    display: flex;
    color: #7f7f7f;
    .line {
      width: 5px;
      height: 20px;
      background: skyblue;
      margin: 0px 5px;
    }
    .dot {
      margin: 0px 5px;
      color: skyblue;
    }
  }
  .center {
    margin: 20px 0px;
    display: flex;
    flex-direction: column;
    align-items: center;
    .time {
      font-weight: 900;
    }
    .container {
      width: 100%;
      display: flex;
      margin: 30px 0px;
      .item {
        flex: 1;
        width: 100%;
        border: 1px solid skyblue;
        margin: 0px 5px;
        &.active {
          border: 1px solid #cccc;
          color: #7f7f7f;
          .top2 {
            background: #ccc;
          }
        }

        .top2 {
          background: #e8f2ff;
          /* margin: 5px 0px; */
          width: 100%;
          height: 30px;
          text-align: center;
          line-height: 30px;
        }
        .bottom {
          width: 100%;
          height: 60px;
          text-align: center;
          line-height: 60px;
        }
      }
    }
  }
}
</style>

18.2 预约挂号底部医生排班业务

ts数据类型

image.png

image.png

获取医生信息的逻辑

image.png

展示数据

image.png

image.png

image.png

image.png image.png

完整代码:

<template>
  <div class="wrap">
    <!-- 顶部结构 -->
    <div class="top">
      <div class="hosname">{{ workData.baseMap?.hosname }}</div>
      <div class="line">|</div>
      <div>{{ workData.baseMap?.bigname }}</div>
      <div class="dot">.</div>
      <div class="hosdeparment">{{ workData.baseMap?.depname }}</div>
    </div>
    <!-- 中间展示日期的结构 -->
    <div class="center">
      <h1 class="time">{{ workData.baseMap?.workDateString }}</h1>
      <div class="container">
        <div
          class="item"
          :class="{
            active: item.status == -1 || item.availableNumber == -1,
            cur: item.workDate == workTime.workDate,
          }"
          v-for="item in workData.bookingScheduleList"
          :key="item"
          @click="changeTime(item)"
        >
          <div class="top2">{{ item.workDate }}-{{ item.dayOfWeek }}</div>
          <div class="bottom">
            <div v-if="item.status == -1">停止挂号</div>
            <div v-if="item.status == 0">
              {{
                item.availableNumber == -1
                  ? `约满了`
                  : `有号(${item.availableNumber})`
              }}
            </div>
            <div v-if="item.status == 1">即将放号</div>
          </div>
        </div>
      </div>
      <el-pagination
        v-model:current-page="pageNo"
        layout="prev, pager, next"
        :total="workData.total"
        @current-change="fetchWorkData"
      />
    </div>
    <!-- 底部展示医生的结构 -->
    <div class="bottom1">
      <!-- 展示即将放号的时间 -->
      <div class="will" v-if="workTime.status == 1">
        <span class="time">2023年{{ workTime.workDateMd }}</span>
        <span class="willtext">放号</span>
      </div>
      <!-- 展示医生的结构 :上午、下午-->
      <div class="doctor" v-else>
        <div class="morning">
          <!-- 顶部文字提示 -->
          <div class="tip1">
            <svg
              t="1689336807398"
              class="icon"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="2545"
              width="32"
              height="32"
            >
              <path
                d="M304.5 691.8c0-145.3 117.8-263 263-263s263 117.8 263 263"
                fill="#FDDA09"
                p-id="2546"
              ></path>
              <path
                d="M567.5 428.8c-16.7 0-32.9 1.7-48.7 4.7 122 22.9 214.3 129.7 214.3 258.3h97.4c0.1-145.2-117.7-263-263-263z"
                fill="#FDA906"
                p-id="2547"
              ></path>
              <path
                d="M772.1 687.3c-8.3 0-15-6.7-15-15 0-66.2-25.8-128.5-72.6-175.4-46.8-46.8-109.1-72.6-175.4-72.6-66.2 0-128.5 25.8-175.4 72.6-46.8 46.8-72.6 109.1-72.6 175.4 0 8.3-6.7 15-15 15s-15-6.7-15-15c0-74.3 28.9-144.1 81.4-196.6 52.5-52.5 122.3-81.4 196.6-81.4s144.1 28.9 196.6 81.4c52.5 52.5 81.4 122.3 81.4 196.6 0 8.3-6.7 15-15 15z"
                fill=""
                p-id="2548"
              ></path>
              <path
                d="M914.1 704.5H120c-8.3 0-15-6.7-15-15s6.7-15 15-15h794.1c8.3 0 15 6.7 15 15s-6.7 15-15 15zM755.2 777.4H278.8c-8.3 0-15-6.7-15-15s6.7-15 15-15h476.5c8.3 0 15 6.7 15 15s-6.8 15-15.1 15zM605.6 858.1H428.5c-8.3 0-15-6.7-15-15s6.7-15 15-15h177.1c8.3 0 15 6.7 15 15s-6.8 15-15 15zM179.4 641h-63.5c-8.3 0-15-6.7-15-15s6.7-15 15-15h63.5c8.3 0 15 6.7 15 15s-6.7 15-15 15zM515.9 323.4c-8.3 0-15-6.7-15-15V181.3c0-8.3 6.7-15 15-15s15 6.7 15 15v127.1c0 8.3-6.7 15-15 15zM271 422.9c-3.8 0-7.7-1.5-10.6-4.4l-56.2-56.2c-5.9-5.9-5.9-15.4 0-21.2 5.9-5.9 15.4-5.9 21.2 0l56.2 56.2c5.9 5.9 5.9 15.4 0 21.2-2.9 3-6.8 4.4-10.6 4.4zM754.8 422.9c-3.8 0-7.7-1.5-10.6-4.4-5.9-5.9-5.9-15.4 0-21.2l56.2-56.2c5.9-5.9 15.4-5.9 21.2 0 5.9 5.9 5.9 15.4 0 21.2l-56.2 56.2c-2.9 3-6.7 4.4-10.6 4.4z"
                fill=""
                p-id="2549"
              ></path>
              <path
                d="M441.2 544.2a18.8 17.8 0 1 0 37.6 0 18.8 17.8 0 1 0-37.6 0Z"
                fill="#050400"
                p-id="2550"
              ></path>
              <path
                d="M553.9 544.2a18.8 17.8 0 1 0 37.6 0 18.8 17.8 0 1 0-37.6 0Z"
                fill="#050400"
                p-id="2551"
              ></path>
              <path
                d="M491.3 577.5c0 13.1 11.2 23.7 25 23.7s25-10.6 25-23.7h-50z"
                fill="#050400"
                p-id="2552"
              ></path>
              <path
                d="M406.9 588.6a21.9 20.7 0 1 0 43.8 0 21.9 20.7 0 1 0-43.8 0Z"
                fill="#FDA906"
                p-id="2553"
              ></path>
              <path
                d="M582.1 588.6a21.9 20.7 0 1 0 43.8 0 21.9 20.7 0 1 0-43.8 0Z"
                fill="#FDA906"
                p-id="2554"
              ></path>
            </svg>
            <span class="text">上午号源</span>
          </div>
          <!-- 每一个医生的信息 -->
          <div class="doc_info" v-for="doctor in morningArr" :key="doctor.id">
            <!-- 展示医生的名字|技能 -->
            <div class="left">
              <div class="info">
                <span>{{ doctor.title }}</span>
                <span>|</span>
                <span>{{ doctor.docname }}</span>
              </div>
              <div class="skill">{{ doctor.skill }}</div>
            </div>
            <!-- 右侧展示挂号的钱数 -->
            <div class="right">
              <div class="money">¥{{ doctor.amount }}元</div>
              <el-button type="primary">{{ doctor.availableNumber }}</el-button>
            </div>
          </div>
        </div>
        <div class="morning">
          <!-- 顶部文字提示 -->
          <div class="tip1">
            <svg
              t="1689338507838"
              class="icon"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="5411"
              width="32"
              height="32"
            >
              <path
                d="M369.86 594.04c45.34-78.5 145.73-105.38 224.23-60.04a164.171 164.171 0 0 1 60.04 60.04H369.86z m306.23 82.05z m-492.26 0h656.35c22.66 0 41.02 18.36 41.02 41.02 0 22.66-18.36 41.02-41.02 41.02H183.83c-22.66 0-41.02-18.36-41.02-41.02-0.01-22.66 18.36-41.02 41.02-41.02z"
                fill="#1296db"
                p-id="5412"
              ></path>
              <path
                d="M678.58 383.66l43.51-43.51c24.03-24.03 62.99-24.03 87.02 0 24.03 24.03 24.03 62.99 0 87.02l-43.51 43.51c-24.14 23.92-63.1 23.74-87.02-0.39-23.77-23.99-23.77-62.65 0-86.63zM512 204.34c33.98 0 61.53 27.55 61.53 61.53v61.53c-0.14 33.98-27.8 61.42-61.78 61.28-33.79-0.14-61.15-27.49-61.28-61.28v-61.53c0-33.98 27.55-61.53 61.53-61.53zM217.08 340.15c24.03-24.03 62.99-24.03 87.02 0l43.51 43.51c24.03 24.03 24.03 62.99 0 87.02-24.03 24.03-62.99 24.03-87.02 0l-43.51-43.51c-24.03-24.03-24.03-62.99 0-87.02z"
                fill="#1296db"
                opacity=".3"
                p-id="5413"
              ></path>
            </svg>
            <span class="text">下午号源</span>
          </div>
          <!-- 每一个医生的信息 -->
          <div class="doc_info" v-for="doctor in afterArr" :key="doctor.id">
            <!-- 展示医生的名字|技能 -->
            <div class="left">
              <div class="info">
                <span>{{ doctor.title }}</span>
                <span>|</span>
                <span>{{ doctor.docname }}</span>
              </div>
              <div class="skill">{{ doctor.skill }}</div>
            </div>
            <!-- 右侧展示挂号的钱数 -->
            <div class="right">
              <div class="money">¥{{ doctor.amount }}元</div>
              <el-button type="primary">{{ doctor.availableNumber }}</el-button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref, computed } from "vue";

import { reqHospitalWork, reqHospitalDoctor } from "@/api/hospital";

import type {
  HospitalWorkData,
  DoctorResponseData,
  DocArr,
  Doctor,
} from "@/api/hospital/type";

import { useRoute } from "vue-router";
// 获取路由对象
let $route = useRoute();

// 分页器当前页码
let pageNo = ref<number>(1);
// 每一页展示几条数据
let limit = ref<number>(6);

// 存储医院科室挂号的数据
let workData = ref<any>({});
// 存储当前排班日期:当前数据的第一个
let workTime: any = ref({});
// 存储排班医生的数据
let docArr = ref<DocArr>([]);

// 组件挂载完毕发一次请求
onMounted(() => {
  fetchWorkData();
});

// 获取挂号的数据
const fetchWorkData = async () => {
  let result: HospitalWorkData = await reqHospitalWork(
    pageNo.value,
    limit.value,
    $route.query.hoscode as string,
    $route.query.depcode as string
  );
  // console.log(result);
  if (result.code == 200) {
    workData.value = result.data;
    // 存储第一天日期的数据
    // workTime.value=workData.bookingScheduleList[0]
    workTime.value = workData.value.bookingScheduleList[0];

    // 获取一次医生的数据
    getDoctorWorkData();
  }
};

// 获取医生排班信息
const getDoctorWorkData = async () => {
  // 医院的编号
  let hoscode: string = $route.query.hoscode as string;
  // 科室的编号
  let depcode: string = $route.query.depcode as string;
  // 时间
  let workDate: string = workTime.value.workDate;
  // 获取排班医生的数据
  let result: DoctorResponseData = await reqHospitalDoctor(
    hoscode,
    depcode,
    workDate
  );
  // console.log(result);
  if (result.code == 200) {
    docArr.value = result.data;
  }
};

// 点击顶部某一天的时候触发回调
const changeTime = (item: any) => {
  // console.log(item);
  // 存储用户选择那一天的数据
  workTime.value = item;
  // 再发一次获取医生的排班的数据
  getDoctorWorkData();
};

// 计算出上午排班的医生数据
let morningArr = computed(() => {
  return docArr.value.filter((doc: Doctor) => {
    return doc.workTime == 0;
  });
});
// 计算出下午排班的医生数据
let afterArr = computed(() => {
  return docArr.value.filter((doc: Doctor) => {
    return doc.workTime == 1;
  });
});
</script>

<style scoped lang="scss">
.wrap {
  .top {
    display: flex;
    color: #7f7f7f;
    .line {
      width: 5px;
      height: 20px;
      background: skyblue;
      margin: 0px 5px;
    }
    .dot {
      margin: 0px 5px;
      color: skyblue;
    }
  }
  .center {
    margin: 20px 0px;
    display: flex;
    flex-direction: column;
    align-items: center;
    transition: all 0.5s;
    .time {
      font-weight: 900;
    }
    .container {
      width: 100%;
      display: flex;
      margin: 30px 0px;
      .item {
        flex: 1;
        width: 100%;
        border: 1px solid skyblue;
        margin: 0px 5px;
        &.active {
          border: 1px solid #cccc;
          color: #7f7f7f;
          .top2 {
            background: #ccc;
          }
        }
        &.cur {
          transform: scale(1.1);
        }

        .top2 {
          background: #e8f2ff;
          /* margin: 5px 0px; */
          width: 100%;
          height: 30px;
          text-align: center;
          line-height: 30px;
        }
        .bottom {
          width: 100%;
          height: 60px;
          text-align: center;
          line-height: 60px;
        }
      }
    }
  }
  .bottom1 {
    .will {
      text-align: center;
      font-size: 30px;
      font-weight: 900;
      .time {
        color: red;
      }
      .willtext {
        color: skyblue;
      }
    }

    .doctor {
      .morning {
        .tip1 {
          display: flex;
          align-items: center;
          .text {
            color: #7f7f7f;
            font-weight: 900;
          }
        }
        .doc_info {
          display: flex;
          justify-content: space-between;
          margin: 10px 0px;
          border-bottom: 1px solid #ccc;
          .left {
            .info {
              color: skyblue;
              margin: 10px 0px;
              span {
                margin: 0px 5px;
                font-size: 18px;
                font-weight: 900;
              }
            }
            .skill {
              margin: 10px 0px;
              color: #7f7f7f;
            }
          }
          .right {
            display: flex;
            width: 150px;
            justify-content: space-between;
            align-items: center;
            .money {
              color: #7f7f7f;
              font-weight: 900;
            }
          }
        }
      }
    }
  }
}
</style>

18.3、确认挂号信息

静态页面搭建

image.png

引入路由

image.png 创建文件

image.png

点击跳转到这个页面

image.png

使用路由

image.png

点击跳转,记得带上医生id

image.png

register_step2.vue

<template>
  <div class="container">
    <h1 class="tip">确认挂号信息</h1>
    <!-- 卡片:展示就诊人信息 -->
    <el-card class="box-card">
      <!-- 卡片的头部 -->
      <template #header>
        <div class="card-header">
          <span>请选择就诊人</span>
          <el-button class="button" type="primary" size="default" :icon="User"
            >添加就诊人</el-button
          >
        </div>
      </template>
      <!-- 卡片的身体部分展示就诊人信息 -->
      <div class="user">
        <!-- :user="user"       :index="index"    :currenIndex="index"    父组件传数据给子组件 -->
        <Visitor
          v-for="(user, index) in userArr"
          :key="user.id"
          class="item"
          :user="user"
          @click="changeIndex(index)"
          :index="index"
          :currentIndex="currentIndex"
        />
      </div>
    </el-card>
    <!-- 底部展示医生的信息 -->
    <el-card class="box-card">
      <!-- 卡片头部 -->
      <template #header>
        <div class="card-header">
          <span>挂号信息</span>
        </div>
      </template>
      <!-- 卡片的身体部分:展示医生的信息-->
      <el-descriptions class="margin-top" :column="2" border>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊日期:</div>
          </template>
          {{ docInfo.workDate }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊医院:</div>
          </template>
          {{ docInfo.param?.hosname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊科室:</div>
          </template>
          {{ docInfo.param?.depname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生姓名:</div>
          </template>
          {{ docInfo?.docname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生职称:</div>
          </template>
          {{ docInfo.title }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生专长:</div>
          </template>
          {{ docInfo.skill }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生服务费:</div>
          </template>
          <span style="color: red">{{ docInfo.amount }}</span>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>

    <!-- 确定挂号按钮 -->
    <div class="btn">
      <el-button
        type="primary"
        size="default"
        :disabled="currentIndex == -1 ? true : false"
        >确定挂号</el-button
      >
    </div>
  </div>
</template>

<script setup lang="ts">
import { User } from "@element-plus/icons-vue";

// 引入获取就诊人的信息接口
import { reqGetUser, reqDoctorInfo } from "@/api/hospital";
import { onMounted, ref } from "vue";
import { useRoute } from "vue-router";
import type {
  UserResponseData,
  UserArr,
  DoctorInfoData,
} from "@/api/hospital/type";
// 就诊人组件
import Visitor from "./visitor.vue";

// 获取路由对象
let $route = useRoute();
// 存储全部就诊人信息
let userArr = ref<UserArr>([]);

// 存储医生的信息
let docInfo = ref<any>({});

// 存储用户确认就诊人索引值
let currentIndex = ref<number>(-1);
// 组件挂载完毕获取数据
onMounted(() => {
  // 获取就诊人信息
  fetchUserData();
  //   获取医生信息
  fetchInfo();
});

// 获取就诊人信息
const fetchUserData = async () => {
  let result: UserResponseData = await reqGetUser();
  //   console.log(result);
  if (result.code == 200) {
    userArr.value = result.data;
  }
};

// 获取医生信息
const fetchInfo = async () => {
  let result: DoctorInfoData = await reqDoctorInfo(
    $route.query.docId as string
  );
  if (result.code == 200) {
    docInfo.value = result.data;
  }
};

// 点击就诊人子组件的回调
const changeIndex = (index: number) => {
  //   console.log(index);
  // 存储当前用户选中就诊人信息索引值
  currentIndex.value = index;
};
</script>

<style scoped lang="scss">
.container {
  .tip {
    font-weight: 900;
    color: #7f7f7f;
    font-size: 20px;
  }
  .box-card {
    margin: 20px 0px;
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .user {
      display: flex;
      flex-wrap: wrap;
      .item {
        width: 32%;
        margin: 5px;
      }
    }
  }
  .btn {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
  }
}
</style>


visitor.vue

<template>
  <div class="visitor">
    <div class="top">
      <div class="left">
        <span class="free">{{ user.isInsure == 1 ? "医保" : "自费" }}</span>
        <span class="username">{{ user.name }}</span>
      </div>
      <div class="right">
        <el-button circle type="primary" :icon="Edit"></el-button>
      </div>
    </div>
    <div class="bottom">
      <p>证件类型:{{ user.param.certificatesTypeString }}</p>
      <p>证件号码:{{ user.certificatesNo }}</p>
      <p>用户性别:{{ user.sex == 0 ? "女生" : "男生" }}</p>
      <p>出生日期:{{ user.birthdate }}</p>
      <p>手机号码:{{ user.phone }}</p>
      <p>婚姻状况:{{ user.isMarry == 1 ? "已婚" : "未婚" }}</p>
      <p>当前住址:{{ user.param.cityString }}</p>
      <p>详细住址:{{ user.paramfullAddress }}</p>
      <!-- 红色已选择的盒子 -->
      <transition name="confirm">
        <div class="confirm" v-if="index === currentIndex">已选择</div>
      </transition>
    </div>
  </div>
</template>

<script setup lang="ts">
import { Edit } from "@element-plus/icons-vue";

// 接受父组件传递过来的就诊人信息
defineProps(["user", "index", "currentIndex"]);
</script>

<style scoped lang="scss">
.visitor {
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  .top {
    height: 60px;
    background: #e5e5e5;
    display: flex;
    justify-content: space-around;
    align-items: center;
    .left {
      .free {
        background: white;
        padding: 5px;
        font-size: 12px;
        margin-right: 5px;
        border-radius: 10px;
      }
      .username {
        color: #7f7f7f;
      }
    }
  }
  .bottom {
    position: relative;
    padding-left: 50px;
    p {
      line-height: 40px;
    }
    .confirm {
      position: absolute;
      width: 200px;
      height: 200px;
      color: red;
      border-radius: 50%;
      border: 1px dashed red;
      text-align: center;
      line-height: 200px;
      left: 20%;
      top: 20%;
      opacity: 0.5;
      transform: rotate(35deg);
      font-weight: 900;
    }
    .confirm-enter-from {
      transform: scale(1);
    }
    .confirm-enter-active {
      transition: all 0.3s;
    }
    .confirm-enter-to {
      transform: scale(1.3);
    }
  }
}
</style>



18.4、医生挂号信息展示

接口

image.png image.png

image.png

image.png

展示医生信息

image.png

18.5、已选择图的完成

image.png

image.png

利用子绝父相的方法

image.png

18.6、确定就诊人业务

image.png

image.png

image.png

image.png

当选择有东西时,才可以确定挂号

image.png

十九、会员中心

19.1小错误

里面应该用单引号,不能用双引号 image.png

19.2 会员中心路由跳转

image.png

image.png

image.png

19.3 确认挂号(接口似乎挂了,一直数据异常)

接口

image.png

image.png

确定挂号

image.png

image.png

完整代码

<template>
  <div class="container">
    <h1 class="tip">确认挂号信息</h1>
    <!-- 卡片:展示就诊人信息 -->
    <el-card class="box-card">
      <!-- 卡片的头部 -->
      <template #header>
        <div class="card-header">
          <span>请选择就诊人</span>
          <el-button class="button" type="primary" size="default" :icon="User"
            >添加就诊人</el-button
          >
        </div>
      </template>
      <!-- 卡片的身体部分展示就诊人信息 -->
      <div class="user">
        <!-- :user="user"       :index="index"    :currenIndex="index"    父组件传数据给子组件 -->
        <Visitor
          v-for="(user, index) in userArr"
          :key="user.id"
          class="item"
          :user="user"
          @click="changeIndex(index)"
          :index="index"
          :currentIndex="currentIndex"
        />
      </div>
    </el-card>
    <!-- 底部展示医生的信息 -->
    <el-card class="box-card">
      <!-- 卡片头部 -->
      <template #header>
        <div class="card-header">
          <span>挂号信息</span>
        </div>
      </template>
      <!-- 卡片的身体部分:展示医生的信息-->
      <el-descriptions class="margin-top" :column="2" border>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊日期:</div>
          </template>
          {{ docInfo.workDate }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊医院:</div>
          </template>
          {{ docInfo.param?.hosname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">就诊科室:</div>
          </template>
          {{ docInfo.param?.depname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生姓名:</div>
          </template>
          {{ docInfo?.docname }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生职称:</div>
          </template>
          {{ docInfo.title }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生专长:</div>
          </template>
          {{ docInfo.skill }}
        </el-descriptions-item>
        <el-descriptions-item>
          <template #label>
            <div class="cell-item">医生服务费:</div>
          </template>
          <span style="color: red">{{ docInfo.amount }}</span>
        </el-descriptions-item>
      </el-descriptions>
    </el-card>

    <!-- 确定挂号按钮 -->
    <div class="btn">
      <el-button
        type="primary"
        size="default"
        :disabled="currentIndex == -1 ? true : false"
        @click="submitOrder"
        >确定挂号</el-button
      >
    </div>
  </div>
</template>

<script setup lang="ts">
import { User } from "@element-plus/icons-vue";

// 引入获取就诊人的信息接口
import { reqGetUser, reqDoctorInfo } from "@/api/hospital";
import { reqSubmitOrder } from "@/api/user";
import type { SubmitOrder } from "@/api/user/type";
import { onMounted, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import type {
  UserResponseData,
  UserArr,
  DoctorInfoData,
} from "@/api/hospital/type";
import { ElMessage } from "element-plus";
// 就诊人组件
import Visitor from "./visitor.vue";

// 获取路由对象
let $route = useRoute();
// 获取路由器对象
let $router = useRouter();
// 存储全部就诊人信息
let userArr = ref<UserArr>([]);

// 存储医生的信息
let docInfo = ref<any>({});

// 存储用户确认就诊人索引值
let currentIndex = ref<number>(-1);
// 组件挂载完毕获取数据
onMounted(() => {
  // 获取就诊人信息
  fetchUserData();
  //   获取医生信息
  fetchInfo();
});

// 获取就诊人信息
const fetchUserData = async () => {
  let result: UserResponseData = await reqGetUser();
  //   console.log(result);
  if (result.code == 200) {
    userArr.value = result.data;
  }
};

// 获取医生信息
const fetchInfo = async () => {
  let result: DoctorInfoData = await reqDoctorInfo(
    $route.query.docId as string
  );
  if (result.code == 200) {
    docInfo.value = result.data;
  }
};

// 点击就诊人子组件的回调
const changeIndex = (index: number) => {
  //   console.log(index);
  // 存储当前用户选中就诊人信息索引值
  currentIndex.value = index;
};

//确定挂号按钮的回调
const submitOrder = async () => {
  //医院编号
  let hoscode = docInfo.value.hoscode;
  //医生的ID
  let scheduleId = docInfo.value.id;
  //就诊人的ID
  let patientId = userArr.value[currentIndex.value].id;
  //提交订单
  let result: SubmitOrder = await reqSubmitOrder(
    hoscode,
    scheduleId,
    patientId
  );
  // console.log(result);

  //提交订单成功
  if (result.code == 200) {
    $router.push({ path: "/user/order", query: { orderId: result.data } });
  } else {
    ElMessage({
      type: "error",
      message: result.message,
    });
  }
};
</script>

<style scoped lang="scss">
.container {
  .tip {
    font-weight: 900;
    color: #7f7f7f;
    font-size: 20px;
  }
  .box-card {
    margin: 20px 0px;
    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    .user {
      display: flex;
      flex-wrap: wrap;
      .item {
        width: 32%;
        margin: 5px;
      }
    }
  }
  .btn {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
  }
}
</style>

19.4 订单详情

image.png

接口

image.png

image.png

19.5 取消预约

image.png

取消预约

image.png

image.png

image.png

19.6 支付二维码静态搭建

image.png

image.png

image.png

19.7 支付二维码获取

将字符串变为二维码插件---npm官网找qrcode插件

image.png

获取二维码

image.png

image.png

image.png

动态绑定图片 image.png

19.8 订单支付业务

接口

image.png

image.png

image.png 当二维码出现时,每隔两秒都要向服务器询问是否支付成功,当支付成功后,还要将定时器清除 image.png

清除定时器(窗口和右上角的叉子)

image.png image.png

二十、实名认证静态搭建

image.png 完整代码

<template>
  <!-- 实名认证结构 -->
  <el-card class="box-card">
    <!-- 卡片的头部 -->
    <template #header>
      <div class="card-header">
        <h1>实名信息</h1>
      </div>
    </template>
    <!-- 实名认证结构的提示部分 -->
    <div class="tip" style="color: #7f7f7f">
      <p>
        <el-icon><InfoFilled /></el-icon>
        完成实名认证后才能添加就诊人,正常进行挂号,为了不影响后续步骤,建议提前实名认证。
      </p>
    </div>
    <!-- 卡片身体的底部:认证成功的结构、认证未成功的结构 -->
    <el-descriptions
      v-if="true"
      class="margin-top"
      :column="1"
      border
      style="margin: 20px auto"
      size="small"
    >
      <el-descriptions-item label-align="center" :width="20">
        <template #label>
          <div class="cell-item">用户姓名</div>
        </template>
        kooriookami
      </el-descriptions-item>
      <el-descriptions-item label-align="center" :width="20">
        <template #label>
          <div class="cell-item">证件类型</div>
        </template>
        kooriookami
      </el-descriptions-item>
      <el-descriptions-item label-align="center" :width="20">
        <template #label>
          <div class="cell-item">证件号码</div>
        </template>
        kooriookami
      </el-descriptions-item>
    </el-descriptions>
    <!-- 用户未认证的结构 -->
    <el-form style="width: 60%; margin: 20px auto" label-width="80">
      <el-form-item label="用户姓名">
        <el-input placeholder="请输入用户的姓名"></el-input>
      </el-form-item>
      <el-form-item label="证件类型">
        <el-select placeholder="请选择证件类型" style="width: 100%">
          <el-option label="身份证"></el-option>
          <el-option label="户口本"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="证件号码">
        <el-input placeholder="请输入证件号码"></el-input>
      </el-form-item>
      <el-form-item label="上传证件">
        <el-upload list-type="picture-card">
          <img
            src="../../../assets/images/auth_example.png"
            alt=""
            style="width: 100%; height: 80%"
          />
        </el-upload>

        <el-dialog>
          <img w-full alt="Preview Image" />
        </el-dialog>
      </el-form-item>
      <el-form-item>
        <el-button type="primary"> 提交</el-button>
        <el-button> 重写</el-button>
      </el-form-item>
    </el-form>
  </el-card>
</template>

<script setup lang="ts">
import { InfoFilled } from "@element-plus/icons-vue";
</script>

<style scoped lang="scss">
.box-card {
  .tip {
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
</style>

20.1 获取用户模块

image.png 接口

image.png

image.png

image.png

20.2 获取表单数据

上传图片功能

小错误

image.png 原因:没有判断是否有图片,也可能上传的是空数组

功能实现:

image.png

image.png

image.png

image.png

重写:

image.png

image.png

image.png

image.png

提交:

image.png

image.png image.png

image.png

20.3 实名认证表单自定义校验

小bug:上传图片成功了,但是校验规则还在 image.png

解决:用了element plus自带的方法

image.png

自定义表单校验

image.png

image.png

校验函数;

// 自定义校验姓名规则
const validatorName = (rule: any, value: any, callBack: any) => {
  // rule即为当前校验字段的校验规则对象   value即为当前校验字段的校验数据data
  // console.log(rule);
  // console.log(value);
  const reg =
    /^[\u00B7\u3007\u3400-\u4DBF\u4E00-\u9FFF\uE000-\uF8FF\uD840-\uD8C0\uDC00-\uDFFF\uF900-\uFAFF]+$/;
  if (reg.test(value)) {
    callBack();
  } else {
    callBack(new Error("请输入正确的中国人的名字"));
  }
};

// 证件类型校验方法
const validatorType = (rule: any, value: any, callBack: any) => {
  // console.log(value);
  if (value == "10" || value == "20") {
    callBack();
  } else {
    callBack(new Error("请选择证件类型"));
  }
};

// 身份证号码
const validatorNo = (rule: any, value: any, callBack: any) => {
  // console.log(111);

  const reg =
    /^([1-6][1-9]|50)\d{4}(18|19|20)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
  const hkb = /^\d{9}$/;
  if (reg.test(value) || hkb.test(value)) {
    callBack();
  } else {
    callBack(new Error("请输入正确的身份证号码或者户口本号码"));
  }
};

// 证件照图片
const validatorUrl = (rule: any, value: any, callBack: any) => {
  if (value.length) {
    callBack();
  } else {
    callBack(new Error("请上传证件照"));
  }
};