├── mock ├── modules │ ├── comm.json │ ├── adminUser.json │ ├── bannerIndex.json │ ├── login.json │ └── logAdminopr.json └── index.ts ├── public ├── logo.png ├── favicon.ico ├── logo-mini.png ├── images │ ├── avatar.jpeg │ ├── figure-1.png │ ├── figure-2.png │ ├── plant-1.png │ ├── plant-2.png │ ├── 21ac2b09aa79bd2244d244fff4d76447.jpg │ ├── 4e558470e518f2d2950149a807ac0fef.jpeg │ ├── ad35dddcf60f2f00dba3b2fa8883bfcb.jpeg │ ├── b2553ba0e33d8203503477b46a0029ff.jpeg │ ├── e29d994ec3d5d2424ce5448d816d9e20.jpeg │ ├── eed68c68672f38f2933951d6910069c8.jpg │ └── f8c1fb49d1da365a80ee8d430e1e22a6.jpeg ├── assets │ ├── images │ │ ├── null.jpg │ │ ├── logo │ │ │ ├── qq.png │ │ │ ├── baidu.png │ │ │ ├── douyin.png │ │ │ ├── email.png │ │ │ ├── google.png │ │ │ ├── taobao.png │ │ │ ├── weixin.png │ │ │ ├── xigua.png │ │ │ ├── bilibili.png │ │ │ ├── facebook.png │ │ │ ├── jingdong.png │ │ │ ├── kuaishou.png │ │ │ ├── toutiao.png │ │ │ ├── twitter.png │ │ │ ├── youtube.png │ │ │ └── zhifubao.png │ │ └── login_avatar.jpg │ └── css │ │ └── custom.css └── index.html ├── babel.config.js ├── src ├── assets │ └── images │ │ └── bg1.jpg ├── config │ ├── index.ts │ ├── modules │ │ ├── base.ts │ │ ├── HUrl.ts │ │ ├── global.ts │ │ └── cdn.ts │ └── config.ts ├── store │ ├── index.ts │ ├── init.ts │ └── modules │ │ └── setting.ts ├── utils │ ├── index.ts │ └── modules │ │ ├── cache.ts │ │ ├── comm.ts │ │ └── setting.ts ├── main.ts ├── App.vue ├── shims-vue.d.ts ├── views │ ├── layout │ │ ├── components │ │ │ ├── main.vue │ │ │ ├── setting │ │ │ │ ├── affix.vue │ │ │ │ ├── alert-message.vue │ │ │ │ └── drawer.vue │ │ │ ├── header │ │ │ │ ├── collapse.vue │ │ │ │ ├── logo.vue │ │ │ │ ├── toolbar.vue │ │ │ │ ├── breadcrumb.vue │ │ │ │ ├── index.vue │ │ │ │ └── profile.vue │ │ │ ├── index.ts │ │ │ └── aside │ │ │ │ ├── submenu.vue │ │ │ │ └── index.vue │ │ └── index.vue │ ├── dashboard │ │ ├── index.vue │ │ └── readme.en.vue │ ├── error │ │ └── 404.vue │ ├── form │ │ ├── image.vue │ │ ├── index.vue │ │ └── other.vue │ ├── log │ │ └── admin │ │ │ └── operate.vue │ ├── auth │ │ └── login.vue │ ├── banner │ │ └── index │ │ │ └── index.vue │ └── admin │ │ └── user │ │ └── index.vue ├── service │ ├── index.ts │ ├── modules │ │ ├── banner │ │ │ └── index.ts │ │ ├── log │ │ │ └── adminopr.ts │ │ ├── admin │ │ │ └── admin.ts │ │ └── auth │ │ │ └── auth.ts │ └── http │ │ ├── code.ts │ │ └── index.ts ├── router │ ├── modules │ │ ├── components.ts │ │ └── routes.ts │ └── index.ts └── init.ts ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── LICENSE ├── vue.config.js ├── package.json ├── .eslintrc.js ├── README.md └── README.en.md /mock/modules/comm.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "操作成功!" 4 | } -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/logo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/logo-mini.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /public/images/avatar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/avatar.jpeg -------------------------------------------------------------------------------- /public/images/figure-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/figure-1.png -------------------------------------------------------------------------------- /public/images/figure-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/figure-2.png -------------------------------------------------------------------------------- /public/images/plant-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/plant-1.png -------------------------------------------------------------------------------- /public/images/plant-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/plant-2.png -------------------------------------------------------------------------------- /src/assets/images/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/src/assets/images/bg1.jpg -------------------------------------------------------------------------------- /public/assets/images/null.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/null.jpg -------------------------------------------------------------------------------- /public/assets/images/logo/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/qq.png -------------------------------------------------------------------------------- /public/assets/images/logo/baidu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/baidu.png -------------------------------------------------------------------------------- /public/assets/images/logo/douyin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/douyin.png -------------------------------------------------------------------------------- /public/assets/images/logo/email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/email.png -------------------------------------------------------------------------------- /public/assets/images/logo/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/google.png -------------------------------------------------------------------------------- /public/assets/images/logo/taobao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/taobao.png -------------------------------------------------------------------------------- /public/assets/images/logo/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/weixin.png -------------------------------------------------------------------------------- /public/assets/images/logo/xigua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/xigua.png -------------------------------------------------------------------------------- /public/assets/images/login_avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/login_avatar.jpg -------------------------------------------------------------------------------- /public/assets/images/logo/bilibili.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/bilibili.png -------------------------------------------------------------------------------- /public/assets/images/logo/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/facebook.png -------------------------------------------------------------------------------- /public/assets/images/logo/jingdong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/jingdong.png -------------------------------------------------------------------------------- /public/assets/images/logo/kuaishou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/kuaishou.png -------------------------------------------------------------------------------- /public/assets/images/logo/toutiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/toutiao.png -------------------------------------------------------------------------------- /public/assets/images/logo/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/twitter.png -------------------------------------------------------------------------------- /public/assets/images/logo/youtube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/youtube.png -------------------------------------------------------------------------------- /public/assets/images/logo/zhifubao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/assets/images/logo/zhifubao.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /public/images/21ac2b09aa79bd2244d244fff4d76447.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/21ac2b09aa79bd2244d244fff4d76447.jpg -------------------------------------------------------------------------------- /public/images/4e558470e518f2d2950149a807ac0fef.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/4e558470e518f2d2950149a807ac0fef.jpeg -------------------------------------------------------------------------------- /public/images/ad35dddcf60f2f00dba3b2fa8883bfcb.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/ad35dddcf60f2f00dba3b2fa8883bfcb.jpeg -------------------------------------------------------------------------------- /public/images/b2553ba0e33d8203503477b46a0029ff.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/b2553ba0e33d8203503477b46a0029ff.jpeg -------------------------------------------------------------------------------- /public/images/e29d994ec3d5d2424ce5448d816d9e20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/e29d994ec3d5d2424ce5448d816d9e20.jpeg -------------------------------------------------------------------------------- /public/images/eed68c68672f38f2933951d6910069c8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/eed68c68672f38f2933951d6910069c8.jpg -------------------------------------------------------------------------------- /public/images/f8c1fb49d1da365a80ee8d430e1e22a6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/todoadmin/vue-admin-chart/HEAD/public/images/f8c1fb49d1da365a80ee8d430e1e22a6.jpeg -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | // 设置相关功能 文件 2 | import * as G from '@/config/modules/global' 3 | import * as HUrl from '@/config/modules/HUrl' 4 | 5 | export { 6 | G, 7 | HUrl 8 | } 9 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Store 仓库存储相关操作(使用Pinia作为默认存储,不适用vuex) 3 | */ 4 | // 设置、用户、临时数据相关 5 | import settingStore from './modules/setting' 6 | 7 | export { 8 | settingStore, 9 | } 10 | -------------------------------------------------------------------------------- /src/store/init.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' // 导入pinia存储store库 2 | 3 | // store存储持久化 4 | import piniaPersist from 'pinia-plugin-persist' 5 | 6 | // 创建pinia的store实例 7 | const store = createPinia() 8 | store.use(piniaPersist) 9 | export default store 10 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | // 设置相关功能 文件 2 | import * as setting from '@/utils/modules/setting' 3 | // 公共函数类库 4 | import * as comm from '@/utils/modules/comm' 5 | // session,localstorage缓存操作相关 6 | import cache from '@/utils/modules/cache' 7 | 8 | export { 9 | setting, 10 | comm, 11 | cache, 12 | } 13 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import { init } from './init' // 引入init初始化文件,尽可能减少对vue框架文件的修改 4 | 5 | const app = createApp(App) 6 | // 初始化相关的属性 7 | init({ app }).then(async ({ router }) => { 8 | router.isReady().then(() => app.mount('#app')) 9 | }) 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | *.tgitconfig 25 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | 8 | declare module '*.json' { 9 | const value: any; 10 | export default value; 11 | } 12 | 13 | declare module 'js-md5' { 14 | import md5 from 'js-md5' 15 | export default md5 16 | } 17 | declare module "*.md" 18 | -------------------------------------------------------------------------------- /src/views/layout/components/main.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /src/service/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 数据请求服务接口相关API或函数处理 3 | * @author jihua.huang 4 | * @since 2022-03-15 5 | */ 6 | import http from '@/service/http' 7 | import * as SAuth from './modules/auth/auth' 8 | import * as SAdmin from './modules/admin/admin' 9 | import * as SLogAdminOpr from './modules/log/adminopr' 10 | import * as SBanner from './modules/banner/index' 11 | 12 | export { 13 | http, 14 | SAuth, 15 | SAdmin, 16 | SBanner, 17 | SLogAdminOpr, 18 | } 19 | -------------------------------------------------------------------------------- /public/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";.el-sub-menu__title:hover{background:transparent !important;}.theme-default{background:#001529;}.theme-default.collapse-sub-menu{background:#fff !important;}.theme-default .el-sub-menu__title{color:#fff !important;}.theme-default > .logo{color:#dedede;}.theme-default .el-menu-item,.theme-default .el-sub-menu__title{color:#f1f1f1;}.theme-default > li:hover,.theme-default.df-sub-menu:hover,.theme-default > li.is-active,.theme-default.df-sub-menu.is-active{color:#f1f2f3 !important;background-color:#409eff !important;} -------------------------------------------------------------------------------- /src/service/modules/banner/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装广告橱窗功能、比如:列表、添加、删除、修改、审核功能等 3 | * @author jihua.huang 4 | * @date 2022-04-26 5 | */ 6 | //引入文件 7 | import { http } from "@/service"; //引入网络请求http 8 | import { HUrl } from "@/config"; //引入http请求URL地址 9 | 10 | /** 11 | * 列表 12 | * @param params object 13 | * @returns json object 14 | */ 15 | export const list = async (params : any = {}) => { 16 | let data: any; 17 | await http.get(HUrl.BANNER_URL,params).then((result : any) => { 18 | data = result; 19 | }).catch((err: any) => { 20 | http.catch(err) 21 | }); 22 | return data; 23 | } 24 | -------------------------------------------------------------------------------- /src/service/modules/log/adminopr.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装日志Log(管理员操作日志)相关功能等 3 | * @author jihua.huang 4 | * @date 2022-04-10 5 | */ 6 | //引入文件 7 | import { http } from "@/service"; //引入网络请求http 8 | import { HUrl } from "@/config"; //引入http请求URL地址 9 | 10 | /** 11 | * 列表 12 | * @param params object 13 | * @returns json object 14 | */ 15 | export const list = async (params : any = {}) => { 16 | let data: any; 17 | await http.get(HUrl.LOG_ADMIN_OPR_URL,params).then((result : any) => { 18 | data = result; 19 | }).catch((err: any) => { 20 | http.catch(err) 21 | }); 22 | return data; 23 | } 24 | -------------------------------------------------------------------------------- /src/service/modules/admin/admin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装管理员功能、比如:列表、添加、删除、修改、审核、角色、权限、功能等 3 | * @author jihua.huang 4 | * @date 2022-03-29 5 | */ 6 | //引入文件 7 | import { http } from "@/service"; //引入网络请求http 8 | import { HUrl } from "@/config"; //引入http请求URL地址 9 | 10 | /** 11 | * 列表 12 | * @param params object 13 | * @returns json object 14 | */ 15 | export const list = async (params : any = {}) => { 16 | let data: any; 17 | await http.get(HUrl.ADMIN_URL,params).then((result : any) => { 18 | data = result; 19 | }).catch((err: any) => { 20 | http.catch(err) 21 | }); 22 | return data; 23 | } 24 | -------------------------------------------------------------------------------- /src/config/modules/base.ts: -------------------------------------------------------------------------------- 1 | /*HTTP Restful API 数据交换地址 */ 2 | //Production 生产环境 3 | if (process.env.NODE_ENV === 'production') { 4 | //http restful API 请求地址 5 | exports.BASE_URL = 'https://api.todoadmin.com' 6 | } else { 7 | //Dev开发环境 http restful API 请求地址 8 | exports.BASE_URL = 'http://localhost:8080' 9 | } 10 | /** 后缀地址(可以为版本目录,如:/v1) */ 11 | exports.PREFIX_PATH = '/v1' 12 | 13 | //DEV host 地址 14 | exports.DEV_HOST = 'localhost' 15 | //DEV port 端口 16 | exports.DEV_PORT = '8080' 17 | //DEV proxy 18 | exports.PROXY_ROOT = '/api' 19 | //DEV环境数据使用 mock数据 还是api接口数据 20 | //value: mock | api 21 | exports.DEV_DATA_SOURCE = 'mock' 22 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 | -------------------------------------------------------------------------------- /src/config/modules/HUrl.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 接口请求地址配置信息 3 | * @author jihua.huang 4 | * @since 2022-03-19 5 | */ 6 | //引入基本配置文件base.js 7 | const BASE = require("./base.ts") 8 | 9 | //HTTP RESTFUL根地址 10 | export const HTTP_BASE_URL = BASE.BASE_URL 11 | 12 | /** 后缀地址(可以为版本目录,如:/v1) */ 13 | export const BASE_PREFIX = HTTP_BASE_URL + BASE.PREFIX_PATH 14 | 15 | /** 用户登陆提交数据接口 */ 16 | export const LOGIN_URL = BASE_PREFIX + '/auth/login' 17 | /** 用户登出提交数据接口 */ 18 | export const LOGOUT_URL = BASE_PREFIX + '/auth/logout' 19 | /** 管理员管理提交数据接口 */ 20 | export const ADMIN_URL = BASE_PREFIX + '/admin/user' 21 | 22 | /** 广告橱窗数据接口 */ 23 | export const BANNER_URL = BASE_PREFIX + '/banner/index' 24 | 25 | /** 管理员操作日志数据接口 */ 26 | export const LOG_ADMIN_OPR_URL = BASE_PREFIX + '/log/adminopr' -------------------------------------------------------------------------------- /src/views/layout/components/setting/affix.vue: -------------------------------------------------------------------------------- 1 | 6 | 22 | 34 | -------------------------------------------------------------------------------- /src/config/config.ts: -------------------------------------------------------------------------------- 1 | /*HTTP Restful API 数据交换地址 */ 2 | // 是否是生产环境 3 | const PRODUCTION = process.env.NODE_ENV === 'production' 4 | //获取CDN相关的配置 5 | const CDN = require('./modules/cdn.ts') 6 | //获取基本的配置 7 | const BASE = require('./modules/base.ts') 8 | 9 | //资源环境 10 | exports.PRODUCTION = PRODUCTION 11 | //静态资源CDN 12 | exports.EXTERNALS = PRODUCTION ? CDN.PROD_EXTERNALS : CDN.DEV_EXTERNALS 13 | //静态资源CDN 14 | exports.STATIC_CDN = PRODUCTION ? CDN.prodCDN : CDN.devCDN 15 | //资源环境 16 | //Api restful 地址 17 | exports.BASE_URL = BASE.BASE_URL 18 | //DEV host 地址 19 | exports.DEV_HOST = BASE.DEV_HOST 20 | //DEV port 端口 21 | exports.DEV_PORT = BASE.DEV_PORT 22 | //DEV proxy 23 | exports.PROXY_ROOT = BASE.PROXY_ROOT 24 | //DEV环境数据使用 mock数据 还是api接口数据 25 | //value: mock | api default value: api 26 | exports.DEV_DATA_SOURCE = BASE.DEV_DATA_SOURCE || 'mock' 27 | -------------------------------------------------------------------------------- /src/views/dashboard/readme.en.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 | 31 | 41 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "useDefineForClassFields": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/service/http/code.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * http response 返回code的特殊处理 3 | * @author jihua.huang 4 | * @date 2022-04-02 5 | */ 6 | 7 | export const responseCode = [ 8 | 10000,//系统强制升级直接退出,(不可使用) 9 | 10001,//系统可升级(可使用) 10 | 10002,//系统全局重要通知,(不可使用)【点击确认直接退出】 11 | 10003,//系统全局重要通知,(可使用)【点击确认关闭提示窗,系统可继续使用】 12 | 10004,//任何信息的公告,比如:提示部分用户或者所有用户,在xx时候充值赠送红包,红包可进行购买任何东西,但不能提现,(可使用)【点击确认关闭提示窗,系统可继续使用】 13 | 10006,//缺少参数 14 | 10007,//系统繁忙,请稍后重试! 15 | 10008,//请求超时,请稍后重试! 16 | 10009,//非法请求 17 | 10010,//接口访问权限受限 18 | 10011,//请求长度超过限制 19 | 10012,//接口不存在 20 | 10013,//IP请求频次超过上限 21 | 10014,//用户请求频次超过上限 22 | 10015,//token未被授权 23 | 10016,//APP未被授权 24 | 10017,//用户未授权 25 | 10018,//接口签名不合法 26 | 10019,//token失效 27 | 10020,//该设备信息异常 28 | 10021,//认证已失效,请重新登录系统 29 | 10022,//权限不足,请联系管理员 30 | 10023,//记录不存在 31 | 10024,//未知错误 32 | 10026,//请求过于频繁,请稍后重试 33 | 10300,//在代码中传入message错误信息即可 34 | ] -------------------------------------------------------------------------------- /src/views/layout/components/header/collapse.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 40 | -------------------------------------------------------------------------------- /src/views/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 维护component的所有共用组件,以便统一调用 3 | * 直接调用index.ts文件,相关的组件名即可,不需要调用不通目录下的vue文件 4 | * @Author: jihua.huang 5 | * @Since: 2022-03-17 6 | */ 7 | // ==========主模板========== 8 | export { default as Main } from './main.vue' 9 | 10 | // ==========左边菜单栏========== 11 | export { default as CompAside } from './aside/index.vue' 12 | export { default as CompAsideSubmenu } from './aside/submenu.vue' 13 | 14 | // ==========头部工具栏(收缩、面包屑、工具栏、用户profile、logo、通知信息、tabs页面标签等)========== 15 | // ----------头部Index包含(收缩、面包屑、工具栏、用户profile)---------- 16 | export { default as CompHeader } from './header/index.vue' 17 | export { default as CompHeaderLogo } from './header/logo.vue' 18 | 19 | // ==========设置(工具箱、消息弹出层等)========== 20 | export { default as CompSettingAlertMessage } from './setting/alert-message.vue' 21 | 22 | // ===========回到顶部=============== 23 | export { default as CompAffix } from './setting/affix.vue' 24 | export { default as CompDrawer } from './setting/drawer.vue' 25 | -------------------------------------------------------------------------------- /src/views/layout/components/header/logo.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 41 | -------------------------------------------------------------------------------- /src/views/layout/components/header/toolbar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 TodoAdmin 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/views/error/404.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 40 | -------------------------------------------------------------------------------- /src/router/modules/components.ts: -------------------------------------------------------------------------------- 1 | //========================对应的Route component Vue组件模板地址(非赖加载方式)====================== 2 | import Layout from '@/views/layout/index.vue' 3 | import LoginLayout from '@/views/auth/login.vue' 4 | import NotFound from '@/views/error/404.vue' 5 | //========================对应的Route component Vue组件模板地址(非赖加载方式)====================== 6 | 7 | //========================对应的Route component Vue组件模板地址(赖加载方式)====================== 8 | //首页 9 | const Home = () => import('@/views/dashboard/index.vue') 10 | //广告橱窗首页 11 | const BannerIndex = () => import('@/views/banner/index/index.vue') 12 | //管理员管理首页 13 | const AdminUser = () => import('@/views/admin/user/index.vue') 14 | //日志管理-管理员操作日志首页 15 | const LogAdminOpr = () => import('@/views/log/admin/operate.vue') 16 | //README 17 | const ReadMeEN = () => import('@/views/dashboard/readme.en.vue') 18 | 19 | //========================对应的Route component Vue组件模板地址(赖加载方式)====================== 20 | 21 | export const componentRouter = { 22 | 'Layout':Layout, 23 | 'LoginLayout':LoginLayout, 24 | 'NotFound':NotFound, 25 | 'Home':Home, 26 | 'BannerIndex':BannerIndex, 27 | 'AdminUser':AdminUser, 28 | 'LogAdminOpr':LogAdminOpr, 29 | 'ReadMeEN':ReadMeEN, 30 | } 31 | -------------------------------------------------------------------------------- /src/init.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 此文件的创建目的是为了尽可能减少对vue框架本身文件的修改 3 | 此文件是:初始化相关组件、插件、存储、网络、图标,以及引入样式、JS/TS文件等 4 | */ 5 | import router from './router' // 路由 6 | import store from './store/init' // store 存储仓库 7 | import ElementPlus from 'element-plus' // 引入ElementPlus 8 | import * as ElementPlusIconsVue from '@element-plus/icons-vue' 9 | 10 | //当为非生产环境(!production)时导入mock 11 | if (process.env.NODE_ENV !== "production") { 12 | require("element-plus/dist/index.css") 13 | //引入配置文件config.js 14 | const conf = require("./config/config.ts") 15 | //DEV环境数据使用 mock数据 还是api接口数据 16 | //value: mock || api default value: api 17 | if (conf.DEV_DATA_SOURCE === 'mock') { 18 | // import { mockXHR } from './../mock/index'; // 引入自定义的mock 19 | const MockM = require("./../mock/index.ts"); 20 | const mockXHR = MockM.mockXHR; 21 | mockXHR() 22 | } 23 | } 24 | 25 | export async function init(options: { app: any }) { 26 | const { app } = options 27 | app.use(store) 28 | app.use(router) 29 | // 引入ElementPlus以及初始化相关的部分属性 30 | app.use(ElementPlus) 31 | // 初始化Element ICON图标 32 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) { 33 | app.component(key, component) 34 | } 35 | //ElIcon({ app }) 36 | return { router } 37 | } 38 | -------------------------------------------------------------------------------- /src/views/layout/components/setting/alert-message.vue: -------------------------------------------------------------------------------- 1 | 15 | 39 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter,createWebHistory } from 'vue-router' 2 | 3 | import { routes, existWhite } from './modules/routes' 4 | import { ElMessage } from 'element-plus' 5 | import { cache } from '@/utils'; 6 | import { G } from '@/config' 7 | 8 | const router = createRouter({ 9 | history: createWebHistory(), 10 | routes 11 | }) 12 | 13 | // 导航路由守卫 14 | router.beforeEach((to:any, from:any, next:any) => { 15 | try { 16 | const title = to.meta && to.meta.title; 17 | if (title) { 18 | document.title = title; 19 | } 20 | // 路由在白名单里面 21 | if (existWhite(to.path)) { 22 | next() 23 | } else { 24 | const token = cache.getLocalStorage(G.AUTHORIZATION_TOKEN) 25 | // 如果token或userInfo为空、null、{}则跳转到指定login页面进行登陆 26 | if (!token) { 27 | // 保存我们所在的位置,以便以后再来 28 | next(G.LOGIN_URL); 29 | } else { 30 | if (to.matched.length === 0) { 31 | ElMessage.error('找不到路由-NotFound Router...') 32 | from.name ? next({ path: from.path, query: from.query }) : next(G.NOTFOUND_URL) 33 | } else { 34 | next() 35 | } 36 | } 37 | } 38 | } catch (error) { 39 | throw error 40 | } 41 | }) 42 | 43 | // 路由跳转后钩子函数中 - 执行进度条加载结束 44 | router.afterEach(() => { 45 | }) 46 | 47 | export default router 48 | -------------------------------------------------------------------------------- /src/views/layout/components/header/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 34 | 35 | 48 | -------------------------------------------------------------------------------- /src/views/layout/components/header/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 40 | 41 | 52 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= htmlWebpackPlugin.options.title %> 8 | 9 | 10 | <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %> 11 | 12 | 13 | <% } %> 14 | 15 | 16 | 19 | 20 | 21 |
22 | 23 | <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> 24 | 25 | <% } %> 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /mock/index.ts: -------------------------------------------------------------------------------- 1 | import Mock from 'mockjs' 2 | //引入配置文件config.js 3 | const conf = require("../src/config/config.ts") 4 | // 设置拦截ajax请求的相应时间 5 | Mock.setup({ 6 | timeout: '200-500' 7 | }) 8 | //读取json文件 9 | function getJsonFile(filePath:string = '') { 10 | if (!filePath) return {} 11 | //读取指定json文件 12 | var json = require('./modules/' + filePath) 13 | return json 14 | } 15 | 16 | // 获取mock的响应数据 17 | const getResponse = ( 18 | url: string, 19 | type: string, 20 | data: {}[], 21 | res: {}[], 22 | ) => { 23 | return { 24 | url, 25 | type: type || "get", 26 | response: (config: any) => { 27 | return res 28 | }, 29 | } 30 | } 31 | 32 | //axios的请求地址,比如:http://api.xxx.com 33 | const MOCK_BASE_URL = conf.BASE_URL 34 | //api版本目录,比如:v1 35 | const PREFIX_PATH = conf.PREFIX_PATH 36 | const mocks = [ 37 | getResponse(MOCK_BASE_URL + PREFIX_PATH + "/auth/login", "post", [], getJsonFile('login.json')), 38 | getResponse(MOCK_BASE_URL + PREFIX_PATH + "/auth/logout", "post", [], getJsonFile('comm.json')), 39 | getResponse(MOCK_BASE_URL + PREFIX_PATH + "/banner/index", "get", [], getJsonFile('bannerIndex.json')), 40 | getResponse(MOCK_BASE_URL + PREFIX_PATH + "/admin/user", "get", [], getJsonFile('adminUser.json')), 41 | getResponse(MOCK_BASE_URL + PREFIX_PATH + "/log/adminopr", "get", [], getJsonFile('logAdminopr.json')), 42 | ] 43 | 44 | export const mockXHR = () => { 45 | for (const i of mocks) { 46 | Mock.mock(new RegExp(i.url), i.type || "get", i.response) 47 | } 48 | } -------------------------------------------------------------------------------- /src/config/modules/global.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 常量、相关配置信息 3 | */ 4 | // 请求头验证的 Authorization token 字段名称 5 | export const AUTHORIZATION_TOKEN:string = 'token' 6 | // 路由菜单列表 7 | export const ROUTE_MENU:string = 'menu' 8 | 9 | // axios请求的timeout超时时间,多少秒 10 | export const AXIOS_TIMEOUT:number = 5000 11 | 12 | // 保存语言的cache storage对应 key 13 | export const LANG_KEY:string = 'LanguageKey' 14 | 15 | // 语言选择(先获取ver.lang的语言,如果ver.lang不存在,则获取LANG_DEFAULT常量的值) zh-cn:中文 en-us:英文 默认为:中文简体 16 | export const LANG_DEFAULT:string = 'zh-cn' 17 | // 默认主题皮肤 18 | export const THEME_DEFAULT = 'theme-default' 19 | 20 | // 保存过期时间的token key 21 | export const TOKEN_TIME_KEY = 'ExpireTime' 22 | // 过期时间有效时长 23 | export const TOKEN_TIME_VALUE = 2 * 24 * 60 * 60 24 | 25 | // 后台管理列表每页默认多少条 26 | export const TABLE_LIST_SIZE = 20 27 | 28 | // 跳转Login的URL地址 29 | export const LOGIN_URL = '/login'; 30 | // 跳转Home的URL地址 31 | export const HOME_URL = '/'; 32 | // 跳转404URL地址 33 | export const NOTFOUND_URL = '/404'; 34 | // 地图map CDN地址 https://geo.datav.aliyun.com/areas_v3/bound = /amap 35 | export const MAPS_URL = { 36 | //'china':'https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json', 37 | 'china':'/api/assets/json/china.json', 38 | //'china':'https://geo.datav.aliyun.com/areas_v3/bound/geojson?code=100000_full', 39 | '100000':'https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json', 40 | }; 41 | 42 | //Github项目仓库地址 43 | export const GITHUB_REPO_URL = 'https://github.com/todoadmin/vue-admin-chart' 44 | //Gitee 码云项目仓库地址 45 | export const GITEE_REPO_URL = 'https://gitee.com/todoadmin/vue-admin-chart' 46 | -------------------------------------------------------------------------------- /mock/modules/adminUser.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "操作成功!", 4 | "data": { 5 | "list": [ 6 | { 7 | "id": 24, 8 | "uqid": 188, 9 | "uid": 890770, 10 | "level": 10, 11 | "status": 1, 12 | "salt": 21227, 13 | "gender": 1, 14 | "create_time": 1650776342, 15 | "update_time": 0, 16 | "login_time": 0, 17 | "password": "8dfbb697ec9f75a074e26d67c5122511", 18 | "username": "ttttt", 19 | "truename": "哈市是", 20 | "ip": "2130706433", 21 | "email": "vpanda666@gmail.com", 22 | "remark": "哈哈哈12312312", 23 | "role": "[136, 134, 90, 112, 114, 118]", 24 | "extra": "[55, 163, 165, 170]" 25 | }, 26 | { 27 | "id": 19, 28 | "uqid": 156, 29 | "uid": 777157, 30 | "level": 10, 31 | "status": 1, 32 | "salt": 6851, 33 | "gender": 2, 34 | "create_time": 1647396480, 35 | "update_time": 1647590886, 36 | "login_time": 1647590880, 37 | "password": "8f4ffa93791f8a92e7e5a1b1f490f1ac", 38 | "username": "admin", 39 | "truename": "管理员", 40 | "ip": "2130706433", 41 | "email": "test123456@admin.com", 42 | "remark": "143343", 43 | "role": "[118]", 44 | "extra": "[259, 261, 265, 267, 1425]" 45 | } 46 | ], 47 | "total": 2, 48 | "size": 2, 49 | "page": 1 50 | } 51 | } -------------------------------------------------------------------------------- /src/views/layout/components/aside/submenu.vue: -------------------------------------------------------------------------------- 1 | 25 | 48 | 57 | -------------------------------------------------------------------------------- /src/service/modules/auth/auth.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装登陆/登出/验证码/token等 3 | * @author jihua.huang 4 | * @date 2022-03-09 5 | */ 6 | //引入文件 7 | import { http } from "@/service"; //引入网络请求http 8 | import { G,HUrl } from "@/config"; //引入http请求URL地址 9 | //导入 store的存储模块 10 | import { settingStore } from '@/store' 11 | // 导入cache模块 12 | import { cache } from '@/utils' 13 | 14 | /** 15 | * 用户登陆 16 | * @param params object 17 | * @returns json object 18 | */ 19 | export const login = async (params : any) => { 20 | // 获取setting store 21 | const settStore = settingStore() 22 | 23 | let data: any; 24 | await http.post(HUrl.LOGIN_URL,params).then((result : any) => { 25 | //只处理code = 0的数据 26 | if (result.code === 0) { 27 | //type:0 为ping 心跳,不需处理,WS数据 28 | if (result.type !== 0) { 29 | // 设置token,并写入到store中 30 | let token = result.token || '' 31 | //Pinia 保存一份 32 | settStore.setToken(token) 33 | //LocalStorage原始也保存一份 34 | cache.setLocalStorage(G.AUTHORIZATION_TOKEN,token) 35 | } 36 | 37 | // 设置用户基本信息,并写入到store中 38 | let userInfo = result.data.userInfo 39 | if (userInfo) { 40 | settStore.setUserInfo(userInfo) 41 | } 42 | } 43 | data = result; 44 | }).catch((err: any) => { 45 | http.catch(err) 46 | }); 47 | return data; 48 | } 49 | 50 | 51 | /** 52 | * 用户登出(需要清理相关的数据和缓存,已经初始化某些数据) 53 | * @param params object 54 | * @returns json object 55 | */ 56 | export const logout = async (params : any) => { 57 | let data: any; 58 | await http.post(HUrl.LOGOUT_URL,params).then((result : any) => { 59 | //退出登陆要清理相关的localstrage和session缓存 60 | //清理session 61 | //清除localStorage所有的值 62 | cache.clearLocalStorage() 63 | data = result; 64 | }).catch((err: any) => { 65 | http.catch(err) 66 | }); 67 | return data; 68 | } 69 | -------------------------------------------------------------------------------- /src/config/modules/cdn.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 配置CDN相关 setting CDN 3 | */ 4 | //获取package.json的属性内容 5 | const pack = require('../../../package.json') 6 | //获取package.json的依赖属性 7 | const ver = pack.dependencies 8 | //生产环境的CDN连接地址 production cdn link 9 | exports.prodCDN = { 10 | css: [ 11 | 'https://cdn.bootcdn.net/ajax/libs/element-plus/' + ver['element-plus'].replace(/\~|\^/g, '') + '/index.css', 12 | 'https://cdn.bootcdn.net/ajax/libs/github-markdown-css/' + ver['github-markdown-css'].replace(/\~|\^/g, '') + '/github-markdown.min.css', 13 | ], 14 | js: [ 15 | 'https://cdn.bootcdn.net/ajax/libs/vue/' + ver.vue.replace(/\~|\^/g, '') + '/vue.runtime.global.prod.min.js', 16 | 'https://cdn.bootcdn.net/ajax/libs/vue-router/' + ver['vue-router'].replace(/\~|\^/g, '') + '/vue-router.global.prod.min.js', 17 | 'https://cdn.bootcdn.net/ajax/libs/axios/' + ver.axios.replace(/\~|\^/g, '') + '/axios.min.js', 18 | 'https://cdn.bootcdn.net/ajax/libs/vue-demi/' + ver['vue-demi'].replace(/\~|\^/g, '') + '/index.iife.min.js', 19 | 'https://cdn.bootcdn.net/ajax/libs/pinia/' + ver.pinia.replace(/\~|\^/g, '') + '/pinia.iife.prod.min.js', 20 | 'https://cdn.bootcdn.net/ajax/libs/element-plus/' + ver['element-plus'].replace(/\~|\^/g, '') + '/index.full.min.js', 21 | 'https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.min.js', 22 | 'https://unpkg.com/@element-plus/icons-vue', 23 | 'https://cdn.jsdelivr.net/npm/@element-plus/icons-vue@2.0.6/dist/index.iife.min.js', 24 | ], 25 | } 26 | 27 | //DEV环境的CDN连接地址 dev cdn link 28 | exports.devCDN = { 29 | css: [ 30 | '/assets/css/element-plus.min.css', 31 | '/assets/css/github-markdown.min.css', 32 | ], 33 | js: [ 34 | ], 35 | } 36 | 37 | //cdn方式引入文件,以下文件不打包,使用外部CDN连接 38 | exports.PROD_EXTERNALS = { 39 | vue:'Vue', 40 | 'vue-router': 'VueRouter', 41 | 'vue-demi': 'VueDemi', 42 | axios:'axios', 43 | pinia:'Pinia', 44 | 'element-plus':'ElementPlus', 45 | '@element-plus/icons-vue':'ElementPlusIconsVue', 46 | 'js-md5':'md5', 47 | } 48 | 49 | exports.DEV_EXTERNALS = {} 50 | -------------------------------------------------------------------------------- /src/utils/modules/cache.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 自定义Cache公共函数,主要操作localStorage和sessionStorage 3 | * @author: Jihua.Huang 4 | * @date: 2022-3-1 5 | */ 6 | const cache = { 7 | // ====================localStorage====================== 8 | /** 9 | * @name: 设置localStorage值 10 | * @date: 2022-3-1 11 | * @param: key string cache-key名称 12 | * @param: value string cache值 13 | */ 14 | setLocalStorage(key: string, value: string) { 15 | window.localStorage.setItem(key, value) 16 | }, 17 | 18 | /** 19 | * @name: 获取localStorage值 20 | * @date: 2022-3-1 21 | * @param: key string cache-key名称 22 | */ 23 | getLocalStorage(key: string) { 24 | const value = window.localStorage.getItem(key) 25 | return value || '' 26 | }, 27 | 28 | /** 29 | * @name: 删除localStorage值 30 | * @date: 2022-3-1 31 | * @param: key string cache-key名称 32 | */ 33 | delLocalStorage(key: string) { 34 | window.localStorage.removeItem(key) 35 | }, 36 | 37 | /** 38 | * @name: 清除localStorage所有的值 39 | * @date: 2022-3-1 40 | * @param: null 41 | */ 42 | clearLocalStorage() { 43 | window.localStorage.clear() 44 | }, 45 | 46 | 47 | // ====================sessionStorage====================== 48 | /** 49 | * @name: 设置sessionStorage值 50 | * @date: 2022-3-1 51 | * @param: key string cache名称 52 | * @param: value string cache-key名称 53 | */ 54 | setSessionStorage(key: string, value: string) { 55 | window.sessionStorage.setItem(key, value) 56 | }, 57 | 58 | /** 59 | * @name: 获取sessionStorage值 60 | * @date: 2022-3-1 61 | * @param: key string cache-key名称 62 | */ 63 | getSessionStorage(key: string) { 64 | const value = window.sessionStorage.getItem(key) 65 | return value || '' 66 | }, 67 | 68 | /** 69 | * @name: 删除sessionStorage值 70 | * @date: 2022-3-1 71 | * @param: key string cache-key名称 72 | */ 73 | delSessionStorage(key: string) { 74 | window.sessionStorage.removeItem(key) 75 | }, 76 | 77 | /** 78 | * @name: 清除sessionStorage所有的值 79 | * @date: 2022-3-1 80 | * @param: null 81 | */ 82 | clearSessionStorage() { 83 | window.sessionStorage.clear() 84 | } 85 | } 86 | export default cache 87 | -------------------------------------------------------------------------------- /src/views/layout/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 48 | 49 | 102 | -------------------------------------------------------------------------------- /src/store/modules/setting.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 定义基于pinia的store存储 3 | * 设置相关的store数据(token、菜单、标签等相关) 4 | * @author jihua.huang 5 | * @since 2022-03-15 6 | */ 7 | import { defineStore } from 'pinia' 8 | 9 | // 创建store,defineStore调用后返回函数,调用该函数获得Store实体 10 | const settingStore = defineStore({ 11 | // 必传的参数,id值,在store中唯一 12 | id: 'settingStore', 13 | // 定义数据,返回对象和属性 14 | state: () => ({ 15 | token: '', // 请求token 16 | userInfo: '', // 用户基本信息 17 | prefer: {}, // 设置偏好,包括:主题皮肤等 18 | collapse: false, // 左边菜单折叠 19 | drawer: false, // 右边抽屉工具页面 20 | dropDown: false, // 用户名的下拉菜单折叠 21 | defaultActive: '', // 默认激活的菜单path -key 22 | }), 23 | // 获取store模块的属性 24 | getters: { 25 | getToken: (state) => state.token, 26 | getUserInfo: (state) => state.userInfo, 27 | getPrefer: (state) => state.prefer, 28 | getCollapse: (state) => state.collapse, 29 | getDrawer: (state) => state.drawer, 30 | getDropDown: (state) => state.dropDown, 31 | getDefaultActive: (state) => state.defaultActive, 32 | }, 33 | // 设置store模块的属性 34 | actions: { 35 | // 保存token 36 | setToken(token: string) { 37 | this.token = token 38 | }, 39 | // 保存用户基本信息 40 | setUserInfo(userInfo: any) { 41 | this.userInfo = userInfo 42 | }, 43 | // 设置偏好,包括:主题皮肤等 44 | setPrefer(prefer: any) { 45 | this.prefer = prefer 46 | }, 47 | // 保存左边menu折叠状态 48 | setCollapse(collapse: boolean) { 49 | this.collapse = collapse 50 | }, 51 | // 设置右边抽屉工具页面打开关闭状态 52 | setDrawer(drawer: boolean) { 53 | this.drawer = drawer 54 | }, 55 | // 保存用户名的下拉menu折叠状态 56 | setDropDown(dropDown: boolean) { 57 | this.dropDown = dropDown 58 | }, 59 | // 设置默认激活的菜单path -key 60 | setDefaultActive(defaultActive: string) { 61 | this.defaultActive = defaultActive 62 | }, 63 | }, 64 | 65 | // 开启数据缓存 -- 需要在store/index.js中import piniaPluginPersist 插件 66 | persist: { 67 | enabled: true, // 开启持久化 68 | strategies: [ 69 | { 70 | // key: 'token', //自定义Key,也可以去掉,使用默认值为参数id的值 71 | storage: localStorage, // 可以为localStorage或者sessionStorage 72 | // 可以通过paths指定需要持久化的值,其他没有指定的则不会持久化 73 | paths: [ 74 | 'token', 'collapse', 'userInfo', 'dropDown','defaultActive', 'drawer', 'prefer', 75 | ] 76 | } 77 | ] 78 | } 79 | }) 80 | 81 | export default settingStore 82 | -------------------------------------------------------------------------------- /src/views/layout/components/aside/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 68 | 86 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // 引入配置文件config.js 2 | const conf = require('./src/config/config.ts') 3 | 4 | const { defineConfig } = require('@vue/cli-service') 5 | const webpack = require('webpack') 6 | module.exports = defineConfig({ 7 | productionSourceMap: false, //不生成map文件 8 | transpileDependencies: true, 9 | lintOnSave: false, 10 | parallel: false, 11 | chainWebpack: config => { 12 | // Markdown文件 13 | config.module.rule('md') 14 | .test(/\.md/) 15 | .use('vue-loader') 16 | .loader('vue-loader') 17 | .end() 18 | .use('vue-markdown-loader') 19 | .loader('vue-markdown-loader/lib/markdown-compiler') 20 | .options({ 21 | raw: true 22 | }) 23 | // 导入cdn静态文件连接 来自config.ts 24 | config.plugin('html').tap(args => { 25 | args[0].cdn = conf.STATIC_CDN 26 | return args 27 | }) 28 | if (conf.PRODUCTION) { 29 | // 删除预加载优化 30 | config.plugins.delete('preload') 31 | config.plugins.delete('prefetch') 32 | // 压缩优化 33 | config.optimization.minimize(true) 34 | // 分割优化 35 | config.optimization.splitChunks({ 36 | chunks: 'all' 37 | }) 38 | // 清除生成环境的console.log输出 39 | config.optimization 40 | .minimizer('terser') 41 | .tap(args => { 42 | Object.assign(args[0].terserOptions.compress, { 43 | pure_funcs: ['console.log'] 44 | }) 45 | return args 46 | }) 47 | } 48 | }, 49 | configureWebpack: { 50 | // cdn的方式引入js文件等 51 | externals: conf.EXTERNALS, 52 | optimization: { 53 | splitChunks: { 54 | cacheGroups: { 55 | // node_modules目录下的库剥离 56 | vendor: { 57 | chunks: 'all', 58 | test: /node_modules/, 59 | name: 'vendor', 60 | minChunks: 1, 61 | maxInitialRequests: 5, 62 | minSize: 0, 63 | priority: 100 64 | }, 65 | // 公用|自定义模块剥离 66 | common: { 67 | chunks: 'all', 68 | test: /[\\/]src[\\/](utils|config|service|router|store|directive)[\\/]/, 69 | name: 'common', 70 | minChunks: 1, 71 | maxInitialRequests: 5, 72 | minSize: 0, 73 | priority: 60 74 | }, 75 | runtimeChunk: { 76 | name: 'manifest' 77 | } 78 | } 79 | } 80 | } 81 | }, 82 | devServer: { 83 | host: conf.DEV_HOST, // dev环境主机地址 84 | port: conf.DEV_PORT, // dev环境默认端口 85 | proxy: { 86 | [conf.PROXY_ROOT]: { 87 | target: conf.BASE_URL, // 设置地址代替axios的BASE_URL 88 | ws: true, // 代理 websocket 89 | changeOrigin: true, // 跨域 90 | secure: true, // https接口 91 | // 路径重写 92 | pathRewrite: { 93 | ['^${conf.PROXY_ROOT}']: '/' 94 | } 95 | } 96 | } 97 | } 98 | }) 99 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-admin-chart", 3 | "version": "1.0.0", 4 | "author": "程序猿视觉[CoderVision] ", 5 | "homepage": "https://base.todoadmin.com", 6 | "description": "Simple fast beautiful vue admin template,vue3 admin,vue-admin,vue-admin-chart,vue后台管理,vue3管理后台,todoadmin chart,vue element-plus,element admin,vue echart,vue element admin,https://base.todoadmin.com", 7 | "license": "MIT", 8 | "private": false, 9 | "scripts": { 10 | "serve": "vue-cli-service serve", 11 | "build": "vue-cli-service build", 12 | "lint": "vue-cli-service lint", 13 | "report": "vue-cli-service build --report --mode production" 14 | }, 15 | "dependencies": { 16 | "@element-plus/icons-vue": "^2.0.4", 17 | "axios": "^0.27.2", 18 | "core-js": "^3.23.1", 19 | "element-plus": "^2.2.5", 20 | "github-markdown-css": "^5.1.0", 21 | "highlight.js": "^11.5.1", 22 | "js-md5": "^0.7.3", 23 | "pinia": "^2.0.14", 24 | "pinia-plugin-persist": "^1.0.0", 25 | "vue": "^3.2.37", 26 | "vue-demi": "^0.13.1", 27 | "vue-router": "^4.0.16" 28 | }, 29 | "devDependencies": { 30 | "@typescript-eslint/eslint-plugin": "^5.28.0", 31 | "@typescript-eslint/parser": "^5.28.0", 32 | "@vue/cli-plugin-babel": "^5.0.5", 33 | "@vue/cli-plugin-eslint": "^5.0.5", 34 | "@vue/cli-plugin-router": "^5.0.5", 35 | "@vue/cli-plugin-typescript": "^5.0.5", 36 | "@vue/cli-service": "^5.0.5", 37 | "@vue/eslint-config-standard": "^7.0.0", 38 | "@vue/eslint-config-typescript": "^11.0.0", 39 | "babel-eslint": "^10.1.0", 40 | "eslint": "^8.17.0", 41 | "eslint-plugin-import": "^2.26.0", 42 | "eslint-plugin-node": "^11.1.0", 43 | "eslint-plugin-promise": "^6.0.0", 44 | "eslint-plugin-vue": "^9.1.1", 45 | "mockjs": "^1.1.0", 46 | "sass": "^1.52.3", 47 | "sass-loader": "^13.0.0", 48 | "terser-webpack-plugin": "^5.3.3", 49 | "ts-loader": "^9.3.0", 50 | "typescript": "^4.7.3", 51 | "vue-loader": "^17.0.0", 52 | "vue-markdown-loader": "^2.5.0", 53 | "vue-template-compiler": "^2.6.14" 54 | }, 55 | "repository": { 56 | "type": "git", 57 | "url": "git+https://github.com/todoadmin/vue-admin-chart.git" 58 | }, 59 | "keywords": [ 60 | "todoadmin", 61 | "vue", 62 | "admin", 63 | "chart-admin", 64 | "dashboard", 65 | "element-ui", 66 | "vue-admin", 67 | "element-admin", 68 | "admin-template", 69 | "admin-management", 70 | "management-system" 71 | ], 72 | "bugs": { 73 | "url": "https://github.com/todoadmin/vue-admin-chart/issues" 74 | }, 75 | "engines": { 76 | "node": ">=8.9", 77 | "npm": ">= 3.0.0" 78 | }, 79 | "eslintConfig": { 80 | "root": true, 81 | "env": { 82 | "node": true 83 | }, 84 | "extends": [ 85 | "plugin:vue/vue3-essential", 86 | "@vue/standard", 87 | "@vue/typescript/recommended" 88 | ], 89 | "parserOptions": { 90 | "ecmaVersion": 2020 91 | }, 92 | "rules": {} 93 | }, 94 | "browserslist": [ 95 | "> 1%", 96 | "last 2 versions", 97 | "not dead", 98 | "not ie 11" 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /src/views/layout/components/setting/drawer.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 78 | 79 | 93 | -------------------------------------------------------------------------------- /src/views/layout/components/header/profile.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 96 | 97 | 141 | -------------------------------------------------------------------------------- /src/utils/modules/comm.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 共用函数 3 | * @author jihua.huang 4 | * @since 2022-03-24 5 | */ 6 | 7 | 8 | /** 9 | * 转换为字符串(false null function undefined NaN 0 空对象 空数组 统一转为空字符串) 10 | * @param value any 11 | * @return String 转换后的字符串 12 | */ 13 | export const toStr = (value:any) => { 14 | let str:string = '' 15 | if (typeof value === 'undefined') { 16 | str = '' 17 | } else if (!value || value === null || typeof value === 'function' || value.isNaN || JSON.stringify(value) === '{}') { 18 | str = '' 19 | } else { 20 | str = value.toString() 21 | } 22 | return str 23 | } 24 | 25 | /** 26 | * 转换开关值(true null function undefined NaN 空对象 空数组 统一转为开) 27 | * @param value any 28 | * @return boolen 转换后的trun/false 29 | */ 30 | export const hasOnOff = (value:any) => { 31 | let ret:boolean = false 32 | if (typeof value === 'undefined') { 33 | ret = true 34 | } else { 35 | if (typeof value === 'string') { 36 | ret = !!parseInt(value) 37 | } else if (typeof value === 'number') { 38 | ret = !!value 39 | } else { 40 | ret = true 41 | } 42 | } 43 | return ret 44 | } 45 | 46 | /** 47 | * 拼接成字符串 48 | * @param str string 49 | * @param str... string 50 | * @return string str 51 | */ 52 | export const concatStr = (str:string,str1:string) => { 53 | return str + str1 54 | } 55 | 56 | /** 57 | * 格式化unix时间戳为年月日格式(2022-03-18 10:08:00) 58 | * @param time number 59 | * @param format string 格式 默认:yyyy-MM-dd h:m:s 60 | * @return string str 61 | */ 62 | export const formatDate = (time:number = 0,format:string = '') => { 63 | let date:any 64 | if (time > 0) { 65 | let timeStr = time.toString(); 66 | if (timeStr.length > 10) { 67 | date = new Date(time); 68 | } else { 69 | // 如果是毫秒则不需要*1000,如果是秒,则需要*1000 70 | date = new Date(time * 1000); 71 | } 72 | } else { 73 | date = new Date(); 74 | } 75 | 76 | if (!format) return date.toLocaleString(); 77 | let dateMap:any = { 78 | y: date.getFullYear(), 79 | M: date.getMonth() + 1, 80 | d: date.getDate(), 81 | h: date.getHours(), 82 | m: date.getMinutes(), 83 | s: date.getSeconds(), 84 | S: date.getMilliseconds() 85 | }; 86 | return format.replace(/(y+)|(M+)|(d+)|(h+)|(m+)|(s+)|(S+)/g, (a) => _add0(dateMap[a[0]], a.length)) 87 | 88 | } 89 | 90 | function _add0(time:any, len:number) { 91 | let timeStr = time.toString(); 92 | let l = timeStr.length; 93 | return l < len ? '0' . repeat(len - l) + time : time; 94 | } 95 | 96 | /** 97 | * 判断字符串是否在item中 98 | * @param item object/string item项 99 | * @param need string 字符串 100 | * @return number 1:弱,2:中,3:强 101 | */ 102 | export const isIndexOf = (item:any,need:any) => { 103 | if (typeof item === 'undefined' || item === null || typeof need === 'undefined' || typeof need === 'undefined') return false 104 | if (Object.keys(item).length >= 1 && need) { 105 | if (item.indexOf(need) !== -1) { 106 | return true; 107 | } 108 | } 109 | return false 110 | } 111 | 112 | /** 113 | * 字符串的IP地址转为整型的IP(127.0.0.1 -> 2130706433 ) 114 | * @param ipAddress string 字符串的IP地址 115 | * @return number 整型IP地址 116 | */ 117 | export const ip2long = ( ipAddress:string ) => { 118 | var output:any; 119 | if ( ipAddress.match ( /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ) ) { 120 | var parts:any = ipAddress.split ( '.' ); 121 | var output:any; 122 | output = ( parts [ 0 ] * Math.pow ( 256, 3 ) ) + 123 | ( parts [ 1 ] * Math.pow ( 256, 2 ) ) + 124 | ( parts [ 2 ] * Math.pow ( 256, 1 ) ) + 125 | ( parts [ 3 ] * Math.pow ( 256, 0 ) ); 126 | } 127 | return output<<0; 128 | } 129 | 130 | /** 131 | * 整型的IP转为字符串的IP地址(2130706433 -> 127.0.0.1) 132 | * @param longAddress number 整型IP地址 133 | * @return string 字符串的IP地址 134 | */ 135 | export const long2ip = ( longAddress:number ) => { 136 | longAddress = longAddress>>>0; 137 | var output; 138 | 139 | if ( !isNaN ( longAddress ) && ( longAddress >= 0 || longAddress <= 4294967295 ) ) { 140 | output = Math.floor (longAddress / Math.pow ( 256, 3 ) ) + '.' + 141 | Math.floor ( ( longAddress % Math.pow ( 256, 3 ) ) / Math.pow ( 256, 2 ) ) + '.' + 142 | Math.floor ( ( ( longAddress % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) / 143 | Math.pow ( 256, 1 ) ) + '.' + 144 | Math.floor ( ( ( ( longAddress % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) % 145 | Math.pow ( 256, 1 ) ) / Math.pow ( 256, 0 ) ); 146 | } 147 | return output; 148 | } 149 | -------------------------------------------------------------------------------- /src/utils/modules/setting.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 设置相关功能 3 | * @author jihua.huang 4 | * @since 2022-03-14 5 | */ 6 | import { G } from '@/config' 7 | import { storeToRefs } from 'pinia' 8 | import { settingStore } from '@/store' // 引入setting permission的store 9 | 10 | /** 11 | * 初始化相关pinia store对象 12 | * @return {*} 13 | */ 14 | const initStore = () => { 15 | const settStore = settingStore() 16 | 17 | // 设置为响应数据 18 | const refSettStore = storeToRefs(settingStore()) 19 | return { settStore, refSettStore } 20 | } 21 | 22 | /** 23 | * 设置左边菜单收起/展开 24 | * @returns bool 25 | */ 26 | export function setCollapse() { 27 | let collapse = initStore().settStore.collapse || false 28 | initStore().settStore.setCollapse(!collapse) 29 | } 30 | 31 | /** 32 | * 获取左边菜单的收起/展开 状态 33 | * @returns bool 34 | */ 35 | export function getCollapse() { 36 | const { collapse } = initStore().refSettStore 37 | return collapse 38 | } 39 | 40 | /** 41 | * 设置右边抽屉工具页面打开/关闭 状态 42 | * @returns bool 43 | */ 44 | export function setDrawer() { 45 | let drawer = initStore().settStore.drawer || false 46 | initStore().settStore.setDrawer(!drawer) 47 | } 48 | 49 | /** 50 | * 获取设置右边抽屉工具页面打开/关闭 状态 51 | * @returns bool 52 | */ 53 | export function getDrawer() { 54 | const { drawer } = initStore().refSettStore 55 | return drawer 56 | } 57 | 58 | /** 59 | * 设置用户名的下拉菜单收起/展开 60 | * @returns bool 61 | */ 62 | export function setDropDown() { 63 | let dropDown = initStore().settStore.dropDown || false 64 | initStore().settStore.setDropDown(!dropDown) 65 | } 66 | 67 | /** 68 | * 获取用户名的下拉菜单收起/展开 状态 69 | * @returns bool 70 | */ 71 | export function getDropDown() { 72 | const { dropDown } = initStore().refSettStore 73 | return dropDown 74 | } 75 | 76 | /** 77 | * 获取用户的基本信息 78 | * @returns object 79 | */ 80 | export function getUserInfo() { 81 | const { userInfo } = initStore().refSettStore 82 | const info : any = userInfo 83 | return info.value 84 | } 85 | 86 | /** 87 | * 设置默认激活的菜单path -key 88 | * @returns number 89 | */ 90 | export function setDefaultActive(path: string) { 91 | if (path) { 92 | initStore().settStore.setDefaultActive(path) 93 | } 94 | return true 95 | } 96 | 97 | /** 98 | * 获取默认激活的菜单path -key 99 | * @returns number 100 | */ 101 | export function getDefaultActive() { 102 | const { defaultActive } = initStore().refSettStore 103 | return defaultActive 104 | } 105 | 106 | /** 107 | * 获取路由数据转换成面包屑数据 108 | * @returns number 109 | */ 110 | export const getBreadcrumb = (route: any) => { 111 | let list:any = [] 112 | const matched:any = route.matched || [] 113 | const home = ['/', '/home'] 114 | const homeObj = { 115 | path: '/home', 116 | name: 'Home', 117 | title: '首页', 118 | link: true 119 | } 120 | // 获取数组长度 121 | const length = Object.keys(matched).length 122 | if (length >= 1) { 123 | Object.values(matched).forEach((item:any) => { 124 | if (item.path && item.path !== '/') { 125 | let breadcrumb:any = { 126 | path: item.path, 127 | name: item.name, 128 | title: item.meta.title 129 | } 130 | list.push(breadcrumb) 131 | } 132 | }) 133 | } 134 | return list 135 | } 136 | 137 | /** 138 | * 设置偏好设置信息 139 | * @returns number 140 | */ 141 | export function setPrefer(data: any) { 142 | // 如果数据值为空,则不处理 143 | if (Object.keys(data).length < 1) { 144 | return 145 | } 146 | 147 | // 获取store里的prefer数据 148 | const prefer = initStore().refSettStore.prefer 149 | let newData:any = {} 150 | // 存在数据的处理 151 | if (Object.keys(prefer.value).length >= 1) { 152 | // 合并对象 153 | newData = Object.assign(prefer.value, data) 154 | } else { 155 | newData = data 156 | } 157 | if (newData) { 158 | // 保存数据到store中 159 | initStore().settStore.setPrefer(newData) 160 | } 161 | return true 162 | } 163 | 164 | /** 165 | * 获取偏好设置信息 166 | * @returns number 167 | */ 168 | export const getPrefer = () => { 169 | const { prefer } = initStore().refSettStore 170 | // 如果不存在主题皮肤,则使用默认皮肤 171 | return prefer 172 | } 173 | 174 | /** 175 | * 获取偏好设置信息 176 | * @returns number 177 | */ 178 | export const getTheme = (val:any) => { 179 | if (typeof val === 'undefined' || !val) { 180 | // 如果不存在主题皮肤,则使用默认皮肤 181 | return G.THEME_DEFAULT 182 | } else { 183 | return val 184 | } 185 | } 186 | /** 187 | * 获取某个偏好设置的开关信息信息 188 | * @returns number 189 | */ 190 | export const getOnOff = (val:any) => { 191 | if (typeof val === 'undefined') { 192 | // 如果不存在key,则使用默认 1 193 | return 1 194 | } else { 195 | return parseInt(val) === 0 ? 0 : 1 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /mock/modules/bannerIndex.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "操作成功!", 4 | "data": { 5 | "list": [ 6 | { 7 | "id": 7, 8 | "uid": 1, 9 | "uqid": 1810, 10 | "sort": 0, 11 | "create_time": 1653929648, 12 | "status": 2, 13 | "start_time": 1661961600, 14 | "end_time": 1664467200, 15 | "ext": "jpeg", 16 | "filename": "8", 17 | "url": "", 18 | "title": "9月清仓大甩卖11", 19 | "content": "9月清仓大甩卖", 20 | "banner_url": "/images/ad35dddcf60f2f00dba3b2fa8883bfcb.jpeg", 21 | "thumb_url": "/images/ad35dddcf60f2f00dba3b2fa8883bfcb.jpeg" 22 | }, 23 | { 24 | "id": 6, 25 | "uid": 1, 26 | "uqid": 1231, 27 | "sort": 0, 28 | "create_time": 1652410084, 29 | "status": 1, 30 | "start_time": 1659283200, 31 | "end_time": 1661875200, 32 | "ext": "jpg", 33 | "filename": "沙漠", 34 | "url": "", 35 | "title": "8月不会停12", 36 | "content": "8月不会停", 37 | "banner_url": "/images/eed68c68672f38f2933951d6910069c8.jpg", 38 | "thumb_url": "/images/eed68c68672f38f2933951d6910069c8.jpg" 39 | }, 40 | { 41 | "id": 5, 42 | "uid": 1, 43 | "uqid": 1229, 44 | "sort": 0, 45 | "create_time": 1652410042, 46 | "status": 1, 47 | "start_time": 1656604800, 48 | "end_time": 1659196800, 49 | "ext": "jpg", 50 | "filename": "hp", 51 | "url": "", 52 | "title": "7月等你来13", 53 | "content": "7月等你来", 54 | "banner_url": "/images/21ac2b09aa79bd2244d244fff4d76447.jpg", 55 | "thumb_url": "/images/21ac2b09aa79bd2244d244fff4d76447.jpg" 56 | }, 57 | { 58 | "id": 4, 59 | "uid": 1, 60 | "uqid": 1227, 61 | "sort": 0, 62 | "create_time": 1652409997, 63 | "status": 1, 64 | "start_time": 1654012800, 65 | "end_time": 1656518400, 66 | "ext": "jpeg", 67 | "filename": "天空下的温暖", 68 | "url": "", 69 | "title": "6月疯狂打折14", 70 | "content": "6月疯狂打折", 71 | "banner_url": "/images/4e558470e518f2d2950149a807ac0fef.jpeg", 72 | "thumb_url": "/images/4e558470e518f2d2950149a807ac0fef.jpeg" 73 | }, 74 | { 75 | "id": 3, 76 | "uid": 1, 77 | "uqid": 1224, 78 | "sort": 0, 79 | "create_time": 1653840063, 80 | "status": 1, 81 | "start_time": 1651334400, 82 | "end_time": 1653926400, 83 | "ext": "jpeg", 84 | "filename": "3", 85 | "url": "", 86 | "title": "5月玩到底15", 87 | "content": "5月玩到底", 88 | "banner_url": "/images/f8c1fb49d1da365a80ee8d430e1e22a6.jpeg", 89 | "thumb_url": "/images/f8c1fb49d1da365a80ee8d430e1e22a6.jpeg" 90 | }, 91 | { 92 | "id": 2, 93 | "uid": 1, 94 | "uqid": 1221, 95 | "sort": 0, 96 | "create_time": 1653840001, 97 | "status": 1, 98 | "start_time": 1652803200, 99 | "end_time": 1656432000, 100 | "ext": "jpeg", 101 | "filename": "1", 102 | "url": "", 103 | "title": "年中大促销16", 104 | "content": "年中大促销", 105 | "banner_url": "/images/b2553ba0e33d8203503477b46a0029ff.jpeg", 106 | "thumb_url": "/images/b2553ba0e33d8203503477b46a0029ff.jpeg" 107 | }, 108 | { 109 | "id": 1, 110 | "uid": 1, 111 | "uqid": 1219, 112 | "sort": 0, 113 | "create_time": 1653840032, 114 | "status": 1, 115 | "start_time": 1652457600, 116 | "end_time": 1655568000, 117 | "ext": "jpeg", 118 | "filename": "13", 119 | "url": "", 120 | "title": "618大优惠17", 121 | "content": "618大优惠", 122 | "banner_url": "/images/e29d994ec3d5d2424ce5448d816d9e20.jpeg", 123 | "thumb_url": "/images/e29d994ec3d5d2424ce5448d816d9e20.jpeg" 124 | } 125 | ], 126 | "info": { 127 | "uid": 1, 128 | "username": "coderVision" 129 | }, 130 | "total": 7, 131 | "size": 7, 132 | "page": 1 133 | } 134 | } -------------------------------------------------------------------------------- /src/views/form/image.vue: -------------------------------------------------------------------------------- 1 | 48 | 49 | 128 | 129 | 159 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | 'plugin:vue/vue3-essential', 8 | '@vue/standard', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | parser: 'babel-eslint', 13 | ecmaVersion: 2020 14 | }, 15 | rules: { 16 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 17 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 18 | 'space-before-function-paren': 0, 19 | 'vue/singleline-html-element-content-newline': 'off', 20 | 'vue/multiline-html-element-content-newline': 'off', 21 | 'vue/name-property-casing': ['error', 'PascalCase'], 22 | 'vue/no-v-html': 'off', 23 | 'vue/html-self-closing': ['error', { 24 | html: { 25 | void: 'never', 26 | normal: 'always', 27 | component: 'always' 28 | }, 29 | svg: 'always', 30 | math: 'always' 31 | }], 32 | 'accessor-pairs': 2, 33 | 'arrow-spacing': [2, { 34 | before: true, 35 | after: true 36 | }], 37 | 'block-spacing': [2, 'always'], 38 | 'brace-style': [2, '1tbs', { 39 | allowSingleLine: true 40 | }], 41 | 'comma-dangle': [2, 'never'], 42 | 'comma-spacing': [2, { 43 | before: false, 44 | after: true 45 | }], 46 | 'comma-style': [2, 'last'], 47 | 'constructor-super': 2, 48 | curly: [2, 'multi-line'], 49 | 'dot-location': [2, 'property'], 50 | 'eol-last': 2, 51 | 'handle-callback-err': [2, '^(err|error)$'], 52 | indent: [2, 2, { 53 | SwitchCase: 1 54 | }], 55 | 'jsx-quotes': [2, 'prefer-single'], 56 | 'key-spacing': [2, { 57 | beforeColon: false, 58 | afterColon: true 59 | }], 60 | 'keyword-spacing': [2, { 61 | before: true, 62 | after: true 63 | }], 64 | 'new-cap': [2, { 65 | newIsCap: true, 66 | capIsNew: false 67 | }], 68 | 'new-parens': 2, 69 | 'no-array-constructor': 2, 70 | 'no-caller': 2, 71 | 'no-class-assign': 2, 72 | 'no-cond-assign': 2, 73 | 'no-const-assign': 2, 74 | 'no-control-regex': 0, 75 | 'no-delete-var': 2, 76 | 'no-dupe-args': 2, 77 | 'no-dupe-class-members': 2, 78 | 'no-dupe-keys': 2, 79 | 'no-duplicate-case': 2, 80 | 'no-empty-character-class': 2, 81 | 'no-empty-pattern': 2, 82 | 'no-eval': 2, 83 | 'no-ex-assign': 2, 84 | 'no-extend-native': 2, 85 | 'no-extra-bind': 2, 86 | 'no-extra-boolean-cast': 2, 87 | 'no-extra-parens': [2, 'functions'], 88 | 'no-fallthrough': 2, 89 | 'no-floating-decimal': 2, 90 | 'no-func-assign': 2, 91 | 'no-implied-eval': 2, 92 | 'no-inner-declarations': [2, 'functions'], 93 | 'no-invalid-regexp': 2, 94 | 'no-irregular-whitespace': 2, 95 | 'no-iterator': 2, 96 | 'no-label-var': 2, 97 | 'no-labels': [2, { 98 | allowLoop: false, 99 | allowSwitch: false 100 | }], 101 | 'no-lone-blocks': 2, 102 | 'no-mixed-spaces-and-tabs': 2, 103 | 'no-multi-spaces': 2, 104 | 'no-multi-str': 2, 105 | 'no-multiple-empty-lines': [2, { 106 | max: 1 107 | }], 108 | 'no-native-reassign': 2, 109 | 'no-negated-in-lhs': 2, 110 | 'no-new-object': 2, 111 | 'no-new-require': 2, 112 | 'no-new-symbol': 2, 113 | 'no-new-wrappers': 2, 114 | 'no-obj-calls': 2, 115 | 'no-octal': 2, 116 | 'no-octal-escape': 2, 117 | 'no-path-concat': 2, 118 | 'no-proto': 2, 119 | 'no-redeclare': 2, 120 | 'no-regex-spaces': 2, 121 | 'no-return-assign': [2, 'except-parens'], 122 | 'no-self-assign': 2, 123 | 'no-self-compare': 2, 124 | 'no-sequences': 2, 125 | 'no-shadow-restricted-names': 2, 126 | 'no-spaced-func': 2, 127 | 'no-sparse-arrays': 2, 128 | 'no-this-before-super': 2, 129 | 'no-throw-literal': 2, 130 | 'no-trailing-spaces': 2, 131 | 'no-undef': 2, 132 | 'no-undef-init': 2, 133 | 'no-unexpected-multiline': 2, 134 | 'no-unmodified-loop-condition': 2, 135 | 'no-unneeded-ternary': [2, { 136 | defaultAssignment: false 137 | }], 138 | 'no-unreachable': 2, 139 | 'no-unsafe-finally': 2, 140 | 'no-unused-vars': [2, { 141 | vars: 'all', 142 | args: 'none' 143 | }], 144 | 'no-useless-call': 2, 145 | 'no-useless-computed-key': 2, 146 | 'no-useless-constructor': 2, 147 | 'no-useless-escape': 0, 148 | 'no-whitespace-before-property': 2, 149 | 'no-with': 2, 150 | 'one-var': [2, { 151 | initialized: 'never' 152 | }], 153 | 'padded-blocks': [2, 'never'], 154 | quotes: [2, 'single', { 155 | avoidEscape: true, 156 | allowTemplateLiterals: true 157 | }], 158 | semi: [2, 'never'], 159 | 'semi-spacing': [2, { 160 | before: false, 161 | after: true 162 | }], 163 | 'space-before-blocks': [2, 'always'], 164 | 'space-in-parens': [2, 'never'], 165 | 'space-infix-ops': 2, 166 | 'space-unary-ops': [2, { 167 | words: true 168 | }], 169 | 'spaced-comment': [2, 'always', { 170 | markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] 171 | }], 172 | 'template-curly-spacing': [2, 'never'], 173 | 'valid-typeof': 2, 174 | 'yield-star-spacing': [2, 'both'], 175 | yoda: [2, 'never'], 176 | 'prefer-const': 0, 177 | 'object-curly-spacing': [2, 'always', { 178 | objectsInObjects: false 179 | }], 180 | 'array-bracket-spacing': [2, 'never'] 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /mock/modules/login.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "操作成功!", 4 | "token": "eyJhbGciOiJIUzI1NiIsInR5cGUiOiJKV1QifQ.eyJpc3MiOiJ7XCJ1aWRcIjoxLFwidXNlcm5hbWVcIjpcImd1ZXN0XCJ9Iiwic3ViIjoiYXBpLnRvZG9hZG1pbi5jbiIsImF1ZCI6ImFwaS50b2RvYWRtaW4uY24iLCJpYXQiOjE2NTUyNjg3NjQsImV4cCI6MTY1NTI4MzE2NCwibmJmIjoxNjU1MjY4NzY0LCJqdGkiOiJmZmJmYjYwYTQ2OTRiYzcyNzBmNTQ3OGIwOGNlY2I1ZiJ9.Xvpz4EPMXS6hi9HfFGviqqyQ4c-SylBTF4QuRNyXpPI", 5 | "data": { 6 | "userInfo": { 7 | "uid": 1, 8 | "username": "guest", 9 | "truename": "李大龙", 10 | "status": 1, 11 | "level": 0, 12 | "avatar_url": "/images/avatar.jpeg" 13 | }, 14 | "menu": [ 15 | { 16 | "path": "/", 17 | "redirect": "/home", 18 | "name": "Home", 19 | "component": "Layout", 20 | "meta": { 21 | "title": "首页", 22 | "layout": true, 23 | "icon": "House", 24 | "requiresAuth": true, 25 | "hidden": false, 26 | "keepAlive": true, 27 | "new": false 28 | }, 29 | "children": [ 30 | { 31 | "path": "/home", 32 | "name": "Home", 33 | "component": "Dashboard", 34 | "meta": { 35 | "title": "首页", 36 | "icon": "House", 37 | "layout": false, 38 | "requiresAuth": true, 39 | "hidden": false, 40 | "keepAlive": true, 41 | "new": false 42 | } 43 | } 44 | ] 45 | }, 46 | { 47 | "path": "/banner", 48 | "name": "Banner", 49 | "component": "Layout", 50 | "meta": { 51 | "title": "广告橱窗管理", 52 | "layout": false, 53 | "icon": "ScaleToOriginal", 54 | "requiresAuth": true, 55 | "hidden": false, 56 | "keepAlive": true, 57 | "new": false 58 | }, 59 | "children": [ 60 | { 61 | "path": "/banner/index", 62 | "name": "BannerIndex", 63 | "component": "@/views/banner/index/index.vue", 64 | "meta": { 65 | "title": "广告橱窗列表", 66 | "icon": "ScaleToOriginal", 67 | "layout": false, 68 | "requiresAuth": true, 69 | "hidden": false, 70 | "keepAlive": true, 71 | "new": false 72 | }, 73 | "actions": [ 74 | 1148, 75 | 1150, 76 | 1152, 77 | 1159, 78 | 1161 79 | ] 80 | } 81 | ] 82 | }, 83 | { 84 | "path": "/admin", 85 | "name": "Admin", 86 | "component": "Layout", 87 | "meta": { 88 | "title": "管理员管理", 89 | "layout": false, 90 | "icon": "User", 91 | "requiresAuth": true, 92 | "hidden": false, 93 | "keepAlive": true, 94 | "new": false 95 | }, 96 | "children": [ 97 | { 98 | "path": "/admin/user", 99 | "name": "AdminUser", 100 | "component": "@/views/admin/user/index.vue", 101 | "meta": { 102 | "title": "管理员列表", 103 | "icon": "User", 104 | "layout": false, 105 | "requiresAuth": true, 106 | "hidden": false, 107 | "keepAlive": true, 108 | "new": false 109 | }, 110 | "actions": [ 111 | 175, 112 | 178, 113 | 181, 114 | 1823, 115 | 1825 116 | ] 117 | } 118 | ] 119 | }, 120 | { 121 | "path": "/log", 122 | "name": "Log", 123 | "component": "Layout", 124 | "meta": { 125 | "title": "日志管理", 126 | "layout": false, 127 | "icon": "Document", 128 | "requiresAuth": true, 129 | "hidden": false, 130 | "keepAlive": true, 131 | "new": false 132 | }, 133 | "children": [ 134 | { 135 | "path": "/log/adminopr", 136 | "name": "LogAdminOpr", 137 | "component": "@/views/log/admin/operate.vue", 138 | "meta": { 139 | "title": "管理员操作日志", 140 | "icon": "Document", 141 | "layout": false, 142 | "requiresAuth": true, 143 | "hidden": false, 144 | "keepAlive": true, 145 | "new": false 146 | }, 147 | "actions": [ 148 | 146 149 | ] 150 | } 151 | ] 152 | } 153 | ] 154 | } 155 | } -------------------------------------------------------------------------------- /src/views/log/admin/operate.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 182 | 249 | -------------------------------------------------------------------------------- /mock/modules/logAdminopr.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "message": "操作成功!", 4 | "data": { 5 | "list": [ 6 | { 7 | "id": 1066, 8 | "uqid": 14108, 9 | "uid": 0, 10 | "func_code": 1161, 11 | "create_time": 1654941682, 12 | "link_id": 0, 13 | "username": "backend", 14 | "ip": "2886860801", 15 | "content": "广告橱窗列表-下架" 16 | }, 17 | { 18 | "id": 1065, 19 | "uqid": 14100, 20 | "uid": 0, 21 | "func_code": 1161, 22 | "create_time": 1654941320, 23 | "link_id": 0, 24 | "username": "backend", 25 | "ip": "2886860801", 26 | "content": "广告橱窗列表-下架" 27 | }, 28 | { 29 | "id": 1064, 30 | "uqid": 14089, 31 | "uid": 0, 32 | "func_code": 1159, 33 | "create_time": 1654940523, 34 | "link_id": 1810, 35 | "username": "backend", 36 | "ip": "2886860801", 37 | "content": "广告橱窗列表-上架[1810]" 38 | }, 39 | { 40 | "id": 1063, 41 | "uqid": 13897, 42 | "uid": 0, 43 | "func_code": 8808, 44 | "create_time": 1654926287, 45 | "link_id": 8821, 46 | "username": "backend", 47 | "ip": "2886860801", 48 | "content": "会员角色管理-分配权限[店铺管理员]" 49 | }, 50 | { 51 | "id": 1062, 52 | "uqid": 13894, 53 | "uid": 0, 54 | "func_code": 49, 55 | "create_time": 1654926225, 56 | "link_id": 13893, 57 | "username": "backend", 58 | "ip": "2886860801", 59 | "content": "菜单管理-新增[店铺中心]" 60 | }, 61 | { 62 | "id": 1061, 63 | "uqid": 13887, 64 | "uid": 0, 65 | "func_code": 394, 66 | "create_time": 1654925945, 67 | "link_id": 584, 68 | "username": "backend", 69 | "ip": "2886860801", 70 | "content": "会员列表-编辑[2-coderVision]" 71 | }, 72 | { 73 | "id": 1060, 74 | "uqid": 13885, 75 | "uid": 0, 76 | "func_code": 394, 77 | "create_time": 1654925898, 78 | "link_id": 3, 79 | "username": "backend", 80 | "ip": "2886860801", 81 | "content": "会员列表-编辑[1-guest]" 82 | }, 83 | { 84 | "id": 1059, 85 | "uqid": 13883, 86 | "uid": 0, 87 | "func_code": 394, 88 | "create_time": 1654925865, 89 | "link_id": 3, 90 | "username": "backend", 91 | "ip": "2886860801", 92 | "content": "会员列表-编辑[1-guest]" 93 | }, 94 | { 95 | "id": 1058, 96 | "uqid": 13845, 97 | "uid": 0, 98 | "func_code": 165, 99 | "create_time": 1654918320, 100 | "link_id": 13841, 101 | "username": "backend", 102 | "ip": "2886860801", 103 | "content": "全局ID管理-删除" 104 | }, 105 | { 106 | "id": 1057, 107 | "uqid": 13281, 108 | "uid": 0, 109 | "func_code": 8808, 110 | "create_time": 1654876125, 111 | "link_id": 8821, 112 | "username": "backend", 113 | "ip": "2886860801", 114 | "content": "会员角色管理-分配权限[店铺管理员]" 115 | }, 116 | { 117 | "id": 1056, 118 | "uqid": 13263, 119 | "uid": 0, 120 | "func_code": 49, 121 | "create_time": 1654873969, 122 | "link_id": 13262, 123 | "username": "backend", 124 | "ip": "2886860801", 125 | "content": "菜单管理-新增[物流订单列表]" 126 | }, 127 | { 128 | "id": 1055, 129 | "uqid": 13258, 130 | "uid": 0, 131 | "func_code": 51, 132 | "create_time": 1654873860, 133 | "link_id": 1425, 134 | "username": "backend", 135 | "ip": "2886860801", 136 | "content": "菜单管理-编辑[物流管理]" 137 | }, 138 | { 139 | "id": 1054, 140 | "uqid": 8842, 141 | "uid": 0, 142 | "func_code": 394, 143 | "create_time": 1654842977, 144 | "link_id": 3, 145 | "username": "backend", 146 | "ip": "2886860801", 147 | "content": "会员列表-编辑[1-guest]" 148 | }, 149 | { 150 | "id": 1053, 151 | "uqid": 8841, 152 | "uid": 0, 153 | "func_code": 394, 154 | "create_time": 1654842294, 155 | "link_id": 584, 156 | "username": "backend", 157 | "ip": "2886860801", 158 | "content": "会员列表-编辑[2-guest]" 159 | }, 160 | { 161 | "id": 1052, 162 | "uqid": 8840, 163 | "uid": 0, 164 | "func_code": 394, 165 | "create_time": 1654842287, 166 | "link_id": 584, 167 | "username": "backend", 168 | "ip": "2886860801", 169 | "content": "会员列表-编辑[2-guest]" 170 | }, 171 | { 172 | "id": 1051, 173 | "uqid": 8839, 174 | "uid": 0, 175 | "func_code": 394, 176 | "create_time": 1654842125, 177 | "link_id": 584, 178 | "username": "backend", 179 | "ip": "2886860801", 180 | "content": "会员列表-编辑[2-guest]" 181 | }, 182 | { 183 | "id": 1050, 184 | "uqid": 8838, 185 | "uid": 0, 186 | "func_code": 394, 187 | "create_time": 1654842117, 188 | "link_id": 3, 189 | "username": "backend", 190 | "ip": "2886860801", 191 | "content": "会员列表-编辑[1-guest]" 192 | }, 193 | { 194 | "id": 1049, 195 | "uqid": 8837, 196 | "uid": 0, 197 | "func_code": 394, 198 | "create_time": 1654841911, 199 | "link_id": 584, 200 | "username": "backend", 201 | "ip": "2886860801", 202 | "content": "会员列表-编辑[1-guest]" 203 | }, 204 | { 205 | "id": 1048, 206 | "uqid": 8836, 207 | "uid": 0, 208 | "func_code": 394, 209 | "create_time": 1654837903, 210 | "link_id": 3, 211 | "username": "backend", 212 | "ip": "2886860801", 213 | "content": "会员列表-编辑[1-guest]" 214 | }, 215 | { 216 | "id": 1047, 217 | "uqid": 8835, 218 | "uid": 0, 219 | "func_code": 8808, 220 | "create_time": 1654837764, 221 | "link_id": 8832, 222 | "username": "backend", 223 | "ip": "2886860801", 224 | "content": "会员角色管理-分配权限[展示员-2]" 225 | } 226 | ], 227 | "total": 924, 228 | "size": 20, 229 | "page": 47 230 | } 231 | } -------------------------------------------------------------------------------- /src/router/modules/routes.ts: -------------------------------------------------------------------------------- 1 | //========================对应的Route component Vue组件模板地址====================== 2 | import { componentRouter } from './components'; 3 | //========================对应的Route component Vue组件模板地址====================== 4 | 5 | //静态路由列表 6 | export const routesStatic:any = [ 7 | { 8 | path: '/',//路径地址 9 | redirect: '/home',//重定向地址 10 | component: componentRouter.Layout,//view组件 11 | meta: { 12 | title: '首页',//菜单标题名称 13 | icon: 'House',//菜单图标 14 | requiresAuth: true,//需认证才能显示该页面 15 | hidden: false,//该菜单是否在view的menu组件隐藏 16 | layout: true,//该菜单只作为一级菜单标题显示 17 | keepAlive:true,//该菜单对应的页面是否保存缓存,用于keep-alive组件设置 18 | new:false,//是否为新功能 19 | }, 20 | children: [ 21 | { 22 | path: 'home', 23 | name: 'Home', 24 | component: componentRouter.Home, 25 | meta: { 26 | title: '首页', 27 | icon: 'House', 28 | requiresAuth: true, 29 | hidden: false, 30 | keepAlive:true, 31 | new:false,//是否为新功能 32 | } 33 | } 34 | ] 35 | }, 36 | { 37 | path: '/form', 38 | redirect: '/form/index',//重定向地址 39 | name: 'Form', 40 | component: componentRouter.Layout, 41 | meta: { 42 | title: '表单组件', 43 | icon: 'Paperclip', 44 | requiresAuth: true, 45 | layout: true, 46 | hidden: false, 47 | keepAlive:true, 48 | new:false, 49 | }, 50 | children: [ 51 | { 52 | path: '/form/index', 53 | name: 'FormIndex', 54 | component: () => import('@/views/form/index.vue'), 55 | meta: { 56 | title: '表单组件', 57 | icon: 'Paperclip', 58 | requiresAuth: true, 59 | hidden: false, 60 | keepAlive:true, 61 | new:true, 62 | }, 63 | } 64 | ] 65 | }, 66 | { 67 | path: '/image', 68 | redirect: '/image/index',//重定向地址 69 | name: 'Image', 70 | component: componentRouter.Layout, 71 | meta: { 72 | title: '图片组件', 73 | icon: 'Paperclip', 74 | requiresAuth: true, 75 | layout: true, 76 | hidden: false, 77 | keepAlive:true, 78 | new:false, 79 | }, 80 | children: [ 81 | { 82 | path: '/image/index', 83 | name: 'ImageIndex', 84 | component: () => import('@/views/form/image.vue'), 85 | meta: { 86 | title: '图片组件', 87 | icon: 'Paperclip', 88 | requiresAuth: true, 89 | hidden: false, 90 | keepAlive:true, 91 | new:true, 92 | }, 93 | } 94 | ] 95 | }, 96 | { 97 | path: '/other', 98 | redirect: '/other/index',//重定向地址 99 | name: 'Other', 100 | component: componentRouter.Layout, 101 | meta: { 102 | title: '其他组件', 103 | icon: 'Paperclip', 104 | requiresAuth: true, 105 | layout: true, 106 | hidden: false, 107 | keepAlive:true, 108 | new:false, 109 | }, 110 | children: [ 111 | { 112 | path: '/other/index', 113 | name: 'OtherIndex', 114 | component: () => import('@/views/form/other.vue'), 115 | meta: { 116 | title: '其他组件', 117 | icon: 'Paperclip', 118 | requiresAuth: true, 119 | hidden: false, 120 | keepAlive:true, 121 | new:true, 122 | }, 123 | } 124 | ] 125 | }, 126 | { 127 | path: '/banner', 128 | name: 'Banner', 129 | component: componentRouter.Layout, 130 | meta: { 131 | title: '广告橱窗', 132 | icon: 'ScaleToOriginal', 133 | requiresAuth: true, 134 | hidden: false, 135 | keepAlive:true, 136 | new:false, 137 | }, 138 | children: [ 139 | { 140 | path: '/banner/index', 141 | name: 'BannerIndex', 142 | component: componentRouter.BannerIndex, 143 | meta: { 144 | title: '广告橱窗列表', 145 | icon: 'ScaleToOriginal', 146 | requiresAuth: true, 147 | hidden: false, 148 | keepAlive:true, 149 | new:true, 150 | }, 151 | } 152 | ] 153 | }, 154 | { 155 | path: '/admin', 156 | name: 'Admin', 157 | component: componentRouter.Layout, 158 | meta: { 159 | title: '管理员管理', 160 | icon: 'User', 161 | requiresAuth: true, 162 | hidden: false, 163 | keepAlive:true, 164 | new:false, 165 | }, 166 | children: [ 167 | { 168 | path: '/admin/user', 169 | name: 'AdminUser', 170 | component: componentRouter.AdminUser, 171 | meta: { 172 | title: '管理员列表', 173 | icon: 'User', 174 | requiresAuth: true, 175 | hidden: false, 176 | keepAlive:true, 177 | new:false, 178 | }, 179 | } 180 | ] 181 | }, 182 | { 183 | path: '/log', 184 | name: 'Log', 185 | component: componentRouter.Layout, 186 | meta: { 187 | title: '日志管理', 188 | icon: 'Document', 189 | requiresAuth: true, 190 | hidden: false, 191 | keepAlive:true, 192 | new:false, 193 | }, 194 | children: [ 195 | { 196 | path: '/log/adminopr', 197 | name: 'LogAdminOpr', 198 | component: componentRouter.LogAdminOpr, 199 | meta: { 200 | title: '管理员操作日志', 201 | icon: 'Document', 202 | requiresAuth: true, 203 | hidden: false, 204 | keepAlive:true, 205 | new:false, 206 | } 207 | } 208 | ] 209 | }, 210 | { 211 | path: '/others', 212 | name: 'Others', 213 | component: componentRouter.Layout, 214 | meta: { 215 | title: '其他页面', 216 | icon: 'InfoFilled', 217 | requiresAuth: true, 218 | hidden: false, 219 | keepAlive:true, 220 | new:false, 221 | }, 222 | children: [ 223 | { 224 | path: '/404', 225 | name: 'NotFound', 226 | component: componentRouter.NotFound, 227 | meta: { 228 | title: '404', 229 | icon: 'InfoFilled', 230 | requiresAuth: true, 231 | hidden: false, 232 | keepAlive:true, 233 | new:false, 234 | } 235 | }, 236 | { 237 | path: '/README.md', 238 | name: 'README.cn', 239 | component: componentRouter.Home, 240 | meta: { 241 | title: 'README-中文', 242 | icon: 'InfoFilled', 243 | requiresAuth: true, 244 | hidden: false, 245 | keepAlive:true, 246 | new:false, 247 | } 248 | }, 249 | { 250 | path: '/README.en.md', 251 | name: 'README.en', 252 | component: componentRouter.ReadMeEN, 253 | meta: { 254 | title: 'README-En', 255 | icon: 'InfoFilled', 256 | requiresAuth: true, 257 | hidden: false, 258 | keepAlive:true, 259 | new:false, 260 | } 261 | } 262 | ] 263 | } 264 | ] 265 | 266 | //静态白名单路由列表 267 | export const routesWhite = [ 268 | { 269 | path: '/404', 270 | name: '404', 271 | component: componentRouter.NotFound, 272 | meta: { 273 | title: '404-NotFound', 274 | requiresAuth: false, 275 | hidden: true, 276 | layout: true, 277 | } 278 | }, 279 | { 280 | path: '/login', 281 | name: 'Login', 282 | component: componentRouter.LoginLayout, 283 | meta: { 284 | title: '登录', 285 | layout: true, 286 | requiresAuth: false, 287 | hidden: true, 288 | } 289 | }, 290 | { 291 | path: '/register', 292 | name: 'Register', 293 | component: componentRouter.LoginLayout, 294 | meta: { 295 | title: '注册', 296 | layout: true, 297 | requiresAuth: false, 298 | hidden: true, 299 | } 300 | } 301 | ] 302 | 303 | //获取动态路由列表 304 | export const routes = routesStatic.concat(routesWhite) 305 | 306 | //获取路由url path地址白名单列表 路由白名单 - 不重定向白名单 ['/404','/login', '/register'] 307 | const whiteList = () => { 308 | let list:any = [] 309 | for (let item of routes) { 310 | //也可以转换为大写或小写再保存 311 | list.push(item.path) 312 | } 313 | return list 314 | } 315 | 316 | //路由在白名单里面 317 | export const existWhite = (path: string) => { 318 | if (whiteList().indexOf(path) !== -1) { 319 | return true; 320 | } else return false; 321 | } 322 | -------------------------------------------------------------------------------- /src/views/auth/login.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 184 | 185 | -------------------------------------------------------------------------------- /src/views/banner/index/index.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 216 | 217 | 329 | -------------------------------------------------------------------------------- /src/service/http/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装axios网络请求 3 | */ 4 | import axios from "axios" 5 | import EventEmitter from 'events' 6 | import { G } from '@/config' //引入常量和配置信息 7 | import { ElLoading } from 'element-plus' 8 | // 导入模块 9 | import { cache } from '@/utils'; 10 | import { responseCode } from './code'; 11 | 12 | //定义一个异常信息接口,返回的数据 13 | interface IException { 14 | code : number, 15 | message: string 16 | } 17 | 18 | const IExceptionData: IException = { 19 | code: -1, 20 | message: '#网络异常-Net Exception#' 21 | } 22 | 23 | let loading:any = null; 24 | class Request extends EventEmitter { 25 | constructor(){ 26 | super(); 27 | this.interceptors() 28 | } 29 | 30 | /** 31 | * 数据加载动画效果 32 | */ 33 | startLoading(value:string = '') { 34 | if (value) { 35 | loading = ElLoading.service({ 36 | target: value,//局部刷新动画,填写class .notice-tabs 37 | lock: false, 38 | fullscreen: false, 39 | text: '加载中...', 40 | background: 'rgba(0,0,0,0.5)' 41 | }) 42 | } else { 43 | loading = ElLoading.service({ 44 | lock: false, 45 | text: '拼命加载中...', 46 | background: 'rgba(0,0,0,0.7)' 47 | }) 48 | } 49 | return loading 50 | } 51 | /** 52 | * 数据停止加载动画效果 53 | */ 54 | endLoading() { 55 | if (loading) { 56 | loading.close() 57 | } 58 | } 59 | 60 | /** 61 | * 拦截器处理 62 | */ 63 | interceptors() { 64 | // 添加请求拦截器 65 | axios.interceptors.request.use( 66 | (config) => { 67 | let paramsData = config.params || {} 68 | if (!paramsData.hasOwnProperty('getBox')) { 69 | //数据加载动画效果(全屏loading) 70 | this.startLoading() 71 | } else { 72 | if (config.params.getBox) { 73 | //数据加载动画效果(局部loading) 74 | this.startLoading(config.params.getBox) 75 | //剔除该key 76 | delete config.params.getBox 77 | } 78 | } 79 | 80 | //如果未定义config,则默认为空 81 | if (!config) { 82 | config = {}; 83 | } 84 | if (!config.headers) { 85 | config.headers = {}; 86 | } 87 | 88 | const token = cache.getLocalStorage(G.AUTHORIZATION_TOKEN) 89 | let Authorization:string = '' 90 | if (token.length >= 20) { 91 | Authorization = 'Bearer ' + token; 92 | } 93 | // 为请求头对象,添加 Token 验证的 Authorization 字段 94 | config.headers.Authorization = Authorization; 95 | // 发送请求前的处理 96 | return config 97 | }, 98 | error => { 99 | this.error(error,'request') 100 | } 101 | ); 102 | // 添加响应拦截器 103 | axios.interceptors.response.use( 104 | // 请求响应的处理 105 | response => { 106 | setTimeout(() => { 107 | //数据结束加载动画效果 108 | this.endLoading(); 109 | }, 60); 110 | if (response.hasOwnProperty('status')) { 111 | const status = response.status; 112 | //code 为正常code范围值,响应成功 113 | if ((status >= 200 && status < 300) || status === 304) { 114 | this.code(response.data) 115 | return Promise.resolve(response.data); 116 | } else { 117 | // 响应错误逻辑处理 5xx 4xx 等等 118 | this.error(response,'response#1') 119 | } 120 | } else { 121 | this.error(response,'response#2') 122 | } 123 | }, 124 | // 响应错误的处理 125 | error => { 126 | // 状态码 127 | this.error(error,'response#3') 128 | } 129 | ); 130 | } 131 | get(url : string,params : any) { 132 | return axios({ 133 | method: 'get', 134 | url, 135 | params, 136 | }); 137 | } 138 | //在某个div里显示loading加载 139 | getBox(url : string,params : any,box : any = {}) { 140 | params['getBox'] = box['getBox'] || '' 141 | return axios({ 142 | method: 'get', 143 | url, 144 | params 145 | }); 146 | } 147 | 148 | /** 149 | * upload 请求 150 | * @param { String } url Url 151 | * @param { Object } params 参数/数据 152 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 153 | */ 154 | upload(url : string,data : any = '') { 155 | return axios({ 156 | method: 'post', 157 | url, 158 | headers: { 'Content-Type': 'multipart/form-data' }, 159 | data 160 | }); 161 | } 162 | 163 | /** 164 | * Axios Post 请求 165 | * @param { String } url Url 166 | * @param { Object } params 参数/数据 167 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 168 | */ 169 | post(url : string,params : any = '',options : any = null) { 170 | let data = this.reqParams(params,options) 171 | return axios({ 172 | method: 'post', 173 | url, 174 | data 175 | }); 176 | } 177 | 178 | /** 179 | * Axios Delete 请求 180 | * @param { String } url Url 181 | * @param { Object } params 参数/数据 182 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 183 | */ 184 | delete(url : string,params : any = '',options : any = null) { 185 | let data = this.reqParams(params,options) 186 | return axios({ 187 | method: 'delete', 188 | url, 189 | data 190 | }); 191 | } 192 | 193 | /** 194 | * Axios Put 请求 195 | * @param { String } url Url 196 | * @param { Object } params 参数/数据 197 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 198 | */ 199 | put(url : string,params : any = '',options: any = null) { 200 | let data = this.reqParams(params,options) 201 | return axios({ 202 | method: 'put', 203 | url, 204 | data 205 | }); 206 | } 207 | 208 | /** 209 | * Axios Patch 请求 210 | * @param { String } url Url 211 | * @param { Object } params 参数/数据 212 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 213 | */ 214 | patch(url : string,params : any = '',options : any = null) { 215 | let data = this.reqParams(params,options) 216 | return axios({ 217 | method: 'patch', 218 | url, 219 | data 220 | }); 221 | } 222 | 223 | /** 224 | * 处理请求参数 225 | * @param { Object } params 参数/数据 226 | * @param { Object } options 设置header 相关属性、timeout、以及其他http request参数 227 | */ 228 | reqParams(params : any, options : any = null) { 229 | if (typeof params === 'object') { 230 | if (Object.keys(params).length < 1) { 231 | params = {} 232 | } 233 | //过滤 {} null [] '' 等长度少于4的 234 | if (JSON.stringify(options).length > 4) { 235 | Object.assign(params, options); 236 | } 237 | return params 238 | } 239 | return {} 240 | } 241 | 242 | /** 243 | * 统一处理code返回值的函数(如果需要对code某些返回值进行拦截处理) 244 | * @param {*} res 245 | * @param {*} resolve 246 | * @param {*} reject 247 | */ 248 | code(res : any) { 249 | //统一处理一些特殊的code值,比如权限不足,token过期,接口已关闭,维护中等相关的状态 250 | if (res.hasOwnProperty('code')) { 251 | //如果返回值的code中存在responseCode的内容,则进行处理 252 | if (responseCode.includes(res.code)) { 253 | let message:any 254 | if (res.hasOwnProperty('message')) { 255 | message = res.message 256 | } 257 | } 258 | } 259 | } 260 | 261 | /** 262 | * 成功回调函数 263 | * @param {*} res 264 | * @param {*} resolve 265 | * @param {*} reject 266 | */ 267 | success(res : any, resolve : any,reject : any) { 268 | if (res.hasOwnProperty('data')) { 269 | //统一处理一些特俗的code值,比如权限不足,token过期,接口已关闭,维护中等相关的状态 270 | if (res.data) { 271 | resolve(res) 272 | } else { 273 | //失败回调 274 | this.fail(res,reject) 275 | } 276 | } else { 277 | resolve(res) 278 | } 279 | } 280 | 281 | 282 | /** 283 | * 失败回调函数 284 | * @param {*} res 数据 285 | * @param {*} reject 286 | */ 287 | fail(res : any, reject : any) { 288 | console.log(res,'+++===---:::Axios Failed:::---===+++') 289 | //数据结束加载动画效果 290 | this.endLoading(); 291 | let errExp:IException = { 292 | code: IExceptionData.code, 293 | message:IExceptionData.message + '#fail' 294 | } 295 | return Promise.reject(errExp) 296 | } 297 | 298 | /** 299 | * 失败回调函数 300 | * @param {*} res 数据 301 | * @param {*} reject 302 | */ 303 | catch(res : any) { 304 | console.log(res,'+++===---:::Axios Catch Exception:::---===+++') 305 | //数据结束加载动画效果 306 | this.endLoading(); 307 | let errExp:IException = { 308 | code: IExceptionData.code, 309 | message:IExceptionData.message + '#catch' 310 | } 311 | return Promise.reject(errExp) 312 | } 313 | 314 | /** 315 | * 异常错误回调函数 316 | * @param {*} error 异常错误数据 317 | * @param {*} type 是request还是response类型 318 | */ 319 | error(error : any,type:string) { 320 | console.log(error,'+++===---:::Axios ' + type + ' Error:::---===+++') 321 | //数据结束加载动画效果 322 | this.endLoading(); 323 | let errExp:IException = { 324 | code: IExceptionData.code, 325 | message:IExceptionData.message + '#' + type 326 | } 327 | return Promise.reject(errExp) 328 | } 329 | } 330 | 331 | const http = new Request(); 332 | 333 | export default http; 334 | -------------------------------------------------------------------------------- /src/views/admin/user/index.vue: -------------------------------------------------------------------------------- 1 | 106 | 107 | 248 | 249 | 316 | -------------------------------------------------------------------------------- /src/views/form/index.vue: -------------------------------------------------------------------------------- 1 | 155 | 156 | 310 | 311 | 328 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README.en.md) 2 | 3 |

TodoAdmin-vue

4 | 5 |

6 | 7 | logo 8 | 9 |

10 |

一阳动、万物生!愿美好不期而遇、愿幸福如约而至、愿疫情早日消散、一起奔赴更好的未来、不辜负你我所有的努力。

11 | 12 |

13 | 14 | TS 15 | 16 | 17 | npm 18 | 19 | 20 | license-mit 21 | 22 |

23 | 24 | 25 | > TodoAdmin-Vue版:是一个集成管理后台端模板解决方案,项目采用TS脚本语言,基于Vue 3.2+/Vue-cli 5.0+/Vue-router 4.0+、Axios0.2.7+、Element-plus 2.2+、Pinia 2.0+、ECharts 5.3+等搭建,项目是以Composition api风格编写,采用远程API接口和本地Mock双接口模式加载数据。 26 | 27 | 28 | 29 | 32 | 35 | 36 |
30 | 31 | 33 | 34 |
37 | 38 | 39 | ## 体验Demo地址 40 | 41 | - [Todoadmin-pro Pro专业版演示地址(vue3.2+ 支持 PC、平板、手机)](https://pro.todoadmin.com) 42 | - [Todoadmin-base 基本版演示地址(vue3.2+ 支持 PC、平板、手机)](https://base.todoadmin.com) 43 |

用户名:guest

密码:123456

44 | 45 | 46 | 47 | 48 | ## 克隆vue-admin-chart 项目 [点击访问Github仓库](https://github.com/todoadmin/vue-admin-chart) 49 | 50 | ```bash 51 | # 克隆项目 52 | git clone -b https://github.com/todoadmin/vue-admin-chart.git 53 | # 安装依赖 54 | npm i or yarn install 55 | # 本地开发 启动项目 56 | npm run serve or yarn serve 57 | ``` 58 | 59 | 60 | ## 克隆vue-admin-chart 项目 [点击访问Gitee仓库](https://gitee.com/todoadmin/vue-admin-chart) 61 | 62 | ```bash 63 | # 克隆项目 64 | git clone -b https://gitee.com/todoadmin/vue-admin-chart.git 65 | # 安装依赖 66 | npm i or yarn install 67 | # 本地开发 启动项目 68 | npm run serve or yarn serve 69 | ``` 70 | 71 | 72 |

以下为Pro版本的相关介绍

73 | 74 | 75 | ## 项目生态插件【依赖】列表 76 | 77 | | 标题名称 | 版本 | 描述 | 78 | | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | 79 | | [vue] | [![vue-ico]][vue] | 构建用户界面的渐进式框架 | 80 | | [vue-router] | [![vue-router-ico]][vue-router] | 单页应用程序路由 | 81 | | [vue-cli] | [![vue-cli-ico]][vue-cli] | 项目脚手架 | 82 | | [axios] | [![axios-ico]][axios] | 基于promise的网络请求库 | 83 | | [element-plus] | [![element-plus-ico]][element-plus] | 基于Vue3的组件库 | 84 | | [element-plus-icons] | [![element-plus-icons-ico]][element-plus-icons] | 基于Vue3的组件图标库 | 85 | | [vue-quill] | [![vue-quill-ico]][vue-quill] | 可视化在线文本编辑器 | 86 | | [quill-image-resize] | [![quill-image-resize-ico]][quill-image-resize] | 文本编辑器图片重置尺寸 | 87 | | [vue-cropper] | [![vue-cropper-ico]][vue-cropper] | 基于Vue3的图片裁剪 | 88 | | [pinia] | [![pinia-ico]][pinia] | 状态管理 | 89 | | [vue-i18n-next] | [![vue-i18n-next-ico]][vue-i18n-next] | 多国语言文字切换 | 90 | | [js-md5] | [![js-md5-ico]][js-md5] | MD5加密 | 91 | | [nprogress] | [![nprogress-ico]][nprogress] | 进度条加载 | 92 | | [echarts] | [![echarts-ico]][echarts] | 多功能图表 | 93 | 94 | 95 | 96 | ## 项目安装(依赖安装) 97 | ``` 98 | npm install or yarn install 99 | ``` 100 | 101 | ### 运行开发环境 102 | ``` 103 | npm run serve or yarn serve 104 | ``` 105 | 106 | ### 生产环境打包 107 | ``` 108 | npm run build or yarn build 109 | ``` 110 | 111 | ### 修复文件 112 | ``` 113 | npm run lint 114 | ``` 115 | 116 | ### 配置文件 117 | ``` 118 | #config目录下 119 | /src/config 120 | 121 | #项目主体配置import模块 122 | /src/config/index.ts 123 | 124 | #项目启动环境配置引入模块 125 | /src/config/config.ts 126 | 127 | #项目启动环境(生产环境/Dev环境)基本配置引入模块 128 | /src/config/module/base.ts 129 | 130 | #项目启动环境(生产环境/Dev环境)CDN配置引入模块 131 | /src/config/module/cdn.ts 132 | 133 | #项目主体全局常量import模块 134 | /src/config/module/global.ts 135 | 136 | #项目主体HTTP/HTTPS Restful请求接口地址常量import模块 137 | /src/config/module/HUrl.ts 138 | ``` 139 | 140 | 141 | [vue]: https://github.com/vuejs/vue 142 | [vue-ico]:https://img.shields.io/badge/Vue-v3.2.36-brightgreen 143 | 144 | [vue-router]: https://github.com/vuejs/vue-router 145 | [vue-router-ico]:https://img.shields.io/badge/Vue--router-v4.0.15-brightgreen 146 | 147 | [vue-cli]: https://github.com/vuejs/vue-cli 148 | [vue-cli-ico]:https://img.shields.io/badge/Vue--cli-v5.0.1-brightgreen 149 | 150 | [axios]: https://github.com/axios/axios 151 | [axios-ico]:https://img.shields.io/badge/axios-v0.2.7-brightgreen 152 | 153 | [element-plus]: https://github.com/element-plus/element-plus 154 | [element-plus-ico]:https://img.shields.io/badge/element--plus-v2.2.2-brightgreen 155 | 156 | [element-plus-icons]: https://github.com/element-plus/element-plus-icons 157 | [element-plus-icons-ico]:https://img.shields.io/badge/element--plus--icons-2.x-brightgreen 158 | 159 | [vue-quill]: https://github.com/vueup/vue-quill 160 | [vue-quill-ico]:https://img.shields.io/badge/Vue--quill-v1.0.0--beta.8-brightgreen 161 | 162 | [quill-image-resize]: https://github.com/kensnyder/quill-image-resize-module 163 | [quill-image-resize-ico]:https://img.shields.io/badge/Quill--image--resize-v3.0.0-brightgreen 164 | 165 | [vue-cropper]: https://github.com/xyxiao001/vue-cropper 166 | [vue-cropper-ico]:https://img.shields.io/badge/vue--cropper-v1.0.3-brightgreen 167 | 168 | [pinia]: https://github.com/vuejs/pinia 169 | [pinia-ico]:https://img.shields.io/badge/Pinia-v2.0.14-brightgreen 170 | 171 | [vue-jsonp]: https://github.com/LancerComet/vue-jsonp 172 | [vue-jsonp-ico]:https://img.shields.io/badge/Vue--jsonp-v2.0.0-brightgreen 173 | 174 | [vue-i18n-next]: https://github.com/intlify/vue-i18n-next/tree/master/packages/vue-i18n 175 | [vue-i18n-next-ico]:https://img.shields.io/badge/Vue--i18n-v9.1.10-brightgreen 176 | 177 | [js-md5]: https://github.com/emn178/js-md5 178 | [js-md5-ico]:https://img.shields.io/badge/js--md5-v0.7.3-brightgreen 179 | 180 | [nprogress]: https://github.com/rstacruz/nprogress 181 | [nprogress-ico]:https://img.shields.io/badge/nprogress-v0.2.0-brightgreen 182 | 183 | [echarts]: http://echarts.apache.org 184 | [echarts-ico]:https://img.shields.io/badge/echarts-v5.3.2-brightgreen 185 | 186 | --- 187 | 188 | ## 前后端功能简介 189 | 前端 190 | - CDN 分布式引入JS/样式/图片/Json/地图数据 191 | - 独家采用API远程实时数据接口和Mock本地数据双接口,可自由切换API或Mock 192 | - 120+高质量组件页面 193 | - 管理后台采用实时接口数据传输 194 | - 实时生成可视化数据大屏动态图表 195 | - 实时数据采用Websocket交互 196 | - 采用Composition API模式 197 | - 采用JWT 认证 198 | - 实时监控系统&服务器资源使用 199 | - 所有开源版本均可免费商用 200 | - 跨平台 PC、手机端、平板等多端兼容 201 | - 动态路由菜单认证和精确到用户的权限路由渲染 202 | - 支持MarkDown(md)文件加载成Vue组件页面 203 | - 支持mock本地模拟数据和远程模拟数据 204 | - 支持按钮功能级别的权限控制 205 | - 支持会员用户和管理员用户的角色、权限等分配 206 | - 支持多种主题切换以及自定义添加主题样式 207 | - 支持多国语言文字切换 208 | - 支持Pinia的状态管理模式 209 | - 支持自定义Vue指令 210 | - 支持对接第三方物流平台 211 | - 支持绑定第三方账号功能 212 | - 支持日志追溯(用户操作和管理员操作) 213 | 214 | 后端 215 | - 接口语言版本:Go (1.7+) 216 | - 接口语言版本:PHP (8.0.2 +)/Swoole (4.8+) 217 | - 接口语言版本:SpringBoot (2.2+) 218 | - 可支持多种开源关系数据库切换:MySQL、MariaDB、PostgreSQL、openGauss、TiDB 219 | - 可支持多种内存数据库切换:Redis、Memcached 220 | - 可支持关系型数据库的集群 221 | - 可支持内存型数据库的集群 222 | - 可支持静态文件(图片、视频、文档等)云存储和CDN分发 223 | 224 | 225 | ## Demo地址&仓库地址 226 | 227 | - [Todoadmin-pro Pro专业版演示地址(vue3.2+ 支持 PC、平板、手机)](https://pro.todoadmin.com) 228 | - [Todoadmin-base 基本版演示地址(vue3.2+ 支持 PC、平板、手机)](https://base.todoadmin.com) 229 | - [Github 地址](https://github.com/todoadmin/vue-admin-chart) 230 | - [Gitee 码云地址](https://gitee.com/todoadmin/vue-admin-chart) 231 |

用户名:guest

密码:123456

232 | 233 | ## 打赏&联系 234 | 235 | - 请喝杯茶呗,打赏后联系 QQ 308407381 236 | 237 | 238 | 241 | 244 | 245 |
239 | 240 | 242 | 243 |
246 | 247 | 248 | ## 优势&注意事项 249 | 250 | ``` 251 | 对比其他开源Admin后台管理框架有如下优势: 252 | 1. 独家采用API远程实时数据接口和Mock本地数据双接口调试(可自由切换数据接口模式) 253 | 2. 支持前后端路由菜单权限和功能按钮权限控制 254 | 3. 采用实时接口传输数据,让你事半功倍 255 | 4. 偏好数据、主题切换、多国语言切换等配置 256 | 5. 支持原生css和scss 自动排序,eslint 自动修复 257 | 6. axios 二次封装,支持多种模式和参数方式 258 | 7. websocket 封装,支持实时数据传输方式 259 | 8. 支持MD5/RSA加密登录 260 | 9. 支持https数据加密传输 261 | 10. 使用CDN分发项目样式css和js、图片,让速度飞起来 262 | 11. 支持全屏操作 263 | 12. 支持MarkDown(md)文件加载成Vue组件页面 264 | 265 | 使用注意事项: 266 | 1. 项目默认使用Chrome浏览器,Vue Devtools 插件调试 267 | 2. 项目默认使用VSCode + Eslint校验规范,需要配置vscode编辑器 268 | 3. 项目也可以使用Goland或IntelliJ IDEA + 相关插件开发 269 | 4. 项目使用MIT开源协议,请保留MIT开源协议即可免费商用 270 | 271 | ``` 272 | 273 | 274 | ## 后台效果图预览 275 | 276 | 以下是截取的是 pro 版的效果图展示: 277 | 278 | 279 | 280 | 283 | 286 | 287 | 288 | 291 | 294 | 295 | 296 | 299 | 302 | 303 | 304 | 307 | 310 | 311 | 312 | 315 | 318 | 319 | 320 | 323 | 326 | 327 | 328 | 331 | 334 | 335 | 336 | 339 | 342 | 343 |
281 | 282 | 284 | 285 |
289 | 290 | 292 | 293 |
297 | 298 | 300 | 301 |
305 | 306 | 308 | 309 |
313 | 314 | 316 | 317 |
321 | 322 | 324 | 325 |
329 | 330 | 332 | 333 |
337 | 338 | 340 | 341 |
344 | 345 | 346 | 347 | ## 提问 348 | 349 |

如果你有任何疑问可以提出,作者会快速处理

350 | 351 | ## 问题提交 352 | 353 |

如果项目存在任何问题或者Bug,可以提交Issue

354 | 355 | 356 | 357 | ## 浏览器&移动端浏览器 358 | 359 | 主流浏览器和IE 10+. 360 | 361 | | [IE / Edge](https://godban.github.io/browsers-support-badges/) IE/Edge | [Firefox](https://godban.github.io/browsers-support-badges/) Firefox | [Chrome](https://godban.github.io/browsers-support-badges/) Chrome | [Safari](https://godban.github.io/browsers-support-badges/) Safari | 362 | | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | 363 | | IE10/IE11/Edge | last 2 versions | last 2 versions | last 2 versions | 364 | 365 | 366 | ## 贡献 367 | 368 | 多谢每一位支持本项目的人士! 369 | 370 | 371 | 372 | ## 鸣谢 373 | 374 | | 项目(排名不分先后) | 375 | | ---------------------------------------------------------------- | 376 | | [vue](https://github.com/vuejs/vue) | 377 | | [element-plus](https://github.com/element-plus/element-plus) | 378 | | [pinia](https://github.com/vuejs/pinia) | 379 | | [vue-i18n-next](https://github.com/intlify/vue-i18n-next/tree/master/packages/vue-i18n) | 380 | | [axios](https://github.com/axios/axios) | 381 | | [echarts](http://echarts.apache.org) | 382 | | [nprogress](https://github.com/rstacruz/nprogress) | 383 | 384 | 385 | ## 商用注意事项 386 | 387 | 此项目可免费用于商业用途,请遵守 [MIT](https://opensource.org/licenses/MIT) 协议并保留作者技术支持声明。 388 | 389 | Copyright (c) 2022-present, Todoadmin.com 390 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | [简体中文](./README.md) | English 2 | 3 |

TodoAdmin-vue

4 | 5 |

6 | 7 | logo 8 | 9 |

10 |

Yang Qi rising, all things are born! May beauty come by chance, happiness come as promised, and the epidemic disappear as soon as possible. Let's go to a better future together and live up to all our efforts.

11 | 12 |

13 | 14 | TS 15 | 16 | 17 | npm 18 | 19 | 20 | license-mit 21 | 22 |

23 | 24 | 25 | > TodoAdmin-Vue:It is an integrated management back-end template solution. The project uses TS script language and is based onVue 3.2+/Vue-cli 5.0+/Vue-router 4.0+、Axios0.2.7+、Element-plus 2.2+、Pinia 2.0+、ECharts 5.3+,The project is composition API style,use remote API & mock dual interface mode to load data. 26 | 27 | 28 | 29 | 32 | 35 | 36 |
30 | 31 | 33 | 34 |
37 | 38 | 39 | ## Demo 40 | 41 | - [Todoadmin-pro Pro demo](https://pro.todoadmin.com) 42 | - [Todoadmin-base Basic demo](https://base.todoadmin.com) 43 |

Username:guest

Password:123456

44 | 45 | 46 | 47 | ## Clone project [Click Github repository](https://github.com/todoadmin/vue-admin-chart) 48 | 49 | ```bash 50 | # Clone 51 | git clone -b https://github.com/todoadmin/vue-admin-chart.git 52 | # install 53 | npm i or yarn install 54 | # run project 55 | npm run serve or yarn serve 56 | ``` 57 | 58 | 59 | ## Clone project [Click Gitee repository](https://gitee.com/todoadmin/vue-admin-chart) 60 | 61 | ```bash 62 | # Clone 63 | git clone -b https://gitee.com/todoadmin/vue-admin-chart.git 64 | # install 65 | npm i or yarn install 66 | # run project 67 | npm run serve or yarn serve 68 | ``` 69 | 70 | 71 |

The following is a introduction of the pro version

72 | 73 | 74 | ## Ecosystem 75 | 76 | | Project | Status | Description | 77 | | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- | 78 | | [vue] | [![vue-ico]][vue] | Progressive JS Framework | 79 | | [vue-router] | [![vue-router-ico]][vue-router] | Single-page application routing | 80 | | [vue-cli] | [![vue-cli-ico]][vue-cli] | Project scaffolding | 81 | | [axios] | [![axios-ico]][axios] | Promise network request Library | 82 | | [element-plus] | [![element-plus-ico]][element-plus] | Vue3 component library | 83 | | [element-plus-icons] | [![element-plus-icons-ico]][element-plus-icons] | Vue3 component Icon library | 84 | | [vue-quill] | [![vue-quill-ico]][vue-quill] | Visual online text editor | 85 | | [quill-image-resize] | [![quill-image-resize-ico]][quill-image-resize] | online text resize image | 86 | | [vue-cropper] | [![vue-cropper-ico]][vue-cropper] | vue image cropper | 87 | | [pinia] | [![pinia-ico]][pinia] | state management | 88 | | [vue-i18n-next] | [![vue-i18n-next-ico]][vue-i18n-next] | Multi language | 89 | | [js-md5] | [![js-md5-ico]][js-md5] | MD5 encryption | 90 | | [nprogress] | [![nprogress-ico]][nprogress] | Progress bar loading | 91 | | [echarts] | [![echarts-ico]][echarts] | echarts | 92 | 93 | 94 | 95 | ## Install(dependencies) 96 | ``` 97 | npm install or yarn install 98 | ``` 99 | 100 | ### Run dev 101 | ``` 102 | npm run serve or yarn serve 103 | ``` 104 | 105 | ### Build production 106 | ``` 107 | npm run build or yarn build 108 | ``` 109 | 110 | ### ESLint 111 | ``` 112 | npm run lint 113 | ``` 114 | 115 | ### Config 116 | ``` 117 | #config dir 118 | /src/config 119 | 120 | #project main import module 121 | /src/config/index.ts 122 | 123 | #project run env require module 124 | /src/config/config.ts 125 | 126 | #project run env(production/Dev)base config require module 127 | /src/config/module/base.ts 128 | 129 | #project run env(production/Dev)CDN config require module 130 | /src/config/module/cdn.ts 131 | 132 | #project main global constant import module 133 | /src/config/module/global.ts 134 | 135 | #project HTTP/HTTPS Restful request constant import module 136 | /src/config/module/HUrl.ts 137 | ``` 138 | 139 | 140 | [vue]: https://github.com/vuejs/vue 141 | [vue-ico]:https://img.shields.io/badge/Vue-v3.2.36-brightgreen 142 | 143 | [vue-router]: https://github.com/vuejs/vue-router 144 | [vue-router-ico]:https://img.shields.io/badge/Vue--router-v4.0.15-brightgreen 145 | 146 | [vue-cli]: https://github.com/vuejs/vue-cli 147 | [vue-cli-ico]:https://img.shields.io/badge/Vue--cli-v5.0.1-brightgreen 148 | 149 | [axios]: https://github.com/axios/axios 150 | [axios-ico]:https://img.shields.io/badge/axios-v0.2.7-brightgreen 151 | 152 | [element-plus]: https://github.com/element-plus/element-plus 153 | [element-plus-ico]:https://img.shields.io/badge/element--plus-v2.2.2-brightgreen 154 | 155 | [element-plus-icons]: https://github.com/element-plus/element-plus-icons 156 | [element-plus-icons-ico]:https://img.shields.io/badge/element--plus--icons-2.x-brightgreen 157 | 158 | [vue-quill]: https://github.com/vueup/vue-quill 159 | [vue-quill-ico]:https://img.shields.io/badge/Vue--quill-v1.0.0--beta.8-brightgreen 160 | 161 | [quill-image-resize]: https://github.com/kensnyder/quill-image-resize-module 162 | [quill-image-resize-ico]:https://img.shields.io/badge/Quill--image--resize-v3.0.0-brightgreen 163 | 164 | [vue-cropper]: https://github.com/xyxiao001/vue-cropper 165 | [vue-cropper-ico]:https://img.shields.io/badge/vue--cropper-v1.0.3-brightgreen 166 | 167 | [pinia]: https://github.com/vuejs/pinia 168 | [pinia-ico]:https://img.shields.io/badge/Pinia-v2.0.14-brightgreen 169 | 170 | [vue-jsonp]: https://github.com/LancerComet/vue-jsonp 171 | [vue-jsonp-ico]:https://img.shields.io/badge/Vue--jsonp-v2.0.0-brightgreen 172 | 173 | [vue-i18n-next]: https://github.com/intlify/vue-i18n-next/tree/master/packages/vue-i18n 174 | [vue-i18n-next-ico]:https://img.shields.io/badge/Vue--i18n-v9.1.10-brightgreen 175 | 176 | [js-md5]: https://github.com/emn178/js-md5 177 | [js-md5-ico]:https://img.shields.io/badge/js--md5-v0.7.3-brightgreen 178 | 179 | [nprogress]: https://github.com/rstacruz/nprogress 180 | [nprogress-ico]:https://img.shields.io/badge/nprogress-v0.2.0-brightgreen 181 | 182 | [echarts]: http://echarts.apache.org 183 | [echarts-ico]:https://img.shields.io/badge/echarts-v5.3.2-brightgreen 184 | 185 | --- 186 | 187 | ## Introduction functions 188 | Web 189 | - CDN Distributed js/ css / image / map json data 190 | - Adopted exclusively API remote real-time interface and mock data, and API or mock can be switched freely 191 | - 120+ high quality components page 192 | - The management real-time interface data transmission 193 | - Real time generation of large screen dynamic chart of visual data 194 | - Real-time data websocket interaction 195 | - Composition API models 196 | - JWT authentication 197 | - Real-time monitoring system & server resource usage 198 | - Commercially available free of charge 199 | - PC, mobile, tablet and other multi terminal compatibility 200 | - Dynamic routing menu authentication 201 | - Support MarkDown(md) file to Vue Component page 202 | - Support mock data and remote data 203 | - Support button function permission control 204 | - Support the assignment of roles and permissions of member and administrator 205 | - Support multiple theme switching and adding custom theme styles 206 | - Support multi language text switching 207 | - Support Pinia state management 208 | - Support for custom Vue directives 209 | - Support docking with third-party logistics platforms 210 | - Support binding third-party accounts 211 | - Support log tracing (member and administrator operation) 212 | 213 | Restful & Websocket 214 | - Go apis ver(1.7+) 215 | - PHP apis ver (8.0.2 +)/Swoole (4.8+) 216 | - SpringBoot apis ver (2.2+) 217 | - Support multiple open source relational database switching:MySQL、MariaDB、PostgreSQL、openGauss、TiDB 218 | - Supports multiple memory database switching:Redis、Memcached 219 | - Cluster supporting relational database 220 | - Cluster supporting memory database 221 | - Support cloud storage and CDN distribution of static files (images, videos, documents, ...) 222 | 223 | 224 | ## Demo & Repository 225 | 226 | - [Todoadmin-pro Pro demo](https://pro.todoadmin.com) 227 | - [Todoadmin-base Basic demo](https://base.todoadmin.com) 228 | - [Github repository](https://github.com/todoadmin/vue-admin-chart) 229 | - [Gitee repository](https://gitee.com/todoadmin/vue-admin-chart) 230 |

Username:guest

Password:123456

231 | 232 | ## Reward & contact 233 | 234 | - If you agree with the project, you can reward and contact QQ:308407381 235 | 236 | 237 | 240 | 243 | 244 |
238 | 239 | 241 | 242 |
245 | 246 | 247 | 248 | ## Advantages & considerations 249 | 250 | ``` 251 | Compared with other open source admin management, it has the following advantages: 252 | 1. Adopted exclusively remote api real-time interface and mock data, and API or mock can be switched freely 253 | 2. Routing menu permission and button permission control 254 | 3. Use real-time transmit data 255 | 4. Preference data, topic switching, multi language switching ... 256 | 5. Native CSS and SCSS automatic sorting and eslint automatic repair 257 | 6. Axios secondary packaging supports multiple modes and parameter modes 258 | 7. Websocket encapsulation, supporting real-time data transmission 259 | 8. Support md5/rsa encryption login 260 | 9. Support HTTPS data encryption transmission 261 | 10. Use CDN to distribute CSS, JS and image, so speed up 262 | 11. Full screen operation 263 | 12. Support MarkDown(md) file to Vue Component page 264 | 265 | Precautions for use: 266 | 1. Project uses Chrome browser , and Vue devtools plug-in debugging 267 | 2. Project uses the vscode + eslint verification, vscode editor needs to be configured 268 | 3. Project can also be developed using Golan or IntelliJ idea + related plug-ins 269 | 4. Project uses the MIT protocol. Please keep the MIT protocol for free commercial use 270 | 271 | ``` 272 | 273 | 274 | ## Rendering Preview 275 | 276 | The following is the screenshot of the pro version: 277 | 278 | 279 | 280 | 283 | 286 | 287 | 288 | 291 | 294 | 295 | 296 | 299 | 302 | 303 | 304 | 307 | 310 | 311 | 312 | 315 | 318 | 319 | 320 | 323 | 326 | 327 | 328 | 331 | 334 | 335 | 336 | 339 | 342 | 343 |
281 | 282 | 284 | 285 |
289 | 290 | 292 | 293 |
297 | 298 | 300 | 301 |
305 | 306 | 308 | 309 |
313 | 314 | 316 | 317 |
321 | 322 | 324 | 325 |
329 | 330 | 332 | 333 |
337 | 338 | 340 | 341 |
344 | 345 | 346 | 347 | ## Questions 348 | 349 |

For questions and support please use the official forum or community chat. The issue list of this repo is exclusively for bug reports and feature requests.

350 | 351 | ## Issues 352 | 353 |

Please make sure to read the Issue Reporting Checklist before opening an issue. Issues not conforming to the guidelines may be closed immediately.

354 | 355 | 356 | 357 | ## Browser & mob 358 | 359 | Mainstream browsers and IE 10+. 360 | 361 | | [IE / Edge](https://godban.github.io/browsers-support-badges/) IE/Edge | [Firefox](https://godban.github.io/browsers-support-badges/) Firefox | [Chrome](https://godban.github.io/browsers-support-badges/) Chrome | [Safari](https://godban.github.io/browsers-support-badges/) Safari | 362 | | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------- | 363 | | IE10/IE11/Edge | last 2 versions | last 2 versions | last 2 versions | 364 | 365 | 366 | ## Contribution 367 | 368 | Thank you to everyone who supports this project! 369 | 370 | 371 | 372 | ## Acknowledgment 373 | 374 | | Projects (in no particular order) | 375 | | ---------------------------------------------------------------- | 376 | | [vue](https://github.com/vuejs/vue) | 377 | | [element-plus](https://github.com/element-plus/element-plus) | 378 | | [pinia](https://github.com/vuejs/pinia) | 379 | | [vue-i18n-next](https://github.com/intlify/vue-i18n-next/tree/master/packages/vue-i18n) | 380 | | [axios](https://github.com/axios/axios) | 381 | | [echarts](http://echarts.apache.org) | 382 | | [nprogress](https://github.com/rstacruz/nprogress) | 383 | 384 | 385 | ## Commercial considerations 386 | 387 | This project can be used for commercial purposes free of charge. Please comply with [MIT]( https://opensource.org/licenses/MIT )Agreement and retention of the author's technical support statement 388 | 389 | Copyright (c) 2022-present, Todoadmin.com 390 | -------------------------------------------------------------------------------- /src/views/form/other.vue: -------------------------------------------------------------------------------- 1 | 335 | 336 | 483 | 484 | 556 | --------------------------------------------------------------------------------