前端TS项目中遇到过的难点(二)

12.根据关键字搜索

通过双向数据绑定(v-model)和相关组件来完成如下效果

前端TS项目中遇到过的难点(二)_第1张图片

          @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参数医院编码是将来要用的,因为你只有携带了医院的编码才能跟后台发起请求,来去获取数据

13.枚举接口

前端TS项目中遇到过的难点(二)_第2张图片

可维护性: 使用枚举将所有接口地址集中在一个地方,使得代码更易维护。如果需要修改接口地址或者添加新的接口,只需在枚举中进行修改,而不必在整个代码中查找和修改多处引用。

避免硬编码: 枚举提供了有意义的命名,避免了在代码中直接硬编码接口地址。这样可以减少因拼写错误或复制粘贴错误而引起的 bug,并提高代码的可读性。

类型安全: TypeScript 的枚举可以提供类型安全。这意味着在使用这些接口地址时,编辑器会提供自动补全和类型检查,减少由于类型不匹配而引起的错误。

集中管理: 将相关的接口地址集中在一个枚举中,有助于组织和管理这些地址。这种集中管理使得团队协作更加容易,新成员可以更快地了解项目的接口结构。

14.pinia仓库

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;

15.scrollIntoView()方法

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"});

16.获取验证码以及倒计时

用户点击验证码获取验证码,并且设置倒计时

这里也是用到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数据变化

17.开发文档及生成二维码页面

//微信开放平台官网地址 微信开放平台 //查看微信扫码登录文档 微信公众平台

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 编码 如果不进行编码,打印出来的是本地的路径,并且后台需要的是编码后的数据,最后加上封装后的组件路径

18.清除页面样式

当我们扫码完成之后就可以吧微信二维码完成之后的样式清除

// 持久化存储用户信息
SET_TOKEN(JSON.stringify($route.query));
// 此路由组件代码执行:说明授权成功
let html: any = document.querySelector('html');
html.style.display = 'none';

19.进度条

安装命令 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();
});

20.页面标签文字

在路由配置中添加相关代码 meta,例如

  path: "register",
          component: () => import("@/pages/hospital/register/index.vue"),
          meta: {
            title: "预约挂号",
          },

在前置守卫中

// 前置守卫
router.beforeEach((to, from, next) => {
  //动态设置网页左上角的标题
  document.title = `家康-${to.meta.title}`;
});

21.路由鉴权

路由鉴权:就是路由能不能被访问权限设置->全局守卫完成

在使用路由鉴权前,首先,安装 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,
    });
  }
};

你可能感兴趣的:(前端)