├── src ├── styles │ ├── var.scss │ ├── defalut.scss │ ├── dark.scss │ └── common.scss ├── assets │ ├── bg.png │ ├── card.gif │ ├── title.gif │ ├── uadmin.png │ ├── wellcome.png │ ├── card-title.png │ └── login-banner.png ├── views │ ├── toolkit │ │ ├── bigFileUpload.vue │ │ ├── downloadUpload.vue │ │ └── virtualList.vue │ ├── home │ │ └── index.vue │ ├── data-screen │ │ ├── components │ │ │ ├── DashboardChart.vue │ │ │ ├── PieChart.vue │ │ │ ├── ScaleBox.vue │ │ │ ├── ListChart.vue │ │ │ ├── LineChart.vue │ │ │ └── MapChart.vue │ │ └── index.vue │ ├── form │ │ ├── form-base │ │ │ └── index.vue │ │ └── form-advanced │ │ │ └── index.vue │ ├── login │ │ ├── index.scss │ │ └── index.vue │ ├── table │ │ ├── table-base │ │ │ └── index.vue │ │ └── table-advanced │ │ │ └── index.vue │ └── notfound.vue ├── vite-env.d.ts ├── style.css ├── api │ ├── loginApi │ │ └── loginFormApi.ts │ ├── interface │ │ └── index.ts │ └── index.ts ├── main.ts ├── typings │ └── plugins.ts ├── App.vue ├── store │ ├── modules │ │ ├── user.ts │ │ ├── tabs.ts │ │ └── menu.ts │ ├── interface │ │ └── index.ts │ └── index.ts ├── routers │ ├── permissionList.ts │ ├── index.ts │ ├── router.ts │ └── allRouter.ts ├── components │ └── dataScreen │ │ └── DataScreenCard.vue ├── layout │ ├── navBar │ │ ├── components │ │ │ ├── CollapsedMenu.vue │ │ │ ├── BreadCrumb.vue │ │ │ └── NavbarMenu.vue │ │ └── NavBar.vue │ ├── siderBar │ │ ├── components │ │ │ └── SubMenu.vue │ │ └── SiderBar.vue │ ├── index.vue │ └── tabsBar │ │ └── TabsBar.vue └── utils │ ├── theme.ts │ └── storage.ts ├── .vscode └── extensions.json ├── public └── uuadmin.ico ├── tsconfig.node.json ├── .gitignore ├── index.html ├── tsconfig.json ├── vite.config.ts ├── package.json ├── LICENSE ├── README.md ├── .stylelintrc.js └── components.d.ts /src/styles/var.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/bg.png -------------------------------------------------------------------------------- /src/styles/defalut.scss: -------------------------------------------------------------------------------- 1 | // 默认主题 2 | @import 'ant-design-vue/dist/antd.css'; 3 | @import '../style.css'; -------------------------------------------------------------------------------- /public/uuadmin.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/public/uuadmin.ico -------------------------------------------------------------------------------- /src/assets/card.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/card.gif -------------------------------------------------------------------------------- /src/styles/dark.scss: -------------------------------------------------------------------------------- 1 | // 暗黑主题 2 | @import 'ant-design-vue/dist/antd.dark.css'; 3 | @import '../style.css'; -------------------------------------------------------------------------------- /src/assets/title.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/title.gif -------------------------------------------------------------------------------- /src/assets/uadmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/uadmin.png -------------------------------------------------------------------------------- /src/assets/wellcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/wellcome.png -------------------------------------------------------------------------------- /src/assets/card-title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/card-title.png -------------------------------------------------------------------------------- /src/assets/login-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SHIHAOZOU/vue-antd-admin-template/HEAD/src/assets/login-banner.png -------------------------------------------------------------------------------- /src/views/toolkit/bigFileUpload.vue: -------------------------------------------------------------------------------- 1 | 4 | 6 | -------------------------------------------------------------------------------- /src/styles/common.scss: -------------------------------------------------------------------------------- 1 | // scroll bar 2 | 3 | ::-webkit-scrollbar { 4 | width: 6px; 5 | } 6 | ::-webkit-scrollbar-thumb { 7 | background-color: #ccc; 8 | } -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue' 5 | const component: DefineComponent<{}, {}, any> 6 | export default component 7 | } 8 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --success-color:red; 3 | --page-bg-color: #fff; 4 | --head-bg-color: rgba(255, 255, 255, 0.7); 5 | --text-color: rgba(0, 0, 0, 0.85); 6 | --line-color: #e8e8e8; 7 | --content-bg-color: #f0f2f5; 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /src/api/loginApi/loginFormApi.ts: -------------------------------------------------------------------------------- 1 | import request from '@/api/index' 2 | import {LoginForm} from '@/api/interface/index' 3 | 4 | // 登录 5 | export const loginApi = (params:LoginForm) => { 6 | return request.POST('/login',params) 7 | } 8 | 9 | // 获取权限菜单 10 | export const getMenuList = () => { 11 | return request.GET('/list') 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import Antd from "ant-design-vue"; 3 | import App from "./App.vue"; 4 | import 'ant-design-vue/dist/antd.css'; 5 | import "./style.css"; 6 | // pinia store 7 | import pinia from "@/store/index"; 8 | // vue Router 9 | import router from "@/routers/index"; 10 | const app = createApp(App); 11 | 12 | app.use(pinia).use(router).use(Antd).mount("#app"); 13 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | uu Admin 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/typings/plugins.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shzou shihaozou@outlook.com 3 | * @Date: 2022-08-20 15:22:33 4 | * @LastEditors: shzou shihaozou@outlook.com 5 | * @LastEditTime: 2022-08-20 15:22:39 6 | * @FilePath: \uu-admin\src\typings\plugins.ts 7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE 8 | */ 9 | 10 | declare module 'js-md5' -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 18 | -------------------------------------------------------------------------------- /src/store/modules/user.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { getLocalStorageParse } from "@/utils/storage"; 3 | 4 | export const UserStore = defineStore({ 5 | id: "UserStore", 6 | state: () => ({ 7 | userInfo: getLocalStorageParse("UserStore")?.userInfo || {}, 8 | }), 9 | getters: {}, 10 | actions: { 11 | setUserInfo(userInfo: object) { 12 | this.userInfo = userInfo; 13 | }, 14 | }, 15 | persist: true, 16 | }); 17 | -------------------------------------------------------------------------------- /src/api/interface/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shzou shihaozou@outlook.com 3 | * @Date: 2022-08-20 15:24:54 4 | * @LastEditors: shzou shihaozou@outlook.com 5 | * @LastEditTime: 2022-08-20 15:28:32 6 | * @FilePath: \uu-admin\src\api\interface\index.ts 7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE 8 | */ 9 | // 登录模块 10 | export interface LoginForm { 11 | username: string; 12 | password: string; 13 | } 14 | export interface FormState extends LoginForm { 15 | remember: boolean; 16 | } -------------------------------------------------------------------------------- /src/routers/permissionList.ts: -------------------------------------------------------------------------------- 1 | import { RouteRecordRaw } from "vue-router" 2 | // 后端获取菜单路由与前端路由匹配 3 | const matchRouter = function (routerList:RouteRecordRaw[],getRouter:RouteRecordRaw[]=[]) { 4 | const macthArray:RouteRecordRaw[] = []; 5 | routerList.forEach(item => { 6 | getRouter.forEach(val => { 7 | if( val.path == item.path ) { 8 | if ( val["children"] && val.children.length>0 ) { 9 | matchRouter(val.children,item.children) 10 | } 11 | macthArray.push(item) 12 | } 13 | }) 14 | }); 15 | return macthArray; 16 | } 17 | 18 | export default matchRouter; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "isolatedModules": true, 12 | "esModuleInterop": true, 13 | "lib": ["ESNext", "DOM"], 14 | "skipLibCheck": true, 15 | "baseUrl": "./", 16 | "paths": { 17 | "@": ["src"], 18 | "@/*":["src/*"] 19 | } 20 | }, 21 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /src/components/dataScreen/DataScreenCard.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 10 | 33 | -------------------------------------------------------------------------------- /src/layout/navBar/components/CollapsedMenu.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | -------------------------------------------------------------------------------- /src/views/toolkit/downloadUpload.vue: -------------------------------------------------------------------------------- 1 | 4 | 23 | -------------------------------------------------------------------------------- /src/layout/navBar/NavBar.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /src/layout/navBar/components/BreadCrumb.vue: -------------------------------------------------------------------------------- 1 | 10 | 29 | -------------------------------------------------------------------------------- /src/store/modules/tabs.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { tabsOption } from "@/store/interface/index"; 3 | import { string } from "vue-types"; 4 | 5 | // interface tabsOptionInterface { 6 | // title:string, 7 | // key:string, 8 | // closable:boolean 9 | // } 10 | export const TabsStore = defineStore({ 11 | id: "TabsStore", 12 | state: () => ({ 13 | tabsOption: [ 14 | { 15 | key: "/home", 16 | title: "首页", 17 | closable: false, 18 | }, 19 | ], 20 | }), 21 | getters: {}, 22 | actions: { 23 | addTab(title: string, path: string, closable: boolean) { 24 | this.tabsOption.push({ 25 | title: title, 26 | key: path, 27 | closable: closable, 28 | }); 29 | }, 30 | setTab(newTabsOption) { 31 | this.tabsOption = newTabsOption; 32 | }, 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 29 | -------------------------------------------------------------------------------- /src/store/interface/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: shzou shihaozou@outlook.com 3 | * @Date: 2022-08-22 10:45:46 4 | * @LastEditors: shzou shihaozou@outlook.com 5 | * @LastEditTime: 2022-08-25 14:41:07 6 | * @FilePath: \uu-admin\src\store\interface\index.ts 7 | * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE 8 | */ 9 | import { RouteRecordRaw } from "vue-router" 10 | export interface MenuOptions { 11 | path: string; 12 | title: string; 13 | meta:{ 14 | title:string; 15 | }, 16 | icon?: string; 17 | children?: MenuOptions[]; 18 | } 19 | 20 | export interface MenuState { 21 | menuList:MenuOptions[]; 22 | matchList:RouteRecordRaw[]; 23 | } 24 | 25 | /** 26 | * tabs 27 | */ 28 | export interface tabsOption { 29 | key: string, 30 | title: string 31 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import vue from "@vitejs/plugin-vue"; 3 | import { resolve } from "path"; 4 | import Components from "unplugin-vue-components/vite"; 5 | import { AntDesignVueResolver } from "unplugin-vue-components/resolvers"; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | resolve: { 10 | alias: { 11 | "@": resolve(__dirname, "./src"), 12 | }, 13 | }, 14 | css: { 15 | preprocessorOptions: { 16 | // less: { 17 | // // modifyVars: getThemeVariables({ 18 | // // dark: true, // 开启暗黑模式 19 | // // }), 20 | // javascriptEnabled: true, 21 | // }, 22 | }, 23 | }, 24 | plugins: [ 25 | vue(), 26 | // 自动引入组件 27 | Components({ 28 | resolvers: [ 29 | AntDesignVueResolver({ 30 | // importStyle: "less", //动态导入 31 | }), 32 | ], 33 | }), 34 | ], 35 | base:'./' 36 | }); 37 | -------------------------------------------------------------------------------- /src/layout/siderBar/components/SubMenu.vue: -------------------------------------------------------------------------------- 1 | 22 | 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uu-admin", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc --noEmit && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@vueuse/core": "^9.1.0", 13 | "ant-design-vue": "^3.2.11", 14 | "axios": "^0.27.2", 15 | "echarts": "^5.4.1", 16 | "js-md5": "^0.7.3", 17 | "nprogress": "^0.2.0", 18 | "pinia": "^2.0.18", 19 | "pinia-plugin-persistedstate": "^2.1.1", 20 | "vue": "^3.2.37", 21 | "vue-router": "^4.1.3" 22 | }, 23 | "devDependencies": { 24 | "@types/node": "^18.7.6", 25 | "@types/nprogress": "^0.2.0", 26 | "@vitejs/plugin-vue": "^3.0.3", 27 | "sass": "^1.54.4", 28 | "stylelint-config-standard": "^27.0.0", 29 | "stylelint-order": "^5.0.0", 30 | "typescript": "^4.6.4", 31 | "unplugin-vue-components": "^0.22.4", 32 | "vite": "^3.0.7", 33 | "vue-tsc": "^0.39.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/views/data-screen/components/DashboardChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { defineStore,createPinia } from "pinia"; 2 | // pinia 持久化 3 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 4 | import { getLocalStorageParse } from "@/utils/storage"; 5 | 6 | export const GlobalStore = defineStore({ 7 | id: "GlobalStore", 8 | state: () => { 9 | return { 10 | // token 11 | token: "", 12 | // collapsed 13 | isCollapsed: false, 14 | // dark theme 15 | isDark: getLocalStorageParse("GlobalStore")?.isDark || false 16 | } 17 | }, 18 | getters: {}, 19 | actions: { 20 | setToken(token: string){ 21 | this.token = token; 22 | }, 23 | setCollapsed(){ 24 | this.isCollapsed = !this.isCollapsed; 25 | }, 26 | setTheme(){ 27 | this.isDark = !this.isDark 28 | } 29 | }, 30 | persist: true 31 | }) 32 | 33 | 34 | const pinia = createPinia(); 35 | pinia.use(piniaPluginPersistedstate); 36 | 37 | export default pinia; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 喝水高手 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 36 | 37 | 53 | -------------------------------------------------------------------------------- /src/utils/theme.ts: -------------------------------------------------------------------------------- 1 | //默认主题和暗黑主题 2 | import dark from "@/styles/dark.scss"; 3 | import defalut from "@/styles/defalut.scss"; 4 | 5 | // 切换css 6 | const changeTheme = (theme: string) => { 7 | const head = document.head; 8 | document.getElementById("theme")?.remove(); 9 | const styleDom = document.createElement("style"); 10 | styleDom.id = "theme"; 11 | styleDom.innerHTML = theme; 12 | head.appendChild(styleDom); 13 | }; 14 | 15 | // 改变css变量 16 | const changeCss = (css: string, value: string) => { 17 | const body = document.body.style; 18 | body.setProperty(css, value); 19 | }; 20 | 21 | // 切换暗黑主题或者默认主题 22 | const DarkMode = (isDark: boolean) => { 23 | if (isDark) { 24 | changeTheme(dark); 25 | changeCss("--page-bg-color", "#1f1f1f"); 26 | changeCss("--head-bg-color", "rgba(0, 0, 0, 0.5)"); 27 | changeCss("--line-color", "#2e2e2e"); 28 | changeCss("--content-bg-color", "rgb(255 255 255 / 4%)"); 29 | changeCss("--text-color", "rgba(255, 255, 255, 0.85)"); 30 | } else { 31 | changeTheme(defalut); 32 | changeCss("--page-bg-color", "white"); 33 | changeCss("--head-bg-color", "rgba(0, 0, 0, 0.7)"); 34 | changeCss("--line-color", "#e8e8e8"); 35 | changeCss("--content-bg-color", "#f0f2f5"); 36 | changeCss("--text-color", "rgba(0, 0, 0, 0.85)"); 37 | changeCss("--success-color", "yellow"); 38 | } 39 | }; 40 | 41 | export { DarkMode, changeCss }; 42 | -------------------------------------------------------------------------------- /src/routers/index.ts: -------------------------------------------------------------------------------- 1 | import router from "@/routers/router"; 2 | import { GlobalStore } from "@/store/index"; 3 | import { MenuStore } from "@/store/modules/menu"; 4 | import { TabsStore } from "@/store/modules/tabs"; 5 | import NProgress from "nprogress"; 6 | import "nprogress/nprogress.css"; 7 | 8 | /** 9 | * 路由拦截 10 | */ 11 | router.beforeEach((to, from, next) => { 12 | NProgress.start() 13 | // 判断当前路由是否需要访问权限 14 | const tabsStore = TabsStore(); 15 | 16 | const hasTabs = tabsStore.tabsOption.findIndex((tab) => tab.key === to.path) > -1; 17 | 18 | if (!hasTabs && to.matched.length > 0) { 19 | const path = to.path === "/login" || to.name === "not-found"; 20 | if (!path) { 21 | tabsStore.addTab(to.meta.title, to.path); 22 | } 23 | } 24 | const globalStore = GlobalStore(); 25 | if (globalStore.token !== "") { 26 | if (to.name == "login") { 27 | next(); 28 | } else { 29 | if (to.matched.length > 0) { 30 | next(); 31 | } else { 32 | const menuStore = MenuStore(); 33 | menuStore.setMenuList().then(() => { 34 | next({ ...to, replace: true }); 35 | }); 36 | } 37 | } 38 | } else { 39 | if (to.path == "/login") { 40 | next(); 41 | } else { 42 | next({ 43 | path: "/login", 44 | query: { 45 | redirect: to.fullPath, 46 | }, 47 | }); 48 | NProgress.done() 49 | } 50 | } 51 | }); 52 | router.afterEach(() => { 53 | NProgress.done() 54 | }) 55 | 56 | export default router; 57 | -------------------------------------------------------------------------------- /src/views/data-screen/components/PieChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | ![logo](https://uuadmin-1256657314.cos.ap-guangzhou.myqcloud.com/uadmin.png) 6 | 7 |

Vue Antd Admin Template

8 |

9 | 10 | Vue 11 | 12 | 13 | Vite 14 | 15 | 16 | Pinia 17 | 18 | 19 | Ant Design Vue 20 | 21 | 22 | TypeScript 23 | 24 |

25 |
26 | 27 | ## 介绍📖 28 | vue-antd-admin-template(uu Admin)是一个极简的后台管理系统解决方案,它基于 Vue3 和 Ant Design Vue 实现。它使用了最新的前端技术栈 Vue3 Setup语法糖 & Vite3 & Vue-Router4 & Ant Design Vue & Pinia & TypeScript 。 29 | 30 | 目前正在持续更新中,对于项目有不完善的地方,希望大家能多提意见。(点Star⭐🤣) 31 | 32 | ## 在线预览👀 33 | https://shihaozou.github.io/vue-antd-admin-template/ 34 | 35 | ## 仓库地址📚 36 | 37 | | 类型 | 链接(欢迎Star⭐) | 38 | | -------- | -------- | 39 | | gitee地址 | https://gitee.com/shihaozou/vue-antd-admin-template | 40 | | github地址 | https://github.com/SHIHAOZOU/vue-antd-admin-template | 41 | 42 | -------------------------------------------------------------------------------- /src/views/data-screen/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 34 | 35 | 63 | -------------------------------------------------------------------------------- /src/store/modules/menu.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from "pinia"; 2 | import { getMenuList } from '@/api/loginApi/loginFormApi'; 3 | import matchRouter from "@/routers/permissionList" 4 | import { allRouter } from "@/routers/allRouter" 5 | import router from "@/routers"; 6 | import {routes,notFound} from "@/routers/router"; 7 | 8 | export const MenuStore = defineStore({ 9 | id:"MenuStore", 10 | state: () => ({ 11 | // 后端传的菜单列表 12 | menuList:[], 13 | // 过滤后的菜单列表 14 | matchList:[], 15 | // 16 | storageRouter:[], 17 | }), 18 | getters: {}, 19 | actions: { 20 | async setMenuList() { 21 | // 获取后端请求路由 22 | const getRouter = await getMenuList(); 23 | // 与前端动态路由表过滤 24 | const arr = matchRouter(allRouter,getRouter.data); 25 | // 合并公共路由表 26 | const routerList = routes.concat(arr); 27 | routerList.forEach(route => { 28 | const routeName:any = route.name 29 | if ( !router.hasRoute(routeName) ) { 30 | // 动态添加到路由中 31 | router.addRoute(route) 32 | } 33 | }) 34 | notFound.forEach((notFound)=>router.addRoute(notFound)) 35 | 36 | // 保存菜单列表 37 | this.matchList = routerList; 38 | // 保存缓存路由 39 | this.storageRouter = routerList; 40 | // test 41 | } 42 | }, 43 | persist: { 44 | key: "MenuStore", 45 | storage: localStorage, 46 | paths: ['storageRouter'] 47 | } 48 | }) 49 | -------------------------------------------------------------------------------- /src/views/form/form-base/index.vue: -------------------------------------------------------------------------------- 1 | 36 | 57 | 58 | -------------------------------------------------------------------------------- /src/layout/tabsBar/TabsBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 49 | -------------------------------------------------------------------------------- /src/utils/storage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * localStroage 3 | */ 4 | // get 5 | const getLocalStorage = (keyName:string) => { 6 | window.localStorage.getItem(keyName) 7 | } 8 | // get parse 9 | const getLocalStorageParse = (keyName:string) => { 10 | let value = window.localStorage.getItem(keyName) 11 | try { 12 | return JSON.parse(value as string) 13 | } catch (error) { 14 | return value 15 | } 16 | } 17 | // set 18 | const setLocalStorage = (keyName:string,value:any) => { 19 | window.localStorage.setItem(keyName,value) 20 | } 21 | //remove 22 | const removeLocalStorage = (keyName:string) => { 23 | window.localStorage.removeItem(keyName) 24 | } 25 | //clear 26 | const clearLocalStorage = () => { 27 | window.localStorage.clear() 28 | } 29 | 30 | /** 31 | * sessionStroage 32 | */ 33 | // get 34 | const getSessionStorage = (keyName:string) => { 35 | window.sessionStorage.getItem(keyName) 36 | } 37 | // get parse 38 | const getSessionStorageParse = (keyName:string) => { 39 | let value = window.sessionStorage.getItem(keyName) 40 | try { 41 | return JSON.parse(value as string) 42 | } catch (error) { 43 | return value 44 | } 45 | } 46 | // set 47 | const setSessionStorage = (keyName:string,value:any) => { 48 | window.sessionStorage.setItem(keyName,value) 49 | } 50 | //remove 51 | const removeSessionStorage = (keyName:string) => { 52 | window.sessionStorage.removeItem(keyName) 53 | } 54 | //clear 55 | const clearSessionStorage = () => { 56 | window.sessionStorage.clear() 57 | } 58 | 59 | export { 60 | getLocalStorage, 61 | getLocalStorageParse, 62 | setLocalStorage, 63 | removeLocalStorage, 64 | clearLocalStorage, 65 | getSessionStorage, 66 | getSessionStorageParse, 67 | setSessionStorage, 68 | removeSessionStorage, 69 | clearSessionStorage 70 | } -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import router from "@/routers" 2 | import { GlobalStore } from "@/store" 3 | import axios,{AxiosRequestConfig,AxiosError,AxiosResponse} from "axios" 4 | import { message } from 'ant-design-vue'; 5 | 6 | // 默认请求地址 7 | axios.defaults.baseURL= 'https://mock.apifox.cn/m1/1492640-0-default/api' 8 | // 设置超时时间 9 | axios.defaults.timeout=5000 10 | // 跨域时允许携带凭证 11 | axios.defaults.withCredentials=true 12 | /** 13 | * @description: 请求拦截器 14 | * @return {*} 15 | */ 16 | axios.interceptors.request.use( 17 | (config: AxiosRequestConfig) => { 18 | // 全局store 19 | const {token} = GlobalStore() 20 | return {...config, headers:{...config.headers, "Authorization": token}}; 21 | }, 22 | (error: AxiosError) => { 23 | return Promise.reject(error); 24 | } 25 | ) 26 | 27 | /** 28 | * @description: 响应拦截器 29 | * @return {*} 30 | */ 31 | axios.interceptors.response.use( 32 | (response: AxiosResponse) => { 33 | const { data, config } = response; 34 | if (data.code === 500) { 35 | const globalStore = GlobalStore(); 36 | globalStore.setToken(""); 37 | router.replace({ path:"/login" }); 38 | message.error(data.msg); 39 | return Promise.reject(data) 40 | } 41 | // 请求成功返回 42 | return data; 43 | }, 44 | (error) => { 45 | return error; 46 | } 47 | ) 48 | 49 | /** 50 | * @description: 常用请求方法 51 | * @return {*} 52 | */ 53 | const request = { 54 | GET:(url:string,params?:object) => { 55 | return axios.get(url,params) 56 | }, 57 | POST:(url:string,params?:object) => { 58 | return axios.post(url,params) 59 | }, 60 | PUT:(url:string,params?:object) => { 61 | return axios.put(url,params) 62 | }, 63 | DELETE:(url:string,params?:object) => { 64 | return axios.delete(url,params) 65 | } 66 | } 67 | 68 | export default request -------------------------------------------------------------------------------- /src/layout/siderBar/SiderBar.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 51 | 52 | 66 | -------------------------------------------------------------------------------- /src/views/login/index.scss: -------------------------------------------------------------------------------- 1 | .login { 2 | width: 100%; 3 | min-height: 100vh; 4 | 5 | .login-left { 6 | float: left; 7 | width: 30%; 8 | min-height: 100vh; 9 | border-right: 2px solid rgba(213, 211, 211, 0.1); 10 | background-color: #044c90; 11 | 12 | .login-left-info { 13 | padding: 40px; 14 | 15 | :first-child { 16 | color: #fff; 17 | font-size: 40px; 18 | font-weight: 400; 19 | } 20 | 21 | span { 22 | display: inline-block; 23 | font-size: 14px; 24 | margin-top: 10px; 25 | line-height: 1.8; 26 | color: hsla(0, 0%, 100%, .6); 27 | } 28 | } 29 | } 30 | 31 | .login-right { 32 | position: relative; 33 | width: 70%; 34 | min-height: 100vh; 35 | float: right; 36 | background-image: url("../../assets/login-banner.png"); 37 | background-repeat: no-repeat; 38 | background-position: -15% 427px; 39 | .login-right-btn { 40 | position: absolute; 41 | top: 10px; 42 | right: 20px; 43 | } 44 | .login-form { 45 | position: absolute; 46 | top: 50%; 47 | left: 50%; 48 | transform: translateX(-50%); 49 | margin-top: -220px; 50 | width: 460px; 51 | height: 400px; 52 | padding: 25px 30px; 53 | background-color: rgba(213, 211, 211, 0.1); 54 | border-radius: 4px; 55 | 56 | & h4 { 57 | text-align: center; 58 | margin: 0 0 25px 0; 59 | } 60 | } 61 | 62 | .info { 63 | position: absolute; 64 | bottom: 0; 65 | left: 45%; 66 | } 67 | } 68 | } 69 | 70 | .login-form-forgot { 71 | float: right; 72 | } 73 | 74 | .ant-input-affix-wrapper { 75 | height: 40px; 76 | } -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | // @see: https://stylelint.io 2 | 3 | module.exports = { 4 | /* 继承某些已有的规则 */ 5 | extends: [ 6 | "stylelint-config-standard", // 配置stylelint拓展插件 7 | "stylelint-config-html/vue", // 配置 vue 中 template 样式格式化 8 | "stylelint-config-standard-scss", // 配置stylelint scss插件 9 | "stylelint-config-recommended-vue/scss", // 配置 vue 中 scss 样式格式化 10 | "stylelint-config-recess-order", // 配置stylelint css属性书写顺序插件, 11 | "stylelint-config-prettier" // 配置stylelint和prettier兼容 12 | ], 13 | overrides: [ 14 | // 扫描 .vue/html 文件中的 -------------------------------------------------------------------------------- /src/routers/router.ts: -------------------------------------------------------------------------------- 1 | 2 | import { createRouter, createWebHashHistory, RouteRecordRaw} from 'vue-router'; 3 | // import { MenuStore } from "@/store/modules/menu"; 4 | // 在router中使用pinia(getActivePinia was called with no active Pinia. Did you forget to install pinia)报错解决 5 | // import pinia from '@/store/index' 6 | // const menuStore = MenuStore(pinia); 7 | import Layout from "@/layout/index.vue" 8 | 9 | 10 | export const routes:RouteRecordRaw[] = [ 11 | { 12 | path:"/", 13 | redirect: '/home', 14 | component:Layout, 15 | meta: { 16 | title: '首页', 17 | }, 18 | children: [ 19 | { 20 | path: '/home', 21 | name: 'home', 22 | component:() => import("../views/home/index.vue"), 23 | meta: { 24 | title: '首页', 25 | } 26 | }, 27 | // { 28 | // path: '/datascreen', 29 | // name: 'datascreen', 30 | // component:() => import("@/views/data-screen/index.vue"), 31 | // meta: { 32 | // title: '数据大屏', 33 | // } 34 | // }, 35 | // ...menuStore.matchList 36 | ] 37 | }, 38 | { 39 | path:"/login", 40 | name:"login", 41 | component:()=>import("../views/login/index.vue"), 42 | meta:{ 43 | auth: false, 44 | title:"登录页", 45 | key:"login" 46 | } 47 | } 48 | // { 49 | // path:"/home", 50 | // name:"home", 51 | // component:()=>import("@/views/home/index.vue"), 52 | // meta:{ 53 | // requiresAuth: false, 54 | // title:"主页", 55 | // key:"home" 56 | // } 57 | // } 58 | ] 59 | export const notFound = [ 60 | { 61 | path: '/nofound', 62 | name: 'NoFound', 63 | component: () => import('../views/notfound.vue'), 64 | meta: { 65 | title: '404' 66 | } 67 | }, 68 | { 69 | path: '/:pathMatch(.*)*', 70 | name: 'not-found', 71 | component: () => import('../views/notfound.vue'), 72 | meta: { 73 | title: '404' 74 | } 75 | } 76 | ] 77 | 78 | const router = createRouter({ 79 | history: createWebHashHistory(), 80 | routes, 81 | strict:false 82 | }) 83 | 84 | export default router -------------------------------------------------------------------------------- /src/views/form/form-advanced/index.vue: -------------------------------------------------------------------------------- 1 | 47 | 59 | 83 | -------------------------------------------------------------------------------- /src/routers/allRouter.ts: -------------------------------------------------------------------------------- 1 | export const allRouter = [ 2 | { 3 | path: "/datascreen", 4 | name: "datascreen", 5 | redirect: "/datascreen", 6 | // component: () => import("@/layout/index.vue"), 7 | meta: { 8 | title: "数据大屏", 9 | }, 10 | children: [ 11 | { 12 | path: "/datascreen", 13 | name: "datascreen", 14 | component: () => import("@/views/data-screen/index.vue"), 15 | meta: { 16 | title: "数据大屏", 17 | }, 18 | }, 19 | ], 20 | }, 21 | { 22 | path: "/form", 23 | name: "form", 24 | redirect: "/formbase", 25 | component: () => import("@/layout/index.vue"), 26 | meta: { 27 | title: "表单组件", 28 | }, 29 | children: [ 30 | { 31 | path: "/formbase", 32 | name: "form-base", 33 | component: () => import("@/views/form/form-base/index.vue"), 34 | meta: { 35 | title: "基础表单", 36 | }, 37 | }, 38 | { 39 | path: "/advancedform", 40 | name: "form-advanced", 41 | component: () => import("@/views/form/form-advanced/index.vue"), 42 | meta: { 43 | title: "高级表单", 44 | }, 45 | }, 46 | ], 47 | }, 48 | { 49 | path: "/table", 50 | name: "table", 51 | redirect: "/tablebase", 52 | component: () => import("@/layout/index.vue"), 53 | meta: { 54 | title: "表格组件", 55 | }, 56 | children: [ 57 | { 58 | path: "/tablebase", 59 | name: "table-base", 60 | component: () => import("@/views/table/table-base/index.vue"), 61 | meta: { 62 | title: "基础表格", 63 | }, 64 | }, 65 | { 66 | path: "/advancedtable", 67 | name: "table-advanced", 68 | component: () => import("@/views/table/table-advanced/index.vue"), 69 | meta: { 70 | title: "高级表格", 71 | }, 72 | }, 73 | ], 74 | }, 75 | { 76 | path: "/toolkit", 77 | name: "toolkit", 78 | redirect: "/downloadUpload", 79 | component: () => import("@/layout/index.vue"), 80 | meta: { 81 | title: "工具集合", 82 | }, 83 | children: [ 84 | { 85 | path: "/downloadUpload", 86 | name: "download-upload", 87 | component: () => import("@/views/toolkit/downloadUpload.vue"), 88 | meta: { 89 | title: "下载上传", 90 | }, 91 | }, 92 | { 93 | path: "/bigFileUpload", 94 | name: "big-file-upload", 95 | component: () => import("@/views/toolkit/bigFileUpload.vue"), 96 | meta: { 97 | title: "大文件上传", 98 | }, 99 | }, 100 | { 101 | path: "/virtualList", 102 | name: "virtual-list", 103 | component: () => import("@/views/toolkit/virtualList.vue"), 104 | meta: { 105 | title: "虚拟列表", 106 | }, 107 | } 108 | ], 109 | }, 110 | ]; 111 | -------------------------------------------------------------------------------- /src/views/toolkit/virtualList.vue: -------------------------------------------------------------------------------- 1 | 17 | 72 | 73 | -------------------------------------------------------------------------------- /src/views/data-screen/components/ScaleBox.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 65 | 66 | 119 | -------------------------------------------------------------------------------- /src/views/table/table-advanced/index.vue: -------------------------------------------------------------------------------- 1 | 48 | 113 | -------------------------------------------------------------------------------- /src/views/notfound.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | 79 | -------------------------------------------------------------------------------- /src/views/data-screen/components/ListChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 65 | 123 | 126 | -------------------------------------------------------------------------------- /src/views/data-screen/components/LineChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /src/views/data-screen/components/MapChart.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 332 | 333 | 334 | --------------------------------------------------------------------------------