v3-admin 是一箇中後臺管理系統基礎解決方案,基於 Vue三、TypeScript、Element-Plus 和 Vue-Cli 4.5css
GitHub地址:github.com/v3-projects… ,已上生產環境。歡迎各位提 Issues、PR,以爲能夠的點個 Star 支持一下!html
- 用戶管理
- 登陸
- 註銷
- 權限驗證
- 頁面權限
- 指令權限
- 多環境
- development
- test
- production
- 全局功能
- svg
- 國際化
- 多主題切換(內置黑暗主題)
- 動態側邊欄
- 動態麪包屑
- 標籤頁快捷導航
- Screenfull 全屏
- 自適應收縮側邊欄
- 錯誤頁面
- 401
- 404
- Dashboard
- admin
- editor
- 自動部署
複製代碼
# 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 配置
複製代碼
# 克隆項目
git clone https://github.com/v3-projects/v3-admin
# 進入項目目錄
cd v3-admin
# 安裝依賴
yarn
# 啓動項目
yarn dev
複製代碼
// 默認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
動態添加的路由。vue
添加動態路由方法:node
案例:在 @/router/asyncModules
下面建立例如 permission.ts
文件webpack
import { RouteRecordRaw } from 'vue-router'
import Layout from '@/layout/index.vue'
const permissionRouter: Array<RouteRecordRaw> = [
{
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
複製代碼
側邊欄 @/layout/components/sidebar
是經過讀取路由並結合權限判斷而動態生成的(換句話說就是常駐路由 + 有權限的路由)ios
能夠在側邊欄中配置一個外鏈,只要你在 path 中填寫了合法的 url 路徑,當你點擊側邊欄的時候就會幫你新開這個頁面nginx
{
path: 'link',
component: Layout,
children: [
{
path: 'https://github.com/v3-projects/v3-admin',
meta: { title: 'link', icon: 'link' },
name: 'Link'
}
]
}
複製代碼
麪包屑 @/components/bread-crumb
也是根據路由動態生成的,爲路由設置 breadcrumb: false
時該路由將不會出如今麪包屑中,設置 redirect: 'noRedirect'
時該路由在麪包屑中不能被點擊git
登陸時經過獲取當前用戶的權限(角色)去比對路由表,生成當前用戶具備的權限可訪問的路由表,經過 addRoute
動態掛載到 router 上github
控制代碼都在 @/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<string>
}
const rolesSettings: RolesSettings = {
openRoles: true,
defaultRoles: ['admin']
}
export default rolesSettings
複製代碼
簡單快速的實現按鈕級別的權限判斷(已註冊到全局,可直接使用):
<el-tag v-permission="['admin']">admin可見</el-tag>
<el-tag v-permission="['editor']">editor可見</el-tag>
<el-tag v-permission="['admin','editor']">admin和editor均可見</el-tag>
複製代碼
但在某些狀況下,不適合使用 v-permission
。例如:Element 的 el-tab 或 el-table-column 以及其它動態渲染 dom 的場景。你只能經過手動設置 v-if 來實現。
這時候可使用權限判斷函數
import { checkPermission } from '@/utils/permission'
複製代碼
<el-tab-pane v-if="checkPermission(['admin'])" label="Admin">admin可見</el-tab-pane>
<el-tab-pane v-if="checkPermission(['editor'])" label="Editor">editor可見</el-tab-pane>
<el-tab-pane v-if="checkPermission(['admin','editor'])" label="AdminEditor">admin和editor均可見</el-tab-pane>
複製代碼
大體的流程以下:
graph LR 頁面/交互 --> 統一管理的API --> 封裝的service.ts --> 服務器
@/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
})
}
複製代碼
@/utils/service.ts
是基於 axios 的封裝,封裝了全局 request 攔截器、response 攔截器、統一的錯誤處理、統一作了超時處理、baseURL設置、CancelToken 等。
項目開發完成,打包代碼時,內置兩種環境:
# 打包測試環境
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)
複製代碼
規範代碼很重要!
.eslintrc.js
文件中@/config/vue.custom.config.ts
中將 lintOnSave
設置爲 false
yarn lint
(提交代碼前執行該命令,特別是在你 lintOnSave 爲 false 的狀況下)package.json
中配置了 gitHooks,每次 commit 時都將檢測代碼
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,jsx,vue,ts,tsx}": [
"vue-cli-service lint",
"git add"
]
}
複製代碼
@/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 以後,不論是開發環境仍是生產環境,都能方便的調用接口。
全局 @/components/svg-icon
組件,圖標存放在 @/assets/svg-icons/icons
無需在頁面中引入組件,可直接使用
<!-- name 爲 svg 文件名 -->
<!-- class 可修改默認樣式 -->
<svg-icon name="user" font-size="20px" class="icon" />
複製代碼
推薦 iconfont
在 deploy/index.js
文件裏填寫服務器的IP、端口、用戶名、密碼等信息,再執行 yarn deploy
命令便可自動發佈打包好的 dist 文件到對應的服務器
注意:該文件中的用戶名密碼等信息爲敏感信息,勿上傳到遠程倉庫,這一點很重要!
新增主題樣式文件
src/style/theme/dark/index.scss
src/style/theme/dark/setting.scss
註冊新的主題
src/style/theme/register.scss
src/config/theme.ts
Google 一下能夠解決 99% 的報錯
剩下 1% 能夠加羣問我,而後我偷偷去 Google
將 @/config/vue.custom.config.ts
文件中 publicPath
的值從 ./
修改成 /
無人問津的交流(吹水)羣:1014374415