通过双向数据绑定(v-model)和相关组件来完成如下效果
@select="goDetail"
:trigger-on-focus="false"
clearable
placeholder="请你输入医院名称"
v-model="hosname"
:fetch-suggestions="fetchData"
/>
...
...
...
import {useRouter} from "vue-router";
// 创建路由器对象
let $router = useRouter();
import { ref } from "vue";
// 收集搜索的关键字(医院的关键字)
let hosname = ref("");
// 顶部组件的回调
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) => {
// 点击推荐项进入医院详情页,将来需要携带query参数医院编码
$router.push({path:'/hospital/register',query:{hoscode:item.hoscode}})
};
:fetch-suggestions="fetchData"获取输入建议的方法,仅当你的输入建议数据resolve 时,通过调用callback(data:[])来返回它
正常的流程就是输入搜索的关键字,会弹出一系列的对应名字,上面代码中的value和 hoscode是输入关键字后的回调,如果整理数据,这里会出不来,整理数据是因为咋们需要的是“名字”,但是接口里面是"value"
其中keyword代表的是关键字,cd是回调函数,这里展示的是整理完数据后的
其次点击查询出来的名字还能进行页面跳转,定义一个方法goDetail,可以接受item,即为选择的那一项,可以把选择的那一项注入将来,并且引入useRouter,创建路由器对象,携带query参数医院编码是将来要用的,因为你只有携带了医院的编码才能跟后台发起请求,来去获取数据
可维护性: 使用枚举将所有接口地址集中在一个地方,使得代码更易维护。如果需要修改接口地址或者添加新的接口,只需在枚举中进行修改,而不必在整个代码中查找和修改多处引用。
避免硬编码: 枚举提供了有意义的命名,避免了在代码中直接硬编码接口地址。这样可以减少因拼写错误或复制粘贴错误而引起的 bug,并提高代码的可读性。
类型安全: TypeScript 的枚举可以提供类型安全。这意味着在使用这些接口地址时,编辑器会提供自动补全和类型检查,减少由于类型不匹配而引起的错误。
集中管理: 将相关的接口地址集中在一个枚举中,有助于组织和管理这些地址。这种集中管理使得团队协作更加容易,新成员可以更快地了解项目的接口结构。
pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。就是和vuex一样的实现数据共享。
Pinia 的优点:
pinia 符合直觉,易于学习。
pinia 是轻量级状态管理工具,大小只有1KB.
pinia 模块化设计,方便拆分。
pinia 没有 mutations,直接在 actions 中操作 state,通过 this.xxx 访问响应的状态,尽管可 以直接操作 state,但是还是推荐在 actions 中操作,保证状态不被意外的改变。
store 的 action 被调度为常规的函数调用,而不是使用 dispatch 方法或者是 MapAction 辅助函数,这是在 Vuex 中很常见的。
支持多个 store。 支持 Vue devtools、SSR、webpack 代码拆分。
安装命令:这里不要安装最新版,要 pnpm i [email protected],不然后面要重装的
他没有mutations,modules
import { defineStore } from "pinia";
//定义小仓库的方法
// pinia仓库写法:组合式api 选择式api
import { reqHospitalDetail, reqHospitalDeparment } from "@/api/hospital";
// ts类型
const useDetailStore = defineStore("Detail", {
state: (): DetailState => {
return {
};
},
actions: {
},getters: {},
});
export default useDetailStore;
该scrollIntoView()
方法将调用它的元素滚动到浏览器窗口的可见区域。
PS:根据其他元素的布局,元素可能无法完全滚动到顶部或底部。
TIPS:页面(容器)可滚动时才有用!
应用场景:
URL中hash标记的进化
聊天窗口滚动显示最新的消息
往一个列表添加item后滚动显示最新的添加的item
回到顶部(#)
滚动到指定位置(#xxx)
示例:
var element = document.getElementById("box");
element.scrollIntoView();
element.scrollIntoView(false);
element.scrollIntoView({block: "end"});
element.scrollIntoView({behavior: "instant", block: "end", inline: "nearest"});
用户点击验证码获取验证码,并且设置倒计时
这里也是用到pinia仓库,
// 引入获取验证码的请求方法
import { reqCode } from "@/api/hospital";
//引入倒计时组件
import CountDown from "../../components/countdown/index.vue";
const useUserStore = defineStore("User", {
state(): UserState {
return {
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: {},
});
export default useUserStore;
首先引入获取验证码的请求方法,在 actions中定义获取验证码的方法,获取成功之后并且将数据存储到code中。
获取成功之后返回一个成功的promise对象, return "ok"; 否则失败则返回一个失败的promise对象
获取验证码
...
...
...
// 定义一个响应式数据控制倒计时显示与隐藏
let flag = ref(false); //flag如果为真,开启倒计时,为假并非倒计时
// 获取验证码按钮的回调
const getCode = async () => {
// 解决element-plus按钮禁用还能点击的问提
if (!isPhone.value || flag.value) return;// 开启倒计时 倒计时组件显示出来
flag.value = true;// 通知pinia仓库存验证码
try {
// 获取验证码成功
await userStore.getCode(loginParam.phone);
loginParam.code = userStore.code;
} catch (error) {
// 获取验证码失败
ElMessage({
type: "error",
message: (error as Error).message,
});
}
};
// 计数器子组件绑定的自定义事件
// 当倒计时为0的时候通知父组件倒计时组件应该隐藏
const getFlag = (val: boolean) => {
// 倒计时模式结束
flag.value = val;
};
因为返回一个成功的promise对象,所有就可以使用try方法,验证码就可以直接使用仓库中的数据
倒计时方法封装成组件后,也需要去仓库中引用,开始是显示,点击获取到验证码之后会禁用,使用watch来监听父组件传递过来的props数据变化
获取验证码({{ time }})
//微信开放平台官网地址 微信开放平台 //查看微信扫码登录文档 微信公众平台
const changeScene = async () => {
// 切换场景为1
scene.value = 1;
//发请求获取微信扫码二维码需要参数
//咱们在向学校的服务器发请求,获取微信扫码登录页面参数
//还需要携带一个参数:告诉学校服务器用户授权成功以后重定向项目某一个页面
let redicert_URL = encodeURIComponent(window.location.origin + '/wxlogin');
let result: WXLoginResponseData = await reqWxLogin(redicert_URL);
//生成微信扫码登录二维码页面
new WxLogin({
self_redirect: true,//true:手机点击确认登录后可以在 iframe 内跳转到 redirect_uri,false:手机点击确认登录后可以在 top window 跳转到 redirect_uri。默认为 false。
id: "login_container",//显示二维码的容器设置
appid: result.data.appid,//应用的唯一标识appid
scope: "snsapi_login",//代表微信扫码登录的页面已经授权了
redirect_uri: result.data.redirectUri,//填写授权回调域路径,就是用户授权成功以后微信服务器向后台推送code地址
state: result.data.state,//state就是学校的服务器重定向的地址携带用户信息
style: "black",//
href: ""
});
}
encodeURIComponent 编码 如果不进行编码,打印出来的是本地的路径,并且后台需要的是编码后的数据,最后加上封装后的组件路径
当我们扫码完成之后就可以吧微信二维码完成之后的样式清除
// 持久化存储用户信息
SET_TOKEN(JSON.stringify($route.query));
// 此路由组件代码执行:说明授权成功
let html: any = document.querySelector('html');
html.style.display = 'none';
安装命令 pnpm i nprogress
// 引入进度条
import NProgress from "nprogress";
// 引入进度条样式
import "nprogress/nprogress.css";
// 进度条的加载,关闭转圈
NProgress.configure({ showSpinner: false });
// 引入路由器
import router from "./router";
// 添加相应的全局守卫
// 前置守卫
router.beforeEach((to, from, next) => {
// 访问路由组件的之前,进度条开始动
NProgress.start();
});
// 后置路由
router.afterEach((to, from) => {
// 访问路由组件进度条结束
NProgress.done();
});
在路由配置中添加相关代码 meta,例如
path: "register",
component: () => import("@/pages/hospital/register/index.vue"),
meta: {
title: "预约挂号",
},
在前置守卫中
// 前置守卫
router.beforeEach((to, from, next) => {
//动态设置网页左上角的标题
document.title = `家康-${to.meta.title}`;
});
路由鉴权:就是路由能不能被访问权限设置->全局守卫完成
在使用路由鉴权前,首先,安装 Vue Router 库:
npm install vue-router
Vue 提供了三种导航守卫:
全局前置守卫(beforeEach):在路由跳转之前执行,可以用来进行权限验证、页面加载前的操作等。 全局解析守卫(beforeResolve):在路由组件被解析之后,渲染之前执行。 全局后置钩子(afterEach):在路由跳转之后执行,可以用来进行日志记录、页面跳转之后的操作等。 另外,还可以在路由配置中使用路由独享的守卫,比如:
路由独享的前置守卫(beforeEnter):只对某个具体的路由生效。 组件内的守卫(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave):在路由组件内部使用,可以访问组件实例。
// 存储用户未登录访问的路由路径
let whiteList = [
"/home",
"/hospital/register",
"/hospital/detail",
"/hospital/notice",
"/hospital/close",
"/hospital/search",
"/wxlogin",
];
// 前置守卫
router.beforeEach((to, from, next) => {
// 判断用户是否登录
let token = userStore.userInfo.token;
// 有tokne就是用户登录 没有反之
if (token) {
next();
} else {
// 用户未登录
if (whiteList.includes(to.path)) {
// 在免登录白名单,直接进入
next();
} else {
//登录组件
userStore.visiable = true;
next({ path: "/home", query: { redirect: to.fullPath } });
}
}
});
用户未登录可以访问whiteList里面相关的页面,并且未登录会跳转到首页,并且弹出登录组件,这里携带了一个参数,代表的是曾经想去一个页面却没去成的路径,在这里携带之后,并且在登录回调设置相关的跳转,再次登录会跳转到曾经想去的页面
登录回调设置:
// 点击用户登录按钮回调
const login = async () => {
// 发起登录请求
// 登录请求成功 顶部组件需要展示用户名字对话框关闭
// 登录请求失败 弹出对应登录失败的错误信息
// 保证表单效验符合条件
await form.value.validate();
try {
// 用户登录成功
await userStore.userLogin(loginParam);
// 关闭对话框
userStore.visiable = false;
// 获取url的query参数
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,
});
}
};