???|??? V3-Admin 开源啦

? 简介

v3-admin 是一个中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element-Plus 和 Vue-Cli 4.5
1?? 功能
- 用户管理 - 登录 - 注销- 权限验证 - 页面权限 - 指令权限- 多环境 - development - test - production- 全局功能 - svg - 国际化 - 多主题切换(内置黑暗主题) - 动态侧边栏 - 动态面包屑 - 标签页快捷导航 - Screenfull 全屏 - 自适应收缩侧边栏- 错误页面 - 401 - 404- Dashboard - admin - editor- 自动部署

2?? 目录
# v3-admin ├─ .env.development# 开发环境 ├─ .env.production# 生产环境 ├─ .env.test# 测试环境 ├─ .eslintrc.js# eslint ├─ deploy# 自动部署 ├─ public │├─ favicon.ico │├─ index.html ├─ src │├─ @types# ts 声明 │├─ api# api 接口 │├─ assets# 静态资源 │├─ components# 全局组件 │├─ config# 全局配置 │├─ constant# 常量/枚举 │├─ directives# 全局指令 │├─ layout# 布局 │├─ locales# 国际化 │├─ model# 全局 model │├─ plugins# 插件 │├─ router# 路由 │├─ store# vuex store │├─ styles# 全局样式 │├─ utils# 全局公共方法 │└─ views# 所有页面 │├─ App.vue# 入口页面 │├─ main.ts# 入口文件 │├─ permission.ts# 权限管理 │└─ shims.d.ts# 模块注入 ├─ tsconfig.json# ts 编译配置 └─ vue.config.js# vue-cli 配置

3?? 安装
# 克隆项目 git clone https://github.com/v3-projects/v3-admin# 进入项目目录 cd v3-admin# 安装依赖 yarn# 启动项目 yarn dev

基础 1?? 路由 配置项
// 默认false,设置 true 的时候该路由不会在侧边栏出现 hidden: true// 设置 noRedirect 的时候该路由在面包屑导航中不可被点击 redirect: 'noRedirect'// 设定路由的名字,一定要填写不然重置路由可能会出问题 name: 'router-name'meta: { // 设置该路由进入的权限,支持多个权限叠加 roles: ['admin', 'editor'] // 设置该路由在侧边栏和面包屑中展示的名字 title: 'title' // 设置该路由的图标,记得将 svg 导入 @/assets/svg-icons/icons icon: 'svg-name' // 默认true,如果设置为false,则不会在面包屑中显示 breadcrumb: false // 默认false,如果设置为true,它则会固定在 tags-view 中 affix: true// 当一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式 // 只有一个时,会将那个子路由当做根路由显示在侧边栏 // 若想不管路由下面的 children 声明的个数都显示你的根路由 // 可以设置alwaysShow: true,这样就会忽略之前定义的规则,一直显示根路由 alwaysShow: true// 当设置了该属性,进入路由时,则会高亮 activeMenu 属性对应的侧边栏 activeMenu: '/dashboard' }

动态路由
constantRoutes 把不需要判断权限的路由放置在常驻路由里面,如 /login/dashboard
asyncRoutes 放置需求动态判断权限并通过 addRoute 动态添加的路由。
添加动态路由方法:
案例:在 @/router/asyncModules 下面创建例如 permission.ts 文件
import { RouteRecordRaw } from 'vue-router' import Layout from '@/layout/index.vue'const permissionRouter: Array = [ { path: '/permission', component: Layout, name: 'Permission', redirect: '/permission/directive', meta: { title: 'permission', icon: 'lock', roles: ['admin', 'editor'], // 可以在根路由中设置角色 alwaysShow: true // 将始终显示根菜单 }, children: [ { path: 'page', component: () => import(/* webpackChunkName: "permission-page" */ '@/views/permission/page.vue'), name: 'PagePermission', meta: { title: 'pagePermission', roles: ['admin'] // 或者在子导航中设置角色 } }, { path: 'directive', component: () => import(/* webpackChunkName: "permission-directive" */ '@/views/permission/directive.vue'), name: 'DirectivePermission', meta: { title: 'directivePermission' // 如果未设置角色,则表示:该页面不需要权限,但会继承根路由的角色 } } ] } ]export default permissionRouter

2?? 侧边栏和面包屑 侧边栏
???|??? V3-Admin 开源啦
文章图片

???|??? V3-Admin 开源啦
文章图片

侧边栏 @/layout/components/sidebar 是通过读取路由并结合权限判断而动态生成的(换句话说就是常驻路由 + 有权限的路由)
侧边栏外链
可以在侧边栏中配置一个外链,只要你在 path 中填写了合法的 url 路径,当你点击侧边栏的时候就会帮你新开这个页面
{ path: 'link', component: Layout, children: [ { path: 'https://github.com/v3-projects/v3-admin', meta: { title: 'link', icon: 'link' }, name: 'Link' } ] }

面包屑
???|??? V3-Admin 开源啦
文章图片

面包屑 @/components/bread-crumb 也是根据路由动态生成的,为路由设置 breadcrumb: false 时该路由将不会出现在面包屑中,设置 redirect: 'noRedirect' 时该路由在面包屑中不能被点击
3?? 权限 登录时通过获取当前用户的权限(角色)去比对路由表,生成当前用户具有的权限可访问的路由表,通过 addRoute 动态挂载到 router 上
角色控制权限
控制代码都在 @/permission.ts 中,这里可根据具体的业务做响应的修改:
import NProgress from 'nprogress' import 'nprogress/nprogress.css' import router from '@/router' import { RouteLocationNormalized } from 'vue-router' import { useStore } from './store' import { UserActionTypes } from './store/modules/user/action-types' import { PermissionActionType } from './store/modules/permission/action-types' import { UserMutationTypes } from './store/modules/user/mutation-types' import { ElMessage } from 'element-plus' import { whiteList } from './config/white-list' import rolesSettings from './config/roles'NProgress.configure({ showSpinner: false })router.beforeEach(async(to: RouteLocationNormalized, _: RouteLocationNormalized, next: any) => { NProgress.start() const store = useStore() // 判断该用户是否登录 if (store.state.user.token) { if (to.path === '/login') { // 如果登录,并准备进入 login 页面,则重定向到主页 next({ path: '/' }) NProgress.done() } else { // 检查用户是否已获得其权限角色 if (store.state.user.roles.length === 0) { try { // 注意:角色必须是一个对象数组! 例如: ['admin'] 或 ['developer', 'editor'] await store.dispatch(UserActionTypes.ACTION_GET_USER_INFO, undefined) if (rolesSettings.openRoles) { // 获取接口返回的 roles const roles = store.state.user.roles // 根据角色生成可访问的 routes store.dispatch(PermissionActionType.ACTION_SET_ROUTES, roles) } else { // 没有开启角色功能,则启用默认角色 store.commit(UserMutationTypes.SET_ROLES, rolesSettings.defaultRoles) store.dispatch(PermissionActionType.ACTION_SET_ROUTES, rolesSettings.defaultRoles) } // 动态地添加可访问的 routes store.state.permission.dynamicRoutes.forEach((route) => { router.addRoute(route) }) // 确保添加路由已完成 // 设置 replace: true, 因此导航将不会留下历史记录 next({ ...to, replace: true }) } catch (err) { // 删除 token,并重定向到登录页面 store.dispatch(UserActionTypes.ACTION_RESET_TOKEN, undefined) ElMessage.error(err || 'Has Error') next('/login') NProgress.done() } } else { next() } } } else { // 如果没有 token if (whiteList.indexOf(to.path) !== -1) { // 如果在免登录的白名单中,则直接进入 next() } else { // 其他没有访问权限的页面将被重定向到登录页面 next('/login') NProgress.done() } } })router.afterEach(() => { NProgress.done() })

取消角色
假如你的业务场景中没有 角色 的概念,那么在 @/config/roles 里可以关闭角色功能,关闭后系统将启用默认角色(一般为最高权限的 admin 角色),即每个登录的用户都可见所有路由
interface RolesSettings { // 是否开启角色功能(开启后需要后端配合,在查询用户详情接口返回当前用户的所属角色) openRoles: boolean // 当角色功能关闭时,当前登录用户的默认角色将生效(默认为admin,拥有所有权限) defaultRoles: Array }const rolesSettings: RolesSettings = { openRoles: true, defaultRoles: ['admin'] }export default rolesSettings

指令权限
简单快速的实现按钮级别的权限判断(已注册到全局,可直接使用):
admin可见 editor可见 admin和editor都可见

但在某些情况下,不适合使用 v-permission。例如:Element 的 el-tab 或 el-table-column 以及其它动态渲染 dom 的场景。你只能通过手动设置 v-if 来实现。
这时候可以使用权限判断函数
import { checkPermission } from '@/utils/permission'

admin可见 editor可见 admin和editor都可见

4?? 发送HTTP请求 大致的流程如下:
graph LR 页面/交互 --> 统一管理的API --> 封装的service.ts --> 服务器

统一管理的 API
import 形式 @/api/login.ts
import { request } from '@/utils/service'interface UserRequestData { username: string password: string }export function accountLogin(data: UserRequestData) { return request({ url: 'user/login', method: 'post', data }) }

封装的 service.ts
@/utils/service.ts 是基于 axios 的封装,封装了全局 request 拦截器、response 拦截器、统一的错误处理、统一做了超时处理、baseURL设置、CancelToken 等。
5?? 多环境 构建
项目开发完成,打包代码时,内置两种环境:
# 打包测试环境 yarn build:test# 打包正式环境 yarn build:prod

变量
.env.production.env.xxx 文件中,配置了该环境对应的变量:
# 当前运行的环境 NODE_ENV=production # 当前环境对应接口的 baseURL VUE_APP_BASE_API = 'https://www.xxx.com'

获取方式:
console.log(process.env.NODE_ENV) console.log(process.env.VUE_APP_BASE_API)

?? 进阶 1?? ESLint 规范代码很重要!
  • 配置项:在 .eslintrc.js 文件中
  • 取消自动校验:@/config/vue.custom.config.ts 中将 lintOnSave 设置为 false
  • 推荐 VSCode 的 ESlint 插件,它可在写代码时,将不符合规范的代码标红,并且在你保存代码是自动修复一些简单的标红的代码(VSCode 配置 ESlint 教程可自行 Google)
  • 手动校验:yarn lint(提交代码前执行该命令,特别是在你 lintOnSave 为 false 的情况下)
2?? Git Hooks package.json 中配置了 gitHooks,每次 commit 时都将检测代码
"gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,jsx,vue,ts,tsx}": [ "vue-cli-service lint", "git add" ] }

3?? 跨域 @/config 文件夹下存放了内置的一些配置项,比如 white-list 路由白名单,vue.custom.config vue.config 文件配置等。
其中的 vue.custom.config 里就有 proxy 进行反向代理。
与之对应的生产环境,则可以使用 nginx 来做反向代理。
反向代理
const vueDefaultConfig = { // ...其他配置 devServer: { // ...其他配置 proxy: { '/api/': { target: 'http://xxxxxx/api/', ws: true, pathRewrite: { '^/api/': '' }, changeOrigin: true, secure: false } } } }module.exports = vueDefaultConfig

CORS
这种方案对于前端来说没有什么工作量,和正常发送请求写法上没有任何区别,工作量基本都在后端这里。
实现 CORS 之后,不管是开发环境还是生产环境,都能方便的调用接口。
4?? SVG 全局 @/components/svg-icon 组件,图标存放在 @/assets/svg-icons/icons
使用方式
无需在页面中引入组件,可直接使用

下载 icon
推荐 iconfont
5?? 自动部署 在 deploy/index.js 文件里填写服务器的IP、端口、用户名、密码等信息,再执行 yarn deploy 命令即可自动发布打包好的 dist 文件到对应的服务器
注意:该文件中的用户名密码等信息为敏感信息,勿上传到远程仓库,这一点很重要!
6?? 新增主题(黑暗主题为例) 新增主题样式文件
  • src/style/theme/dark/index.scss
  • src/style/theme/dark/setting.scss
注册新的主题
  • src/style/theme/register.scss
  • src/config/theme.ts
? 常见问题 1?? 所有的报错 Google 一下可以解决 99% 的报错
剩下 1% 可以加群问我,然后我偷偷去 Google
2?? 依赖失败
  • 不要使用 cnpm
  • 推荐用 yarn
  • 尝试删除 node_modules 后再次依赖
  • Google 一下
3?? 路由模式切换为 browserHistory 后,刷新出现空白页面 将 @/config/vue.custom.config.ts 文件中 publicPath 的值从 ./ 修改为 /
? 其他 1?? 站在巨人的肩膀上
  • vue-element-admin
  • vue3-composition-admin
  • d2-admin
  • vue-vben-admin
2?? 仓库地址 【???|??? V3-Admin 开源啦】https://github.com/v3-project...

    推荐阅读