开发记录 从零开始开发后台管理系统,还是有很多值得记录的地方。构建工具Vite、使用Vue3。
1.批量导入指定目录的组件 /*
- @author 友人a丶
- @date 2022-07-11
- @app Vue应用对象
- /
export default function (app) {
/
- 指定要导入的文件目录
- 直接加载用{eager:true},懒加载用glob
- / const modules = import.meta.glob([‘@/layouts//index.js’, ‘@/components//index.js’]); for (let i in modules) { let name = /(.)?\/(.*)\/index.js/.exec(i); /直接引入组件/ app.component(name[2], modules[i].default); /异步组件/ app.component(name[2], defineAsyncComponent(modules[i])); } }
直接加载的时候modules就是元素为组件对象的数组,懒加载的时候是元素为import方法的函数数组 。
2.应用初始化 /*
- @author 友人a丶
- @date 2022-07-11 *
- 引导系统初始化
- 初始化全局响应拦截器
- 初始化路由守卫
- 初始化用户登录
- /
import loadGuard from “./loadGuard”
import loadInterceptor from “./loadInterceptor”
import watchRem from “./watchRem”;
export default async function (){
/
- 自动调整rem
- / watchRem(); /
- 加载拦截器
- / loadInterceptor(); /
- 加载路由守卫
- */ loadGuard(); }
3.自动调整rem /*
- @author 友人a丶
- @date 2022-07-11
- 自动调整rem的大小
- */ export default function () { let l = () => { let r = document.documentElement, o = r.offsetWidth / 100; o < 16 && (o = 16), r.style.fontSize = o + “px”, window.rem = o }; l(); window.addEventListener(“resize”,()=>l()); }
4.导航守卫 /*
- @author 友人a丶
- @date 2022-07-11 *
- 加载全局路由守卫
- */
import {router} from “@/router”;
import userStore from “@/stores/user”;
import load from “@/common/load”;
import systems from “@/stores/system”;
import NProgress from ‘nprogress’
import loadUser from “@/service/loadUser”;
/进度条/
NProgress.configure({showSpinner: false})
// 不需要拦截的路由配置
const ignoreRoute = {
names: [‘404’, ‘403’], //根据路由名称匹配
paths: [‘/login’], //根据路由fullPath匹配
/**
- 判断路由是否包含在该配置中
- @param route vue-router 的 route 对象
- @returns {boolean} / includes(route) { return ignoreRoute.names.includes(route.name) || ignoreRoute.paths.includes(route.path) } } /
- 加载路由守卫
- /
export default function () {
console.log(“加载路由守卫…”);
let user = userStore();//全局状态
/
- 加载进度条
- / router.beforeEach((to, from, next) => { // start progress bar if (!NProgress.isStarted()) { NProgress.start() } next() }); /
- 判断系统是否初始化
- / router.beforeEach(async (to, from, next) => { if(!systems().loaded){ await loadUser();//加载用户信息初始化系统 } next(); //下一个 }); /
- 已登录时,访问登录页面,让它走
- / router.beforeEach( (to, from, next) => { if (user.role != 0 && (to.path == “/login”)) { next(‘/’); }else{ next(); //下一个 } }); /
- 判断是否登录
- /
router.beforeEach((to, from, next) => {
/
- 判断是否需要拦截
- /
console.log(“登录判断守卫激活….”)
if (ignoreRoute.includes(to)) {
next();
} else {
/
- 角色为0,代表未登录
- / if (user.role == 0) { next({path: ‘/login’}); } else { next(); } } }) /
- 判断用户权限
- /
router.beforeEach((to, from, next) => {
console.log(“权限判断守卫激活….”)
/
- 判断是否需要拦截
- /
if (ignoreRoute.includes(to)) {
next();
} else {
/
- 判断用户权限
- / if (user.role < to.meta.role) { load.error(“您无权限访问该页面….”); next({path: ‘/403’}); } else { next(); } } }) /
- 切换页面标题
- / router.beforeEach((to, from, next) => { document.title = to.name next(); }) /
- 结束进度条
- */ router.afterEach(() => { // finish progress bar NProgress.done() }); }
5.axios拦截器 /*
- @author 友人a丶
- @date 2022-07-11 *
- 加载axio拦截器
- /
import axios from “axios”;
import load from “@/common/load”;
import {router} from “@/router”;
import apis from ‘@/service/api’;
// 不需要拦截的接口
const ignoreApi = {
api: [
apis.login
],
includes(api) {
/
* 判断当前请求的接口是否在忽略的列表
* /
for(let item of ignoreApi.api){
let reg=new RegExp(
.*${item}.*
); if(reg.test(api)){ return true; } } return false; } } / - 注册响应拦截器
- / export default function (){ console.log(“加载拦截器…”); axios.interceptors.response.use(function (res){ console.log(“请求接口:”+res.config.url) console.log(res); / * 判断是否需要拦截 * / if(ignoreApi.includes(res.config.url)){ return res; } / * 判断用户登录是否失效 * */ if(res.code == -1){ load.confirm(“当前登录状态已失效,请您重新登录!”,()=>{ router.replace(‘/login’) }) } return res; }); }
6.获取某个路由的子路由(用于生成菜单) /*
- 操作路由的相关方法
- / import {router} from “@/router/index”; /
- 获取某个路由项的子项
- */ export function getChildren(path) { let routes=router.getRoutes(); for (let i of routes) { if (i.path == path) { return i.children; } } }
7.简单的弹出封装(antd design vue) import {
message,
Modal
} from “ant-design-vue”; let hide = []; export default {
loading(text = '加载中...') {
hide.push(message.loading(text, 0))
},
loaded() {
if (hide.length > 0) {
let timer=setTimeout(()=>{
hide[hide.length - 1]()
hide.splice(hide.length - 1, 1)
},500);
}
},
error(text = '加载异常') {
message.error(text);
},
success(text = 'ok!') {
message.success(text);
},
confirm(text, callback = null) {
Modal.confirm({
title: '提示',
centered: true,
content: text,
maskClosable: false,
onOk: (close) => {
close(); //关闭
if (callback) {
callback()
}
}
})
}
}
8.退出登录 /*
- @author 友人a丶
- @date 2022-07-11
- 用户退出登录
- / import user from “@/stores/user”; import Cookies from “js-cookie”; import {router} from “@/router”; import load from “@/common/load”; export default function () { load.confirm(“确认退出登录吗?”,()=>{ user().$reset(); //重置用户数据的状态管理器 / * 清空cookie * / Object.keys(Cookies.get()).forEach((item)=>{ Cookies.remove(item); }) / * 跳转登录界面 * / router.replace(‘/login’); }) } 9.获取需要缓存的组件列表 /
- @author 友人a丶
- @date 2022-07-11
- name代表组件名
- 获取需要缓存的组件
- /
import routes from ‘@/router’
export function getChached(path=‘’) {
let routes=router.getRoutes();
let cahced=[]; //是否开启缓存
/
- 为空代表获取所有一级组件
- */ if(path == “”){ routes.forEach((item)=>{ if(item.meta.cache){ cahced.push(item.meta.cache); } }); }else{ /遍历目标子组件/ for (let i of routes) { if (i.path == path) { i.children.forEach((item)=>{ if(item.meta.cache){ cahced.push(item.meta.cache); } }); } } } return cahced; }
需要考虑 1.如何让显示的菜单响应路由的变化(跳转到某个页面,自动选中某个菜单)? 本身菜单被点击了,自己会变化被选中的状态,需要考虑的是从其他页面跳转过来的时候,如何正常匹配显示被选的菜单;
路由包括静态的路由和有变化的参数路由,某些情况下还会具有参数。
let selectedKeys = computed({
get() {
let current = route.fullPath;
for (let i = 0; i < items.length; i++) {
//断言右边是空或者?或者/
// 完整匹配或者带参数匹配
let regexp=new RegExp(`(?:.*${items[i].path}$)|(?:.*${items[i].path}[\?\/].*)`);
if(regexp.test(current)){
return [i];
}
}
},
set(value) {
return;
}
}
);
2.如何组织目录?
代表页面的组件一般以文件夹的形式通过index.js导出组件,方便观察层次结构,并且页面组件一般都会拆分JS模块,通过文件夹也更加方便文件的分类,保持目录的简洁。 其他的组件,如果设计到大量的逻辑,需要拆分JS模块,可以用文件夹,如何很简单的直接用.vue文件即可。 如何让父子组件的层级更加清晰?首先名字可以按层级写;parent-children.vue。 名字较长的组件用“-”分割,更加友好。
3.结构型的组件划分?
将布局看组架子(布局组件)、视图看做需要的内容(视图组件),布局承载内容; 通过全局状态的设置来动态调整布局组件的显示和隐藏。
4.如何组织无限层级的子路由作为菜单? 模板方式实现起来非常的麻烦,JSX的方式更加适合这种需求;
首先需要根据当前路由获取一个可以作为祖先的父级路由对象
5.运行中的router getRoutes(); 不管是push、redirect、route-link,都可以进行相对路径(dynamic)或者绝对路径(/dynamic)跳转;
[
{
"path": "/spread/tencent",
"name": "腾讯广告",
"meta": {
"role": 0,
"icon": "icon-guangdiantong"
},
"props": {
"default": false
},
"children": [],
"instances": {},
"leaveGuards": {},
"updateGuards": {},
"enterCallbacks": {},
"components": {}
}
]