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

1,310 阅读7分钟

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

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

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

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

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

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

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

一、接口

服务器地址: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.html

订单接口: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

二、项目其他配置

2.1 浏览器自动打开 找到 package.json 配置文件!

"scripts": {
"dev": "vite --open",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},

2.2src 别名的配置

找到 vite.config.ts 配置文件。 如果红色语法提示请安装@types/node 是 TypeScript 的一个声明文件包,用于描述 Node.js 核心模块和常用的第三方库的类型信息

image.png 安装这份

image.png

image.png

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": path.resolve(__dirname, 'src')
}
}
})

找到 tsconfig.json 配置文件,找到配置项 compilerOptions 添加配置,这一步的作用是让 IDE 可以对路径进行智能提示

"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}

//微信开放平台官网地址 open.weixin.qq.com

//查看微信扫码登录文档 mp.weixin.qq.com/

项目当中全部路由:、 用户未登录可以访问的路由 /home /hospital/register /hospital/detail /hospital/notice /hospital/close / hospital/search 剩下其余的路由未登录不能访问的,如果用户登录了全部的路由都是可以访问的

引入清除默认样式

image.png

image.png

image.png

三、顶部、底部静态页面搭建

页面展示

image.png App.vue

<template>
  <div class="container">
    <!-- 顶部全局组件 -->
    <HospitalTop />
    <!-- 展示路由组件的区域 -->
    <div class="content">展示路由组件的区域</div>
    <!-- 底部全局组件 -->
    <HospitalBottom />
  </div>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.container {
  display: flex;
  /* 切换主轴方向 */
  flex-direction: column;
  /* justify-content: center; */
  align-items: center;
  .content {
    margin-top: 70px;
    width: 1200px;
    min-height: 700px;
  }
}
</style>

image.png

hospital_top.vue

<template>
  <div class="top">
    <div class="content">
      <!-- 左侧 -->
      <div class="left">
        <img src="../../assets/images/logo.png" alt="" />
        <p>尚医通 预约挂号统一平台</p>
      </div>
      <!-- 右侧 -->
      <div class="right">
        <div class="help">帮助中心</div>
        <div class="login">登录/注册</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.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>

hospital_bottom.vue

<template>
  <div class="bottom">
    <div class="contont">
      <div class="left">蜀ICP备2021007923号</div>
      <div class="right">
        <span> 联系我们 </span>
        <span>合作伙伴</span>
        <span>隐私协议</span>
        <span>用户协议</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.bottom {
  width: 100%;
  height: 50px;
  background-color: #f0f2f5;
  display: flex;
  justify-content: center;
  .contont {
    width: 1200px;
    height: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 14px;
    .right {
      span {
        margin: 0px 5px;
      }
    }
  }
}
</style>

四、路由配置

1.1目录

image.png

1.2路由index文件

import { createRouter, createWebHistory } from "vue-router";
// createApp方法,用于创建路由器实例,可以管理多个路由
export default createRouter({
  // 路由模式的设置
  history: createWebHistory(),
  // 管理路由
  routes: [
    {
      path: "/home",
      //   路由懒加载模式
      component: () => import("@/pages/home/index.vue"),
    },
    {
      path: "/hospital",
      component: () => import("@/pages/hospital/index.vue"),
    },
    {
      path: "/",
      //   路由重定向到home
      redirect: "/home",
    },
  ],
  // 滚动行为:控制滚动条的位置
  scrollBehavior() {
    return {
      left: 0,
      top: 0,
    };
  },
});

image.png

image.png

1.3 路由出口

image.png

五、首页静态搭建banner

element-plus 的使用

image.png 搜索组件 image.png 效果图

image.png

六、首页等级与地区组件拆分与静态搭建

image.png level/index.vue

<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>
        <li>三级甲等</li>
        <li>三级甲等</li>
        <li>三级甲等</li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<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>

region/index.vue

<template>
  <div class="region">
    <div class="content">
      <div class="left">地区:</div>
      <ul>
        <li class="active">全部</li>
        <li>昌平区</li>
        <li>昌平区</li>
        <li>昌平区</li>
        <li v-for="item in 20" :key="item">昌平区</li>
      </ul>
    </div>
  </div>
</template>

<script setup lang="ts"></script>

<style scoped lang="scss">
.region {
  color: #7f7f7f;
  margin-top: 10px;
  .content {
    display: flex;
    .left {
      margin-right: 10px;
      width: 50px;
    }
    ul {
      display: flex;
      flex-wrap: wrap;
      li {
        margin-right: 10px;
        margin-bottom: 10px;
        &.active {
          color: #55a6fe;
        }
      }
      li:hover {
        color: #55a6fe;
        /* 鼠标经过时出现小手 */
        cursor: pointer;
      }
    }
  }
}
</style>

七、分页搭建

image.png

八、网络axios二次封装

utils/request.ts

// 对于axios 函数库进行二次封装
// 你工作的时候是否axios进行二次封装?二次封装的目的是什么?
// 目的1:利用axios请求、响应拦截器功能
// 目的2:请求拦截器,一般可以在请求头中携带公共的参数:token
// 目的3:响应拦截器,可以简化服务器返回的数据,处理http网络错误
import axios from "axios";
import { ElMessage } from "element-plus";

// 利用axios.create方法创建一个axios实例:可以设置基础路径、超时的时间的设置
const request = axios.create({
  baseURL: "/api", //请求的基础路径的设置
  timeout: 5000, //超时时间的设置,超出5秒请求就失败
});

// 请求拦截器
request.interceptors.request.use((config) => {
  // config :请求拦截器回调注入的对象(配置对象),p配置对象的身上最重要的一件事情headers属性
  //   k可以通过请求头携带公共参数-token
  return config;
});
// 响应拦截器
request.interceptors.response.use(
  (response) => {
    // 响应拦截器成功地回调,一般会进行简化
    return response;
  },
  (error) => {
    // 处理http网络错误
    let status = error.response.status;
    switch (status) {
      case 404:
        // 错误提示信息
        ElMessage({
          type: "error",
          //   message: error.message,
          message: "请求失败路径,出现问题",
        });
        break;
      case 401:
        // 错误提示信息
        ElMessage({
          type: "error",
          //   message: error.message,
          message: "参数有误",
        });
        break;
      case 500 | 501 | 502 | 503 | 504 | 505:
        // 错误提示信息
        ElMessage({
          type: "error",
          //   message: error.message,
          message: "服务器挂了",
        });
        break;
    }
    return Promise.reject(new Error(error.message));
  }
);

// 务必对外暴露axios
export default request;


九、配置代理跨域

image.png

十、分页及获取数据的实现

10.1 获取数据接口配置
// 统一管理首页模块接口
import request from "@/utils/request";
// 通过枚举管理首页模块的接口地址
enum API {
  // 获取已有的医院的是数据接口地址
  HOSPITAL_URL = "/hosp/hospital/",
}
// 获取医院的数据
export const reqHospital = (page: number, limit: number) =>
  request.get(API.HOSPITAL_URL + `${page}/${limit}`);

image.png

10.2 获取首页数据

image.png

image.png

10. 分页功能

image.png

image.png

十一、等级和区域展示

11.1等级及高亮展示
接口类型

image.png

image.png

image.png

11.2获取等级数据

image.png

image.png

image.png

image.png

11.3获取医院地区数据

image.png

image.png

高亮显示

image.png

image.png

11.4首页根据等级与地区筛选医院展示

给组件绑定事件

image.png

image.png

image.png

image.png

image.png image.png

image.png 暂无数据

image.png

十二、首页根据关键字搜索医院及路由简单跳转

接口ts类型

image.png

数据接口

image.png

绑定事件

image.png

image.png 当用户搜索关键字时,会出现推荐数据项

image.png

image.png 因为官方文档中写的是需要返回value值,所以就弄了一个showData函数,让其返回一个对象(对象的话就会有吧value值)

image.png

路由跳转三步走

image.png

十三、首页常见科室搭建

image.png

image.png

image.png

十四、医院详情菜单与子路由

image.png

建五个文件

image.png

路由菜单

image.png

点击这些组件就会进行跳转

image.png

image.png

点击搜索框的推荐数据,也会进行跳转到详情页

image.png

image.png

十五、Pinia仓库存储医院详情数据

pnpm i pinia@2.1.3(注意不要安装最新版,否则会有冲突报错)

image.png

image.png

拿数据->取数据->存数据

image.png

十六、预约挂号业务1

image.png

image.png

image.png

base64码转图片

data:image/jpeg;base64,

 <img :src="`data:image/jpeg;base64,`+hospitalStore.hospitalInfo.hospital?.logoData" alt=""/>

16.2、医院详情业务

image.png

<template>
  <div class="detail">
    <!-- 医院名字与等级 -->
    <div class="top">
      <div class="name">{{ hospitalStore.hospitalInfo.hospital?.hosname }}</div>
      <div class="level">
        <svg
          t="1688813652495"
          class="icon"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="9729"
          width="16"
          height="16"
        >
          <path
            d="M939.358251 423.424662c-23.01825-37.252439-62.924121-60.779272-107.019409-63.209624-2.755764-0.38681-5.510504-0.579191-8.347109-0.579191l-152.202471-0.121773c6.649444-28.975938 10.015098-58.653865 10.015098-88.657202 0-28.223808-3.213181-57.139372-9.556657-85.952604-0.447185-2.043542-1.098008-4.006244-1.932002-5.866614-15.820314-57.302077-67.37755-96.841605-127.282918-96.841605-72.827679 0-132.081201 59.254545-132.081201 132.081201 0 3.334955 0.132006 6.66991 0.406253 10.015098-2.196015 57.211003-32.108279 109.947088-80.269162 141.363611-14.447037 9.42465-18.524912 28.773324-9.099239 43.220361 9.414417 14.437827 28.752858 18.535145 43.220361 9.099239 65.811892-42.925648 106.429984-115.325585 108.656699-193.684234 0.030699-1.332345-0.010233-2.663666-0.14224-3.996011-0.203638-2.012843-0.304945-4.016477-0.304945-6.019087 0-38.381146 31.233352-69.614497 69.614497-69.614497 32.57593 0 60.474326 22.204721 67.824735 53.997821 0.356111 1.534959 0.823761 3.019777 1.402953 4.453429 4.696975 22.814612 7.076162 45.579081 7.076162 67.743894 0 37.485753-6.222725 74.352405-18.494213 109.592001-3.324722 9.546424-1.819438 20.111037 4.02671 28.345582 5.856381 8.245801 15.332197 13.146415 25.448602 13.156648l193.226816 0.101307c1.423419 0.264013 2.857071 0.426719 4.300956 0.477884 24.116257 0.9967 45.935192 13.614066 58.603723 34.090423 7.838525 12.31242 11.438517 26.800389 10.431583 41.939181-0.080841 0.945535-0.121773 1.911536-0.11154 2.877537 0 0.854461 0.040932 1.697665 0.11154 2.53166 0.010233 0.335644-0.030699 0.661056-0.11154 0.976234-0.101307 0.376577-0.193405 0.772596-0.284479 1.159406l-74.972529 330.391802c-0.914836 1.281179-1.738597 2.6432-2.449795 4.046153-5.937223 11.762905-14.660908 21.48329-25.346271 28.172643-10.746762 6.812149-23.059182 10.614755-35.757388 11.06194-0.854461-0.061398-513.766226-0.224104-513.766226-0.224104-0.467651-0.020466-0.935302-0.030699-1.402953-0.030699 0 0-111.01542 0.172939-112.718201 0.457418-1.932002 0-3.446495-1.50426-3.446495-3.415796l0.299829-416.334173c0-1.901303 1.545192-3.446495 3.01466-3.456728l1.245364 0.121773c1.174756 0.132006 2.653433 0.284479 3.52836 0.193405l83.82822-0.222057 0 339.367221c0 17.253966 13.979386 31.233352 31.233352 31.233352s31.233352-13.979386 31.233352-31.233352L281.009092 435.350273c0-1.778506 0-8.631588 0-10.411117 0-17.253966-13.979386-30.928407-31.233352-30.928407-1.50426 0-117.547183 0.304945-119.402437 0.304945-36.34272 0-65.913199 29.566386-65.913199 65.893756l-0.299829 416.334173c0 36.337603 29.571503 65.902966 65.913199 65.902966 2.541893 0 111.406323-0.457418 111.406323-0.457418 0.457418 0.020466 0.925069 0.030699 1.382487 0.030699 0 0 511.458671 0.274246 512.505513 0.274246 25.469068 0 50.296523-7.197936 71.647807-20.73116 19.612687-12.281721 35.777855-29.881564 46.839795-50.967812 3.660366-5.622044 6.435573-11.875468 8.256034-18.615986 0.11154-0.416486 0.213871-0.823761 0.304945-1.240247l74.881454-330.340637c1.596358-6.212492 2.257413-12.586666 2.00261-18.992563C960.892707 473.366098 953.948551 446.331371 939.358251 423.424662z"
            fill="#d81e06"
            p-id="9730"
          ></path>
          <path
            d="M450.027553 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C481.260905 532.629853 467.281519 518.650467 450.027553 518.650467z"
            fill="#d81e06"
            p-id="9731"
          ></path>
          <path
            d="M693.805696 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C725.039048 532.629853 711.058638 518.650467 693.805696 518.650467z"
            fill="#d81e06"
            p-id="9732"
          ></path>
          <path
            d="M648.940882 660.09492c-14.304797-9.393951-33.592073-5.398964-43.159986 8.763594-0.132006 0.193405-13.614066 19.754926-34.171264 19.754926-19.98824 0-32.423457-18.09717-33.266661-19.368116-9.17087-14.427594-28.254507-18.809391-42.834574-9.770528-14.650675 9.109472-19.155269 28.366048-10.055007 43.016723 11.214413 18.047028 41.96988 48.588625 86.156242 48.588625 43.962258 0 75.104535-30.318516 86.572728-48.222281C667.404396 688.461991 663.216004 669.500127 648.940882 660.09492z"
            fill="#d81e06"
            p-id="9733"
          ></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=""
        />
      </div>
      <div class="right">
        <div class="address">
          <svg
            t="1688828004474"
            class="icon"
            viewBox="0 0 1024 1024"
            version="1.1"
            xmlns="http://www.w3.org/2000/svg"
            p-id="10870"
            width="16"
            height="16"
          >
            <path
              d="M512 512a136.533333 136.533333 0 1 1 136.533333-136.533333 136.533333 136.533333 0 0 1-136.533333 136.533333z m0-219.272533a81.92 81.92 0 1 0 81.92 81.92 81.92 81.92 0 0 0-81.92-81.92z"
              fill="#0073FF"
              p-id="10871"
            ></path>
            <path
              d="M512 831.214933a27.306667 27.306667 0 0 1-19.2512-8.055466l-214.493867-214.357334a330.5472 330.5472 0 1 1 467.490134 0l-214.357334 214.357334a27.306667 27.306667 0 0 1-19.387733 8.055466z m0-732.091733a275.933867 275.933867 0 0 0-195.106133 471.04L512 765.269333l195.106133-195.106133A275.933867 275.933867 0 0 0 512 99.1232z"
              fill="#0073FF"
              p-id="10872"
            ></path>
            <path
              d="M514.321067 979.490133c-147.456 0-306.107733-37.000533-306.107734-118.3744 0-45.602133 51.746133-81.92 145.681067-102.4a27.306667 27.306667 0 1 1 11.605333 53.384534c-78.370133 17.066667-102.673067 41.915733-102.673066 49.015466 0 18.432 88.064 63.761067 251.4944 63.761067s251.4944-45.192533 251.4944-63.761067c0-7.3728-25.258667-32.768-106.496-49.834666a27.306667 27.306667 0 1 1 11.195733-53.384534c96.6656 20.343467 150.186667 56.9344 150.186667 103.2192-0.273067 80.964267-158.9248 118.3744-306.3808 118.3744z"
              fill="#0073FF"
              p-id="10873"
            ></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>

<script setup lang="ts">
// 获取医院详情仓库数据进行展示
import useDetailStore from "@/store/modules/hospitalDetail";
let hospitalStore = useDetailStore();
</script>

<style scoped lang="scss">
.detail {
  .top {
    display: flex;
    align-items: center;
    .name {
      font-size: 30px;
      font-weight: 900;
    }
    .level {
      color: #7f7f7f;
      margin-left: 10px;
      span {
        margin-left: 5px;
      }
    }
  }
  .logo {
    display: flex;
    margin: 10px 0px;
    .left {
      img {
        width: 80px;
        height: 80px;
        border-radius: 50px;
      }
    }
    .right {
      color: #7f7f7f;
      margin-left: 10px;
      div {
        margin: 10px 0px;
      }
    }
  }
  .footer {
    color: #7f7f7f;
    p {
      margin-top: 10px;
      line-height: 30px;
    }
  }
}
</style>

16.3 小bug

第一次进去本来是能够显示的,而且带有编号,但是但切换菜单时,编号就消失了

image.png

image.png

16.4做科室时遇见的错误

image.png

image.png 错误原因:在箭头后面加对象符号{},但是却没有return 回去,导致打印出来的result是undefine。

解决办法:1、不加{},因为箭头函数如果只有一行表达式,可以不加括号,箭头函数自带return。 2、 或者在箭头后面加{return xxxxx},不要忘记return,不然会出现错误

image.png

16.5、医院科室业务

效果

image.png

左侧导航栏及高亮效果

image.png image.png

image.png

右侧信息及点击哪个导航就定位到对应的科室

image.png

image.png

完整代码:

<template>
  <div class="register">
    <div class="top">
      <div class="title">
        {{ hospitalStore.hospitalInfo.hospital?.hosname }}
      </div>
      <div class="level">
        <svg
          t="1688813652495"
          class="icon"
          viewBox="0 0 1024 1024"
          version="1.1"
          xmlns="http://www.w3.org/2000/svg"
          p-id="9729"
          width="16"
          height="16"
        >
          <path
            d="M939.358251 423.424662c-23.01825-37.252439-62.924121-60.779272-107.019409-63.209624-2.755764-0.38681-5.510504-0.579191-8.347109-0.579191l-152.202471-0.121773c6.649444-28.975938 10.015098-58.653865 10.015098-88.657202 0-28.223808-3.213181-57.139372-9.556657-85.952604-0.447185-2.043542-1.098008-4.006244-1.932002-5.866614-15.820314-57.302077-67.37755-96.841605-127.282918-96.841605-72.827679 0-132.081201 59.254545-132.081201 132.081201 0 3.334955 0.132006 6.66991 0.406253 10.015098-2.196015 57.211003-32.108279 109.947088-80.269162 141.363611-14.447037 9.42465-18.524912 28.773324-9.099239 43.220361 9.414417 14.437827 28.752858 18.535145 43.220361 9.099239 65.811892-42.925648 106.429984-115.325585 108.656699-193.684234 0.030699-1.332345-0.010233-2.663666-0.14224-3.996011-0.203638-2.012843-0.304945-4.016477-0.304945-6.019087 0-38.381146 31.233352-69.614497 69.614497-69.614497 32.57593 0 60.474326 22.204721 67.824735 53.997821 0.356111 1.534959 0.823761 3.019777 1.402953 4.453429 4.696975 22.814612 7.076162 45.579081 7.076162 67.743894 0 37.485753-6.222725 74.352405-18.494213 109.592001-3.324722 9.546424-1.819438 20.111037 4.02671 28.345582 5.856381 8.245801 15.332197 13.146415 25.448602 13.156648l193.226816 0.101307c1.423419 0.264013 2.857071 0.426719 4.300956 0.477884 24.116257 0.9967 45.935192 13.614066 58.603723 34.090423 7.838525 12.31242 11.438517 26.800389 10.431583 41.939181-0.080841 0.945535-0.121773 1.911536-0.11154 2.877537 0 0.854461 0.040932 1.697665 0.11154 2.53166 0.010233 0.335644-0.030699 0.661056-0.11154 0.976234-0.101307 0.376577-0.193405 0.772596-0.284479 1.159406l-74.972529 330.391802c-0.914836 1.281179-1.738597 2.6432-2.449795 4.046153-5.937223 11.762905-14.660908 21.48329-25.346271 28.172643-10.746762 6.812149-23.059182 10.614755-35.757388 11.06194-0.854461-0.061398-513.766226-0.224104-513.766226-0.224104-0.467651-0.020466-0.935302-0.030699-1.402953-0.030699 0 0-111.01542 0.172939-112.718201 0.457418-1.932002 0-3.446495-1.50426-3.446495-3.415796l0.299829-416.334173c0-1.901303 1.545192-3.446495 3.01466-3.456728l1.245364 0.121773c1.174756 0.132006 2.653433 0.284479 3.52836 0.193405l83.82822-0.222057 0 339.367221c0 17.253966 13.979386 31.233352 31.233352 31.233352s31.233352-13.979386 31.233352-31.233352L281.009092 435.350273c0-1.778506 0-8.631588 0-10.411117 0-17.253966-13.979386-30.928407-31.233352-30.928407-1.50426 0-117.547183 0.304945-119.402437 0.304945-36.34272 0-65.913199 29.566386-65.913199 65.893756l-0.299829 416.334173c0 36.337603 29.571503 65.902966 65.913199 65.902966 2.541893 0 111.406323-0.457418 111.406323-0.457418 0.457418 0.020466 0.925069 0.030699 1.382487 0.030699 0 0 511.458671 0.274246 512.505513 0.274246 25.469068 0 50.296523-7.197936 71.647807-20.73116 19.612687-12.281721 35.777855-29.881564 46.839795-50.967812 3.660366-5.622044 6.435573-11.875468 8.256034-18.615986 0.11154-0.416486 0.213871-0.823761 0.304945-1.240247l74.881454-330.340637c1.596358-6.212492 2.257413-12.586666 2.00261-18.992563C960.892707 473.366098 953.948551 446.331371 939.358251 423.424662z"
            fill="#d81e06"
            p-id="9730"
          ></path>
          <path
            d="M450.027553 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C481.260905 532.629853 467.281519 518.650467 450.027553 518.650467z"
            fill="#d81e06"
            p-id="9731"
          ></path>
          <path
            d="M693.805696 518.650467c-17.253966 0-31.233352 13.979386-31.233352 31.233352l0 30.470989c0 17.253966 13.979386 31.233352 31.233352 31.233352 17.253966 0 31.233352-13.979386 31.233352-31.233352l0-30.470989C725.039048 532.629853 711.058638 518.650467 693.805696 518.650467z"
            fill="#d81e06"
            p-id="9732"
          ></path>
          <path
            d="M648.940882 660.09492c-14.304797-9.393951-33.592073-5.398964-43.159986 8.763594-0.132006 0.193405-13.614066 19.754926-34.171264 19.754926-19.98824 0-32.423457-18.09717-33.266661-19.368116-9.17087-14.427594-28.254507-18.809391-42.834574-9.770528-14.650675 9.109472-19.155269 28.366048-10.055007 43.016723 11.214413 18.047028 41.96988 48.588625 86.156242 48.588625 43.962258 0 75.104535-30.318516 86.572728-48.222281C667.404396 688.461991 663.216004 669.500127 648.940882 660.09492z"
            fill="#d81e06"
            p-id="9733"
          ></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=""
        />
      </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 class="deparment">
      <div class="leftNav">
        <ul>
          <li
            v-for="(deparment, index) in hospitalStore.deparmentArr"
            :class="{ active: index == currentIndex }"
            @click="changeIndex(index)"
            :key="deparment.depcode"
          >
            {{ deparment.depname }}
          </li>
        </ul>
      </div>
      <div class="deparmentInfo">
        <!-- 用一个div代表:大科室与小科室 -->
        <div
          class="showDeparment"
          v-for="deparment in hospitalStore.deparmentArr"
          :key="deparment.depcode"
        >
          <h1 class="cur">{{ deparment.depname }}</h1>
          <!-- 每一个大的科室下小科室 -->
          <ul>
            <li v-for="item in deparment.children" :key="item.depcode">
              {{ item.depname }}
            </li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
// 引入医院详情仓库的数据
import useDetailStore from "@/store/modules/hospitalDetail";
let hospitalStore = useDetailStore();
// 控制科室高亮的响应式数据
let currentIndex = ref<number>(0);
// 左侧大的科室点击的事件
const changeIndex = (index: number) => {
  currentIndex.value = index;
  // 点击导航获取右侧科室(大的科室好标题)
  let allH1 = document.querySelectorAll(".cur");
  // console.log(allH1);
  // 滚动到对应科室的位置
  allH1[currentIndex.value].scrollIntoView({
    behavior: "smooth",//过渡动画效果
    block:'start',//滚动到的位置,默认是起始位置
  });
};
</script>

<style scoped lang="scss">
.register {
  .top {
    display: flex;
    .title {
      font-size: 30px;
      font-weight: 900;
    }
    .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;
        boder-radius: 50%;
      }
    }
    .right {
      /* 左边占了80px,剩下的空间给右边 */
      flex: 1;
      font-size: 14px;
      margin-left: 20px;
      .time,
      .route,
      .address,
      .releasetime,
      .ruleInfo {
        margin-top: 10px;
        color: #7f7f7f;
      }
      .rule {
        margin: 10px 0;
      }
    }
  }
  .deparment {
    width: 100%;
    height: 500px;
    display: flex;
    margin-top: 20px;
    /* background: pink; */
    .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: 30px;
          /* 高亮效果 */
          &.active {
            border-left: 1px solid red;
            color: red;
            background: white;
          }
        }
      }
    }
    .deparmentInfo {
      /* 除了 leftNav占了80px,其他都是deparmentInfo*/
      flex: 1;
      margin-left: 20px;
      height: 100%;
      overflow: auto;
      /* 清除滚动条 */
      &::-webkit-scrollbar {
        display: none;
      }
      .showDeparment {
        h1 {
          background: rgb(248, 248, 248);
          color: #7f7f7f;
        }
        ul {
          display: flex;
          flex-wrap: wrap;
          li {
            width: 33%;
            color: #7f7f7f;
            line-height: 30px;
          }
        }
      }
    }
  }
}
</style>

16.6、登录静态搭建

注册为全局组件main.ts

image.png

image.png

image.png

完整代码

<template>
  <div class="login-container">
    <!-- title:对话框左上角的标题  v-model :控制对话框的显示与隐藏 -->
    <el-dialog v-model="userStore.visiable" title="用户登录">
      <!-- 对话框身体部分结构 -->
      <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 type="primary" style="width: 100%"
                  >用户登陆</el-button
                >
                <div class="bottom" @click="changeScene">
                  <p>微信扫码登录</p>
                  <svg
                    t="1688921974790"
                    class="icon"
                    viewBox="0 0 1024 1024"
                    version="1.1"
                    xmlns="http://www.w3.org/2000/svg"
                    p-id="15061"
                    width="32"
                    height="32"
                  >
                    <path
                      d="M1023.162182 641.303273c0-143.243636-143.243636-260.910545-306.944-260.910546-168.820364 0-306.944 117.666909-306.944 260.910546s133.003636 260.910545 306.944 260.910545c35.816727 0 71.633455-10.24 107.426909-20.456727l97.210182 56.273454-25.576728-92.090181c71.633455-56.273455 127.883636-127.906909 127.883637-204.637091z m-404.130909-46.056728c-20.456727 0-35.816727-15.36-35.816728-35.816727 0-15.336727 20.456727-35.816727 35.816728-35.816727 25.576727 0 46.056727 15.36 46.056727 35.816727-0.023273 15.36-20.48 35.816727-46.056727 35.816727z m194.397091 0c-15.336727 0-35.816727-15.36-35.816728-35.816727 0-15.336727 20.456727-35.816727 35.816728-35.816727 25.576727 0 46.056727 15.36 46.056727 35.816727 0 15.36-15.36 35.816727-46.056727 35.816727z m0 0"
                      fill="#04AE0F"
                      p-id="15062"
                    ></path>
                    <path
                      d="M358.097455 104.122182c-199.517091 0-358.120727 133.026909-358.120728 306.967273 0 97.210182 56.273455 179.060364 143.243637 240.453818L107.426909 758.970182l127.906909-61.393455c46.033455 10.24 81.850182 20.456727 127.906909 20.456728h35.816728c-5.12-25.576727-10.216727-51.153455-10.216728-76.730182 0-158.580364 133.003636-286.510545 306.944-286.510546h35.816728C690.641455 211.549091 537.157818 104.122182 358.097455 104.122182z m-112.54691 240.453818c-25.576727 0-56.273455-15.36-56.273454-46.056727 0-25.6 25.6-46.056727 56.273454-46.056728 25.576727 0 46.056727 15.36 46.056728 46.056728-5.12 30.696727-20.456727 46.056727-46.056728 46.056727z m250.693819 0c-25.576727 0-56.273455-15.36-56.273455-46.056727 0-25.6 25.576727-46.056727 56.273455-46.056728 25.576727 0 46.056727 15.36 46.056727 46.056728-0.023273 30.696727-20.48 46.056727-46.056727 46.056727z m0 0"
                      fill="#04AE0F"
                      p-id="15063"
                    ></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="1688921974790"
                    class="icon"
                    viewBox="0 0 1024 1024"
                    version="1.1"
                    xmlns="http://www.w3.org/2000/svg"
                    p-id="15061"
                    width="16"
                    height="16"
                  >
                    <path
                      d="M1023.162182 641.303273c0-143.243636-143.243636-260.910545-306.944-260.910546-168.820364 0-306.944 117.666909-306.944 260.910546s133.003636 260.910545 306.944 260.910545c35.816727 0 71.633455-10.24 107.426909-20.456727l97.210182 56.273454-25.576728-92.090181c71.633455-56.273455 127.883636-127.906909 127.883637-204.637091z m-404.130909-46.056728c-20.456727 0-35.816727-15.36-35.816728-35.816727 0-15.336727 20.456727-35.816727 35.816728-35.816727 25.576727 0 46.056727 15.36 46.056727 35.816727-0.023273 15.36-20.48 35.816727-46.056727 35.816727z m194.397091 0c-15.336727 0-35.816727-15.36-35.816728-35.816727 0-15.336727 20.456727-35.816727 35.816728-35.816727 25.576727 0 46.056727 15.36 46.056727 35.816727 0 15.36-15.36 35.816727-46.056727 35.816727z m0 0"
                      fill="#515151"
                      p-id="15062"
                    ></path>
                    <path
                      d="M358.097455 104.122182c-199.517091 0-358.120727 133.026909-358.120728 306.967273 0 97.210182 56.273455 179.060364 143.243637 240.453818L107.426909 758.970182l127.906909-61.393455c46.033455 10.24 81.850182 20.456727 127.906909 20.456728h35.816728c-5.12-25.576727-10.216727-51.153455-10.216728-76.730182 0-158.580364 133.003636-286.510545 306.944-286.510546h35.816728C690.641455 211.549091 537.157818 104.122182 358.097455 104.122182z m-112.54691 240.453818c-25.576727 0-56.273455-15.36-56.273454-46.056727 0-25.6 25.6-46.056727 56.273454-46.056728 25.576727 0 46.056727 15.36 46.056728 46.056728-5.12 30.696727-20.456727 46.056727-46.056728 46.056727z m250.693819 0c-25.576727 0-56.273455-15.36-56.273455-46.056727 0-25.6 25.576727-46.056727 56.273455-46.056728 25.576727 0 46.056727 15.36 46.056727 46.056728-0.023273 30.696727-20.48 46.056727-46.056727 46.056727z m0 0"
                      fill="#515151"
                      p-id="15063"
                    ></path>
                  </svg>
                  <p>微信扫一扫关注</p>
                  <p>“快速预约挂号”</p>
                </div>
                <div class="item">
                  <img src="../../assets/images/code_login_wechat.png" alt="" />
                  <svg
                    t="1688923491762"
                    class="icon"
                    viewBox="0 0 1024 1024"
                    version="1.1"
                    xmlns="http://www.w3.org/2000/svg"
                    p-id="16931"
                    width="16"
                    height="16"
                  >
                    <path
                      d="M766.003899 974.097466H282.947368a47.906433 47.906433 0 0 1-47.906432-47.906433V83.836257a47.906433 47.906433 0 0 1 47.906432-47.906432h483.056531a47.906433 47.906433 0 0 1 47.906432 47.906432v842.354776a47.906433 47.906433 0 0 1-47.906432 47.906433z m-241.528265-57.88694a61.879142 61.879142 0 1 0-61.879143-61.879142 61.879142 61.879142 0 0 0 61.879143 61.879142z m202.604288-791.454191H321.871345v598.83041h405.208577V124.756335z"
                      fill="#E61D15"
                      p-id="16932"
                    ></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>

<script setup lang="ts">
import { User, Lock } from "@element-plus/icons-vue";
// 获取user仓库的数据visiable,可以控制login组件的对话框显示与隐藏
// import useUserStore from '@/store/mod'
import useUserStore from "@/store/modules/user";
import { ref } from "vue";
let userStore = useUserStore();
let scene = ref<number>(0); //0代表手机号码登录,1代表微信扫码登录

// 点击微信扫码登录|微信小图标切换为微信扫码登录
const changeScene = () => {
  scene.value = 1;
};
</script>

<script lang="ts">
// 用vue2写个别名,方便vue插件寻找数据
export default {
  name: "Login",
};
</script>

<style scoped lang="scss">
.login-container {
  /* 要修改组件内部的样式,就要用深度选择器 */
  :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>

16.7、登录组件获取验证码

接口设置

image.png 输入框验证规则

image.png image.png

image.png

获取验证码的方法

image.png 放在仓库中

image.png

16.8获取验证码倒计时业务

将倒计时组件进行封装

image.png

用v-if v-else 判断展示哪个组件,当flag为真时,倒计时组件开启,否则关闭。

还有就是当你输入的手机号码不符合规定时或者当开启倒计时后,获取验证码的按钮将会被禁用

image.png

image.png

image.png

十七、登录业务功能

登录相关的ts类型

image.png

登录接口

image.png

登录请求 image.png

image.png

登录逻辑

发送登录请求

登录请求成功:顶部组件需要展示用户名字/对话框关闭

登录失败:弹出对应登录失败的错误信息

image.png

image.png

17.1、普通表单校验规则(一般不用)

image.png

image.png

17.2、自定义表单校验

image.png

image.png

image.png

image.png

image.png

17.3、关闭对话框时表单清空

image.png

image.png

方法二: 当对话框关闭时,直接销毁Login组件

APP.vuew image.png

17.4、退出登录

image.png

image.png

清除本地存储和仓库存储的数据

image.png

image.png

17.5、微信扫码登录之二维码嵌套在login内部

image.png

image.png

image.png

image.png

image.png

17.6 微信扫码登录

image.png

image.png

image.png

image.png

image.png

image.png