# Ant-Design-Vue高颜值后台管理系统(权限版) **Repository Path**: ma-xiaofeng520/common-ant-design-front ## Basic Information - **Project Name**: Ant-Design-Vue高颜值后台管理系统(权限版) - **Description**: 基于Ant-design的通用后台权限管理系统--前端 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2025-03-05 - **Last Updated**: 2025-03-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 通用后台权限管理系统 ## 在线体验 ## 1、安装依赖 ```bash npm install ``` ## 2、启动项目 ```bash npm run dev ``` ## 3、打包项目 ```bash npm run build ``` >注意:打包时要设置生产环境的变量,否则数据请求不到(具体看vite.config.js文件) ## 一、vite配置文件 直接引入使用即可,大致也就这些 ```js import { defineConfig, loadEnv } from "vite"; import vue from "@vitejs/plugin-vue"; //引入path路径,用于路径别名 import path from "path"; // 导出 Vite 配置 export default defineConfig(({ command, mode }) => { //加载各环境下的配置 let env = loadEnv(mode, process.cwd()); return { base: "./", // 开发或生产环境服务的公共基础路径 resolve: { alias: { "@": path.resolve(__dirname, "./src"), // 路径别名 }, }, server: { host: true, // 监听所有地址 port: 5173, proxy: { //设置代理,必须填 [env.VITE_APP_BASE_API]: { //目标代理服务器地址:部署时将localhost改成自己服务器的地址即可 target: env.VITE_DEV_SERVE, changeOrigin: true, //是否设置同源,输入是的 //重写路径 rewrite: (path) => path.replace(/^\/api/, ""), }, }, }, plugins: [vue()], //scss样式配置:global.scss中的变量可以全局使用 css: { preprocessorOptions: { scss: { javascriptEnabled: true, additionalData: '@import "./src/style/global.scss";', }, }, }, }; }); ``` ## 二、路由配置 ### 1、安装 ```bash npm install vue-router@4 ``` ### 2、路由文件 这里将路由组件和路由配置文件分开 **路由组件** ```js //常量路由 export const constantRoutes = [ { path: "/", name: "layout", component: () => import("@/components/layout/Index.vue"), redirect: "/home", meta: { title: "", hidden: false, icon: "", }, children: [ { path: "/home", name: "Home", component: () => import("@/views/home/Index.vue"), meta: { title: "首页", hidden: false, icon: "Avatar", }, }, ], }, { path: "/login", name: "Login", component: () => import("@/views/login/Index.vue"), meta: { title: "登录", hidden: true, icon: "UploadFilled", }, }, { path: "/404", name: "404", component: () => import("@/views/404/Index.vue"), meta: { title: "404", hidden: true, icon: "UploadFilled", }, }, ]; ``` **配置文件** ```js // 从"vue-router"中导入createWebHistory和createRouter方法 import { createWebHistory, createRouter } from "vue-router"; import { constantRoutes } from '@/router/routes.js'; const router = createRouter({ // 创建基于HTML5 history模式的history实例 history: createWebHistory(), // 设置路由配置 routes: constantRoutes, }); // 导出路由 export default router; ``` ### 3、入口文件中引入 ```js //引入路由配置文件 import router from '@/router'; const app = createApp(App); app.use(router); ``` ## 三、sass样式配置 ### **1、安装** ```bash npm install -D sass ``` ### **2、全局样式index.scss** ```scss // 引入清除默认样式 @import "./reset.scss"; //滚动条样式设置 /* 隐藏滚动条 */ ::-webkit-scrollbar { display: none; } ``` ### **3、去除默认样式reset.scss** ```scss * { box-sizing: border-box; background-repeat: no-repeat; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { margin: 0; line-height: 1; } article, aside, footer, header, nav, section, main, figcaption, figure, menu, details { display: block; } audio, canvas, video { display: inline-block; } img { display: block; border: 0; } h1, h2, h3, h4, h5, h6 { margin: 0; padding: 0; font-weight: normal; } p { margin: 0; padding: 0; } address, cite, dfn, em, var { font-style: normal; } ul, ol { margin: 0; padding: 0; list-style-type: none; } a { background-color: transparent; font-size: inherit; color: inherit; text-decoration: none; &:active, &:hover { outline: 0; } } :focus { outline: 0; } button, input, select, textarea { margin: 0; font-size: inherit; } // button, // html [type="button"], // [type="reset"], // [type="submit"] { // padding: 0; // border: 0; // color: inherit; // background-color: transparent; // -webkit-appearance: button; // cursor: pointer; // } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input { padding: 0; line-height: normal; &::-webkit-input-placeholder { font-weight: 300; } &::-ms-input-placeholder { font-weight: 300; } &::-moz-placeholder { font-weight: 300; } } [type="number"] { -moz-appearance: textfield; &::-webkit-inner-spin-button, &::-webkit-outer-spin-button { margin: 0; height: auto; -webkit-appearance: none; } } [type="search"] { -webkit-appearance: textfield; &::-webkit-search-cancel-button, &::-webkit-search-decoration { -webkit-appearance: none; } } textarea { overflow: auto; resize: none; -webkit-appearance: none; } select { -webkit-appearance: none; background-color: #fff; } table { border-collapse: collapse; border-spacing: 0; } ``` ### **4、入口文件中引入** main.js文件引入样式文件 ```js //引入全局样式文件 import './style/index.scss'; ``` **想在全局使用scss变量,需要在vite.config.js中配置一个全局变量文件** 也就是在vite.config.js文件中配置的以下这段 ```js //scss样式配置:global.scss中的变量可以全局使用 css: { preprocessorOptions: { scss: { javascriptEnabled: true, additionalData: '@import "./src/style/global.scss";', }, }, }, ``` ## 四、环境变量配置 **开发环境** ```bash # 变量必须以 VITE_ 为前缀才能暴露给外部读取:import.meta.env NODE_ENV = 'development' VITE_APP_TITLE = '通用后台管理系统模板' VITE_APP_BASE_API = '/api' VITE_SERVE = 'http://localhost:8081' ``` **生产环境** ```bash # 变量必须以 VITE_ 为前缀才能暴露给外部读取:import.meta.env VITE_APP_TITLE = '通用后台管理系统模板' VITE_APP_BASE_API = '/api' VITE_SERVE = 'http://localhost:8082' ``` ## 五、布局 ### 1、安装 **ant-design组件库** ```bash npm install ant-design-vue@4.x --save ``` **自动按需引入组件** 安装插件 >**npm install unplugin-vue-components -D** 配置插件:在**vite.config.js**中引入 ```js import Components from 'unplugin-vue-components/vite'; import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; export default defineConfig({ plugins: [ // ... Components({ resolvers: [ AntDesignVueResolver({ importStyle: false, // css in js }), ], }), ], }); ``` **入口文件引入(全局注册)** ```js //引入antdesign Ui组件库 import Antd from "ant-design-vue"; //引入 ui样式 import "ant-design-vue/dist/reset.css"; ``` ### **2、响应式布局** **基本结构** ![screenshot-1716442668711](https://s2.loli.net/2024/05/23/KjuU4Tsx1CzWwae.png) **响应式布局** 侧边栏响应式布局,只需要设置配置 `breakpoint` 属性即生效,视窗宽度小于 `breakpoint` 时 Sider 缩小为 `collapsedWidth` 宽度,若将 `collapsedWidth` 设置为零, ### 3、顶部布局 - **左侧logo** - **右侧用户头像展示以及其他** #### **封装logo组件** #### 封装右侧部分 ## 六、主题设置 ### 1、安装Pinia 这里需要用到Pinia来存储主题状态 ```bash npm install pinia ``` 持久化插件 ```bash npm install pinia-persistedstate-plugin ``` ### 2、暗黑模式切换 **暗黑模式已去除,不好看** ant design 提供了方便切换的组件,直接在App.vue中引入使用即可 ```vue ``` **token**就是下面的主题皮肤设置时用到的参数 ### 3、主题皮肤设置 定制主题 ```js //皮肤配置 export const theme = [ { themeOne: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#2f54eb", colorWarning: "#FAAD14", colorError: "#F5222D", colorInfo: "#2f54eb", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeTwo: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#722ed1", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#722ed1", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeThree: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#eb2f96", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#eb2f96", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeFour: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#52c41a", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#52c41a", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeFive: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#13c2c2", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#13c2c2", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeSix: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#1677ff", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#1677ff", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeSeven: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#faad14", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#faad14", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeEight: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#fadb14", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#fadb14", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeNine: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#873800", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#873800", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeTen: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#f5222d", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#f5222d", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeEleven: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#fa541c", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#fa541c", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeTwelve: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#fa8c16", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#fa8c16", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeThirteen: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#5b8c00", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#5b8c00", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, { themeFourteen: { colorSuccess: "#52c41a", wireframe: true, colorPrimary: "#9e1068", colorWarning: "#faad14", colorError: "#ff4d4f", colorInfo: "#9e1068", boxShadow: "0 2px 4px 0 rgba(0, 0, 0, .1),0 1px 6px -1px rgba(0, 0, 0, 0.02),0 2px 4px 0 rgba(0, 0, 0, 0.02)", }, }, ]; export const themeContent = [ { theme: "themeOne", color: "#2f54eb", }, { theme: "themeTwo", color: "#722ed1", }, { theme: "themeThree", color: "#eb2f96", }, { theme: "themeFour", color: "#52c41a", }, { theme: "themeFive", color: "#13c2c2", }, { theme: "themeSix", color: "#1677ff", }, { theme: "themeSeven", color: "#faad14", }, { theme: "themeEight", color: "#fadb14", }, { theme: "themeNine", color: "#873800", }, { theme: "themeTen", color: "#f5222d", }, { theme: "themeEleven", color: "#fa541c", }, { theme: "themeTwelve", color: "#fa8c16", }, { theme: "themeThirteen", color: "#5b8c00", }, { theme: "themeFourteen", color: "#9e1068", }, ]; ``` **持久化主题数据** ```js import { defineStore } from "pinia"; import { ref } from "vue"; import { theme } from "@/theme.js"; export const useThemeStore = defineStore("theme", () => { //判断是否是暗黑模式,默认不是暗黑模式 const isDark = ref(false); //默认皮肤设置 const currentSkin = ref({ colorSuccess: "#52C41A", wireframe: true, colorPrimary: "#722ED1", colorWarning: "#FAAD14", colorError: "#F5222D", colorInfo: "#722ED1", }); //遍历主题皮肤,当切换时过滤当前皮肤 const filterTheme = (item) => { for (let i = 0; i < theme.length; i++) { // 检查当前对象是否包含item键 if (item in theme[i]) { // 如果找到,返回item对象 currentSkin.value = theme[i][item]; } } } return { isDark, currentSkin, filterTheme, }; }); ``` ### 4、主题风格 **Pinia中存储一个控制变量isBusiness** #### 简约风 ![screenshot-1716485784848](https://s2.loli.net/2024/05/24/GICglDQWPVM4Om9.png) #### 商务风 ![screenshot-1716485831690](https://s2.loli.net/2024/05/24/anfdrItxU3M2gEe.png) ## 七、侧边栏收缩 存储是否收缩状态 ```js import { defineStore } from "pinia"; import { ref } from "vue"; export const useCollapsedStore = defineStore("collapsed", () => { //侧边栏收缩状态控制,默认不收缩 const collapsed = ref(false); return { collapsed, }; }); ``` ## 八、封装Svg组件 ### 1、安装 ```bash npm i vite-plugin-svg-icons -D ``` ### 2、vite.config.js配置 ```js import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' plugins: [ vue(), createSvgIconsPlugin({ // 配置svg图标所在位置 iconDirs: [path.resolve(process.cwd(), "src/assets/icons")], symbolId: "icon-[dir]-[name]", }), ], ``` ### 3、main.js入口文件中引入 ```js import 'virtual:svg-icons-register' ``` ### 4、简单使用 **去阿里图标库下载图标的svg代码,然后在项目的src/assets/icons/目录下新建.svg文件即可** ```html
``` 通过以上就可以在项目中使用svg了,问题是不可能每用一次就写这么些代码,接下来就封装为一个**svg组件**吧! ### 5、封装Svg组件 SvgIcon.vue ```vue ``` **组件使用** ```vue ``` 还有一个问题就是项目中每用到一处svg就要引入很麻烦,接下来把它注册为一个**全局组件~** ### 6、注册全局组件 > **自定义插件的方式来注册全局组件** #### ①、自定义插件对象并暴露 在项目中新建plugins文件夹,其下新建index.js文件 ```js //引入所有需要注册为全局组件的组件 import SvgIcon from "@/components/common/svg/Index.vue"; //全局对象 const allGlobalComponent = { SvgIcon }; //对外暴露插件对象 export default { //务必使用install方法 install(app) { //注册项目中的所有全局组件 Object.keys(allGlobalComponent).forEach(key => { //注册为全局组件 app.component(key, allGlobalComponent[key]); }); } } ``` #### ②、在main.js入口文件中引入 ```js //引入自定义插件用来注册全局组件 import globalComponent from "@/plugins"; //安装自定义插件 app.use(globalComponent); ``` #### ③、测试使用全局Svg组件 ```vue ``` ## 九、引入Ant design图标库 ### 1、安装 ```bash npm install --save @ant-design/icons-vue ``` ### 2、main.js入口文件中引入 ```js //引入图标库 import * as antIcons from "@ant-design/icons-vue"; //注册图标组件 Object.keys(antIcons).forEach((key) => { app.component(key, antIcons[key]); }); ``` ### 3、具体使用 **用于菜单遍历时动态设置菜单图标** ```bash ``` **解释** > 是什么意思? > >`` 是 Vue 中的动态组件语法。 > >通过 `:is` 属性将动态组件的名称设置为 `item.meta.icon`, >这样 Vue 将会根据 `item.meta.icon` 的值来动态地渲染对应的图标组件。 > >假设 `item.meta.icon` 的值为 `SettingOutlined`, >那么 `` 将会渲染 `` 组件。 > >这种方式可以让你在运行时动态地选择要渲染的组件,并且非常适合在循环中使用。 ## 十、页面过渡效果 **content组件过渡** ```vue ``` **App.vue跟组件过渡** ```vue ``` ## 十一、页面刷新与全屏功能 ### **页面刷新** ```vue ``` ```js import { useRefreshStore } from "@/stores/models/refresh"; const useRefresh = useRefreshStore(); //刷新标志进行本地存储 const doRefresh = () => { useRefresh.refresh = !useRefresh.refresh; }; ``` 在content视图展示时监听refresh是否变化 ```vue ``` ### 全屏 **使用到了插件screenfull** ```bash npm install screenfull ``` **点击全屏按钮触发事件:核心代码** ```js import screenfull from "screenfull"; //全屏 const changeScreen = () => { if (screenfull.isEnabled) { //可以来回切换全屏/非全屏 screenfull.toggle(); } else { ElMessage({ message: "您的浏览器不支持全屏!", type: "warning", }); } }; ``` ## 十二、Axios封装 ### 1、安装 ```bash npm install axios ``` ### 2、请求过滤 ```js //对于axios进行二次封装 import axios from "axios"; import { message } from "ant-design-vue"; import router from "@/router"; // import useUserStore from "@/stores/models/user"; // import { useBreadcrumbStore } from "@/stores/models/breadcrumb/breadcrumb.js"; //配置通用的基础路径和超时时间 const requests = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, timeout: 5000, }); //请求拦截器 requests.interceptors.request.use((config) => { //config:配置对象,对象里面有一个属性很重要,headers请求头 // let userStore = useUserStore(); // if (userStore.token) { // config.headers["authorization"] = userStore.token; // 设置请求头 // } return config; }); //响应拦截器 requests.interceptors.response.use( (response) => { //响应成功的回调 // let breadcrumbStore = useBreadcrumbStore(); // let userStore = useUserStore(); const res = response.data; const code = res.code; if (code !== 200) { message.error(`res.message`); } if (code === 403) { // userStore.logout(); // breadcrumbStore.removeBreadcrumb(); router.push("/login"); } if (code === 1002) { // userStore.logout(); // breadcrumbStore.removeBreadcrumb(); router.push("/login"); } return res; }, (error) => { //响应失败的回调 return Promise.reject(new Error("faile")); } ); //对外暴露 export default requests; ``` ### 3、简单请求 ```js import request from "@/api/request.js"; //请求前缀 const PREFIX = "/user"; export function doLogin(data) { return request({ //模板字符串拼接参数 url: `${PREFIX}/login`, method: "POST", data: data, }); } ``` ## 十三、进度条配置 ### 1、安装 ```bash npm install --save nprogress ``` ### 2、修改默认样式 **找到源码nprogress.css** **修改默认颜色** b18540b8dab745cfe39eeffd9616bab5.png ## 十四、自定义指令 ### 1、新建权限文件 **在跟目录下新建permission文件夹再创建index.js文件** ```javascript import pinia from "@/stores"; import useUserStore from "../stores/models/user/user"; const uesrStore = useUserStore(pinia); export const hasButton = (app) => { //全局自定义指令v-has,实现按钮权限判断 app.directive('permission', { //代办使用这个全局自定义指令的Dom/组件挂载完毕时会执行一次 mounted(el, options) { //el为元素,options是指令的选项对象 if (!uesrStore.buttons.includes(options.value)) { //说明没有权限,直接删除元素即可 el.remove(); //以下是禁用 //el.disabled = true; } }, }) }; ``` ### **2、在main.js入口文件中引入** ```javascript //引入自定义指令文件 import { hasPermission } from "@/permission/index"; const app = createApp(App); hasPermission(app); ``` ### **3、使用** ```vue ``` **通过自定义的指令在需要进行权限设定的按钮或者其他组件上绑定v-permission即可判断**