├── .editorconfig ├── src ├── theme │ ├── index.css │ ├── dark │ │ ├── element-plus │ │ │ ├── css-vars.css │ │ │ ├── css-vars.css.map │ │ │ ├── css-vars.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── checkbox.scss │ │ │ └── var.scss │ │ ├── index.scss │ │ └── custom │ │ │ └── ct-css-vars.scss │ ├── lighting │ │ ├── element-plus │ │ │ ├── css-vars.css │ │ │ ├── css-vars.css.map │ │ │ ├── css-vars.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── checkbox.scss │ │ │ └── var.scss │ │ ├── index.scss │ │ └── custom │ │ │ └── ct-css-vars.scss │ ├── utils │ │ ├── index.ts │ │ └── change-theme.ts │ ├── mixins │ │ ├── config.scss │ │ ├── mixins.scss │ │ ├── function.scss │ │ └── _var.scss │ ├── index.scss │ ├── base │ │ ├── index.scss │ │ ├── element-plus │ │ │ ├── css-vars.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── checkbox.scss │ │ │ └── var.scss │ │ └── custom │ │ │ └── ct-css-vars.scss │ └── china-red │ │ ├── index.scss │ │ └── element-plus │ │ ├── css-vars.scss │ │ ├── redio.scss │ │ ├── table.scss │ │ ├── form.scss │ │ ├── pagination.scss │ │ ├── checkbox.scss │ │ └── var.scss ├── icons │ ├── common │ │ ├── 404.svg │ │ ├── zip.svg │ │ ├── chart.svg │ │ ├── size.svg │ │ ├── link.svg │ │ ├── guide.svg │ │ ├── component.svg │ │ ├── money.svg │ │ ├── email.svg │ │ ├── drag.svg │ │ ├── documentation.svg │ │ ├── fullscreen.svg │ │ ├── user.svg │ │ ├── lock.svg │ │ ├── excel.svg │ │ ├── example.svg │ │ ├── star.svg │ │ ├── table.svg │ │ ├── search.svg │ │ ├── password.svg │ │ ├── education.svg │ │ ├── tab.svg │ │ ├── message.svg │ │ ├── theme.svg │ │ ├── peoples.svg │ │ ├── edit.svg │ │ ├── nested.svg │ │ ├── tree-table.svg │ │ ├── eye.svg │ │ ├── clipboard.svg │ │ ├── list.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── hamburger.svg │ │ ├── wechat.svg │ │ ├── skill.svg │ │ ├── people.svg │ │ ├── language.svg │ │ ├── eye-open.svg │ │ ├── sidebar-logo.svg │ │ ├── bug.svg │ │ ├── demo.svg │ │ ├── pdf.svg │ │ ├── exit-fullscreen.svg │ │ ├── tree.svg │ │ ├── shopping.svg │ │ └── dashboard.svg │ ├── nav-bar │ │ ├── link.svg │ │ ├── user.svg │ │ ├── example.svg │ │ ├── table.svg │ │ ├── password.svg │ │ ├── nested.svg │ │ ├── eye.svg │ │ ├── eye-open.svg │ │ ├── tree.svg │ │ ├── theme-icon.svg │ │ └── dashboard.svg │ └── SvgIcon.vue ├── utils │ └── bus.ts ├── assets │ ├── gif │ │ └── dianchi.gif │ ├── 401_images │ │ └── 401.gif │ └── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png ├── views │ ├── nested │ │ ├── menu1 │ │ │ ├── menu1-3 │ │ │ │ └── index.vue │ │ │ ├── menu1-2 │ │ │ │ ├── menu1-2-1 │ │ │ │ │ └── index.vue │ │ │ │ ├── menu1-2-2 │ │ │ │ │ └── index.vue │ │ │ │ └── index.vue │ │ │ ├── index.vue │ │ │ └── menu1-1 │ │ │ │ └── index.vue │ │ └── menu2 │ │ │ └── index.vue │ ├── redirect │ │ └── index.tsx │ ├── basic-demo │ │ ├── hook │ │ │ └── index.vue │ │ ├── mock │ │ │ └── index.vue │ │ ├── pinia │ │ │ └── index.vue │ │ ├── svg-icon │ │ │ └── index.vue │ │ ├── keep-alive │ │ │ ├── tab-keep-alive.vue │ │ │ ├── third-child.vue │ │ │ ├── second-child.vue │ │ │ ├── index.vue │ │ │ └── second-keep-alive.vue │ │ ├── vue3-template │ │ │ └── Vue3Template.vue │ │ ├── parent-children │ │ │ ├── SubChildren.vue │ │ │ ├── index.vue │ │ │ └── Children.vue │ │ └── worker │ │ │ └── index.vue │ ├── setting-switch │ │ ├── index.vue │ │ └── SettingSwitch.vue │ └── dashboard │ │ └── index.vue ├── components │ ├── TestUnit.vue │ └── ElSvgIcon.vue ├── lib │ ├── element-plus.ts │ └── el-svg-icon.ts ├── styles │ ├── init-loading.css │ ├── project-style.scss │ ├── transition.scss │ ├── index.scss │ └── reset-elemenet-plus.scss ├── directives │ ├── index.ts │ ├── button-codes.ts │ ├── codes-permission.ts │ ├── roles-permission.ts │ └── lang.ts ├── lang │ └── index.ts ├── mock-prod-server.ts ├── layout │ ├── sidebar │ │ ├── MenuIcon.vue │ │ ├── Link.vue │ │ ├── index.vue │ │ └── Logo.vue │ ├── app-main │ │ ├── Hamburger.vue │ │ └── component │ │ │ ├── SizeSelect.vue │ │ │ ├── ScreenFull.vue │ │ │ ├── LangSelect.vue │ │ │ └── ThemeSelect.vue │ └── index.vue ├── api │ └── user.ts ├── hooks │ ├── use-self-router.ts │ ├── use-layout.ts │ ├── use-error-log.ts │ └── use-common.ts ├── store │ ├── config.ts │ └── tags-view.ts ├── permission.ts ├── plugins │ └── vite-plugin-setup-extend │ │ └── index.ts ├── main.ts └── App.vue ├── ts-out-dir └── src │ ├── permission.d.ts │ ├── directives │ ├── index.d.ts │ ├── button-codes.d.ts │ ├── codes-permission.d.ts │ ├── roles-permission.d.ts │ ├── index.js │ ├── button-codes.js │ ├── codes-permission.js │ └── roles-permission.js │ ├── lib │ ├── element-plus.d.ts │ └── element-plus.js │ ├── utils │ ├── bus.js │ ├── axios-req.d.ts │ ├── bus.d.ts │ ├── common-util.d.ts │ ├── axios-req.js │ └── common-util.js │ ├── hooks │ ├── use-error-log.d.ts │ ├── use-layout.d.ts │ ├── use-self-router.d.ts │ ├── use-common.d.ts │ ├── use-table.d.ts │ ├── use-permission.d.ts │ ├── use-common.js │ ├── use-self-router.js │ ├── use-error-log.js │ └── use-layout.js │ ├── settings.d.ts │ ├── main.d.ts │ ├── api │ ├── user.d.ts │ └── user.js │ ├── router │ └── index.d.ts │ ├── store │ ├── tagsView.d.ts │ ├── basic.d.ts │ └── tagsView.js │ ├── views │ └── redirect │ │ ├── index.d.ts │ │ └── index.jsx │ ├── settings.js │ ├── main.js │ └── permission.js ├── .eslintignore ├── public └── favicon.ico ├── .vscode ├── settings.json └── extensions.json ├── .eslintrc.json ├── .npmrc ├── .husky ├── pre-commit └── commit-msg ├── typings ├── shims-vue.d.ts ├── env.d.ts ├── global.d.ts ├── components.d.ts └── basic.d.ts ├── mock ├── example.ts └── user.ts ├── vitest.setup.ts ├── .prettierrc ├── tsconfig.json ├── .env.build ├── .env.build-test ├── .env.serve-dev ├── .env.serve-test ├── vitest.config.ts ├── index.html ├── .gitignore ├── tsconfig.base.json └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/404.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/zip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/css-vars.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/css-vars.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/permission.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /src/theme/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './change-theme' 2 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/index.d.ts: -------------------------------------------------------------------------------- 1 | export default function (app: any): void; 2 | -------------------------------------------------------------------------------- /ts-out-dir/src/lib/element-plus.d.ts: -------------------------------------------------------------------------------- 1 | export default function (app: any): void; 2 | -------------------------------------------------------------------------------- /ts-out-dir/src/utils/bus.js: -------------------------------------------------------------------------------- 1 | import mitt from 'mitt'; 2 | export default mitt(); 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | public 2 | node_modules 3 | .history 4 | .husky 5 | dist 6 | *.d.ts 7 | -------------------------------------------------------------------------------- /src/utils/bus.ts: -------------------------------------------------------------------------------- 1 | //bus even 2 | import mitt from 'mitt' 3 | export default mitt() 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-ts/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-error-log.d.ts: -------------------------------------------------------------------------------- 1 | export declare const useErrorLog: () => void; 2 | -------------------------------------------------------------------------------- /src/assets/gif/dianchi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-ts/HEAD/src/assets/gif/dianchi.gif -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-ts/HEAD/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-ts/HEAD/src/assets/404_images/404.png -------------------------------------------------------------------------------- /ts-out-dir/src/utils/axios-req.d.ts: -------------------------------------------------------------------------------- 1 | export default function axiosReq(config: any): import("axios").AxiosPromise; 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "npm.packageManager": "yarn" 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-ts/HEAD/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["./eslintrc/eslint-config.cjs", "./eslintrc/.eslintrc-auto-import.json"] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar", "esbenp.prettier-vscode","dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | 4 | ###aliyun address 5 | registry = https://registry.npmmirror.com 6 | 7 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-layout.d.ts: -------------------------------------------------------------------------------- 1 | export declare function isExternal(path: any): boolean; 2 | export declare function resizeHandler(): void; 3 | -------------------------------------------------------------------------------- /ts-out-dir/src/settings.d.ts: -------------------------------------------------------------------------------- 1 | import type { SettingsConfig } from '~/basic'; 2 | declare const settings: SettingsConfig; 3 | export default settings; 4 | -------------------------------------------------------------------------------- /ts-out-dir/src/utils/bus.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: import("mitt").Emitter>; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | #推送之前运行eslint检查 5 | npm run lint 6 | #推送之前运行单元测试检查 7 | #npm run test:unit 8 | 9 | -------------------------------------------------------------------------------- /src/theme/utils/change-theme.ts: -------------------------------------------------------------------------------- 1 | export const toggleHtmlClass = (className) => { 2 | document.querySelectorAll('html')[0].className = className 3 | } 4 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-1/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/menu1-2-2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /src/icons/common/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/mixins/config.scss: -------------------------------------------------------------------------------- 1 | $namespace: 'el' !default; 2 | $common-separator: '-' !default; 3 | $element-separator: '__' !default; 4 | $modifier-separator: '--' !default; 5 | $state-prefix: 'is-' !default; 6 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/button-codes.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | mounted(el: any, binding: any): void; 3 | componentUpdated(el: any, binding: any): void; 4 | }; 5 | export default _default; 6 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/codes-permission.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | mounted(el: any, binding: any): void; 3 | componentUpdated(el: any, binding: any): void; 4 | }; 5 | export default _default; 6 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/roles-permission.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | mounted(el: any, binding: any): void; 3 | componentUpdated(el: any, binding: any): void; 4 | }; 5 | export default _default; 6 | -------------------------------------------------------------------------------- /ts-out-dir/src/main.d.ts: -------------------------------------------------------------------------------- 1 | import '@/styles/index.scss'; 2 | import 'virtual:svg-icons-register'; 3 | import './permission'; 4 | import './theme/index.scss'; 5 | import 'uno.css'; 6 | import 'element-plus/dist/index.css'; 7 | -------------------------------------------------------------------------------- /typings/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | /*fix the import warning issue of vue file*/ 2 | declare module '*.vue' { 3 | import { DefineComponent } from 'vue' 4 | const component: DefineComponent<{}, {}, any> 5 | export default component 6 | } 7 | -------------------------------------------------------------------------------- /src/icons/common/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mock/example.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | url: '/getMapInfo', 4 | method: 'get', 5 | response: () => { 6 | return { 7 | code: 200, 8 | title: 'mock请求测试' 9 | } 10 | } 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /vitest.setup.ts: -------------------------------------------------------------------------------- 1 | import { config } from '@vue/test-utils' 2 | import { vi } from 'vitest' 3 | import ResizeObserver from 'resize-observer-polyfill' 4 | 5 | vi.stubGlobal('ResizeObserver', ResizeObserver) 6 | 7 | config.global.stubs = {} 8 | -------------------------------------------------------------------------------- /ts-out-dir/src/api/user.d.ts: -------------------------------------------------------------------------------- 1 | export declare const userInfoReq: () => Promise; 2 | export declare const loginReq: (subForm: any) => import("axios").AxiosPromise; 3 | export declare const loginOutReq: () => import("axios").AxiosPromise; 4 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #. "$(dirname "$0")/_/husky.sh" 3 | #在项目中我们会使用commit-msg这个git hook来校验我们commit时添加的备注信息是否符合规范。在以前的我们通常是这样配置: 4 | #--no-install 参数表示强制npx使用项目中node_modules目录中的commitlint包(如果需要开启,注意:需要安装npx) 5 | #npx --no-install commitlint --edit $1 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "tabWidth": 2, 4 | "printWidth": 120, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "bracketSpacing": true, 8 | "semi": false, 9 | "htmlWhitespaceSensitivity": "ignore" 10 | } 11 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-self-router.d.ts: -------------------------------------------------------------------------------- 1 | export declare const getQueryParam: () => any; 2 | export declare const routerPush: (name: any, params: any) => void; 3 | export declare const routerReplace: (name: any, params: any) => void; 4 | export declare const routerBack: () => void; 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "@/*": ["src/*"], 6 | "~/*": ["typings/*"] 7 | } 8 | }, 9 | "include": ["src", "typings"], 10 | "exclude": ["node_modules", "**/dist"] 11 | } 12 | -------------------------------------------------------------------------------- /.env.build: -------------------------------------------------------------------------------- 1 | #The defined variable must start with VITE_APP_ 2 | #notes: login use mock api 3 | 4 | VITE_APP_ENV = 'dev' 5 | VITE_APP_BASE_URL = '' 6 | 7 | #image or oss address 8 | VITE_APP_IMAGE_URL = '' 9 | 10 | #proxy, use this to test proxy 11 | #VITE_APP_BASE_URL = '/api' 12 | -------------------------------------------------------------------------------- /.env.build-test: -------------------------------------------------------------------------------- 1 | #The defined variable must start with VITE_APP_ 2 | #notes: login use mock api 3 | 4 | VITE_APP_ENV = 'dev' 5 | VITE_APP_BASE_URL = '' 6 | 7 | #image or oss address 8 | VITE_APP_IMAGE_URL = '' 9 | 10 | #proxy,use this to test proxy 11 | #VITE_APP_BASE_URL = '/api' 12 | -------------------------------------------------------------------------------- /.env.serve-dev: -------------------------------------------------------------------------------- 1 | #The defined variable must start with VITE_APP_ 2 | #notes: login use mock api 3 | 4 | VITE_APP_ENV = 'dev' 5 | VITE_APP_BASE_URL = '' 6 | 7 | #image or oss address 8 | VITE_APP_IMAGE_URL = '' 9 | 10 | #proxy, use this to test proxy 11 | #VITE_APP_BASE_URL = '/api' 12 | -------------------------------------------------------------------------------- /.env.serve-test: -------------------------------------------------------------------------------- 1 | #The defined variable must start with VITE_APP_ 2 | #notes: login use mock api 3 | 4 | VITE_APP_ENV = 'dev' 5 | VITE_APP_BASE_URL = '' 6 | 7 | #image or oss address 8 | VITE_APP_IMAGE_URL = '' 9 | 10 | #proxy, use this to test proxy 11 | #VITE_APP_BASE_URL = '/api' 12 | -------------------------------------------------------------------------------- /src/icons/common/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/lib/element-plus.js: -------------------------------------------------------------------------------- 1 | import * as AllComponent from 'element-plus'; 2 | const elementPlusComponentNameArr = ['ElButton']; 3 | export default function (app) { 4 | elementPlusComponentNameArr.forEach((component) => { 5 | app.component(component, AllComponent[component]); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /ts-out-dir/src/router/index.d.ts: -------------------------------------------------------------------------------- 1 | import type { RouterTypes } from '~/basic'; 2 | export declare const constantRoutes: RouterTypes; 3 | export declare const roleCodeRoutes: RouterTypes; 4 | export declare const asyncRoutes: RouterTypes; 5 | declare const router: import("vue-router").Router; 6 | export default router; 7 | -------------------------------------------------------------------------------- /src/components/TestUnit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/element-plus.ts: -------------------------------------------------------------------------------- 1 | import * as AllComponent from 'element-plus' 2 | //element-plus中按需引入会引起首次加载过慢 3 | const elementPlusComponentNameArr = ['ElButton'] 4 | export default function (app) { 5 | elementPlusComponentNameArr.forEach((component) => { 6 | app.component(component, AllComponent[component]) 7 | }) 8 | } 9 | -------------------------------------------------------------------------------- /src/styles/init-loading.css: -------------------------------------------------------------------------------- 1 | /*开屏loading样式设置*/ 2 | .loader-wrapper{ 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | height: 80vh; 8 | } 9 | 10 | .loading-gif{ 11 | 12 | } 13 | 14 | .load_title{ 15 | margin-top: 30px; 16 | font-size: 16px; 17 | } 18 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /typings/env.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface ImportMetaEnv { 3 | readonly VITE_APP_BASE_URL: string 4 | readonly VITE_APP_IMAGE_URL: string 5 | readonly VITE_APP_ENV: string 6 | // 更多环境变量... 7 | } 8 | interface ImportMeta { 9 | readonly env: ImportMetaEnv 10 | } 11 | } 12 | export {} 13 | -------------------------------------------------------------------------------- /typings/global.d.ts: -------------------------------------------------------------------------------- 1 | import type { defineOptions as _defineOptions } from 'unplugin-vue-define-options/macros.d.ts' 2 | declare global { 3 | interface ObjKeys { 4 | [propName: string]: any 5 | } 6 | const GLOBAL_VAR: String 7 | const defineOptions: typeof _defineOptions 8 | const $ref: any 9 | } 10 | export {} 11 | -------------------------------------------------------------------------------- /src/icons/common/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/el-svg-icon.ts: -------------------------------------------------------------------------------- 1 | import * as components from '@element-plus/icons-vue' 2 | 3 | export default { 4 | install: (app) => { 5 | for (const key in components) { 6 | const componentConfig = components[key]; 7 | app.component(componentConfig.name, componentConfig); 8 | } 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/icons/common/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/index.scss: -------------------------------------------------------------------------------- 1 | // we can add this to custom namespace, default is 'el' 2 | //@forward "element-plus/theme-chalk/src/mixins/config.scss" with ( 3 | // $namespace: "el" 4 | //); 5 | 6 | //base-theme 7 | @use "./base"; 8 | //theme style such as dark-theme lighting 9 | @use "./dark"; 10 | @use "./lighting"; 11 | @use "./china-red"; 12 | -------------------------------------------------------------------------------- /src/icons/common/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /ts-out-dir/src/store/tagsView.d.ts: -------------------------------------------------------------------------------- 1 | export declare const useTagsViewStore: import("pinia").StoreDefinition<"tagsView", { 2 | visitedViews: never[]; 3 | }, {}, { 4 | addVisitedView(view: any): void; 5 | delVisitedView(view: any): Promise; 6 | delOthersVisitedViews(view: any): Promise; 7 | delAllVisitedViews(): Promise; 8 | }>; 9 | -------------------------------------------------------------------------------- /src/icons/common/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/base/index.scss: -------------------------------------------------------------------------------- 1 | /*china-red*/ 2 | //element-plus 3 | @use "./element-plus/css-vars"; 4 | @use "./element-plus/var"; 5 | @use "./element-plus/button"; 6 | @use "./element-plus/checkbox"; 7 | @use "./element-plus/redio"; 8 | @use "./element-plus/pagination"; 9 | @use "./element-plus/form"; 10 | @use "./element-plus/table"; 11 | 12 | //custom 13 | @use "./custom/ct-css-vars"; 14 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/index.js: -------------------------------------------------------------------------------- 1 | import buttonCodes from './button-codes'; 2 | import codesPermission from './codes-permission'; 3 | import rolesPermission from './roles-permission'; 4 | export default function (app) { 5 | app.directive('ButtonCodes', buttonCodes); 6 | app.directive('CodesPermission', codesPermission); 7 | app.directive('RolesPermission', rolesPermission); 8 | } 9 | -------------------------------------------------------------------------------- /ts-out-dir/src/views/redirect/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly>, {}>; 2 | export default _default; 3 | -------------------------------------------------------------------------------- /src/theme/china-red/index.scss: -------------------------------------------------------------------------------- 1 | /*china-red*/ 2 | //element-plus 3 | @use "./element-plus/css-vars"; 4 | @use "./element-plus/var"; 5 | @use "./element-plus/button"; 6 | @use "./element-plus/checkbox"; 7 | @use "./element-plus/redio"; 8 | @use "./element-plus/pagination"; 9 | @use "./element-plus/form"; 10 | @use "./element-plus/table"; 11 | 12 | //custom 13 | @use "./custom/ct-css-vars"; 14 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/css-vars.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["var.scss","css-vars.scss","../../mixins/_var.scss"],"names":[],"mappings":"AAAA;ACMA;EACE;ECKA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA","file":"css-vars.css"} -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/css-vars.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sourceRoot":"","sources":["var.scss","css-vars.scss","../../mixins/_var.scss"],"names":[],"mappings":"AAAA;ACMA;EACE;ECKA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA","file":"css-vars.css"} -------------------------------------------------------------------------------- /src/theme/dark/index.scss: -------------------------------------------------------------------------------- 1 | /*china-red*/ 2 | //element-plus 3 | //@use "./element-plus/css-vars"; 4 | //@use "./element-plus/var"; 5 | //@use "./element-plus/button"; 6 | //@use "./element-plus/checkbox"; 7 | //@use "./element-plus/redio"; 8 | //@use "./element-plus/pagination"; 9 | //@use "./element-plus/form"; 10 | //@use "./element-plus/table"; 11 | 12 | //custom 13 | @use "./custom/ct-css-vars"; 14 | -------------------------------------------------------------------------------- /src/theme/lighting/index.scss: -------------------------------------------------------------------------------- 1 | /*china-red*/ 2 | //element-plus 3 | //@use "./element-plus/css-vars"; 4 | //@use "./element-plus/var"; 5 | //@use "./element-plus/button"; 6 | //@use "./element-plus/checkbox"; 7 | //@use "./element-plus/redio"; 8 | //@use "./element-plus/pagination"; 9 | //@use "./element-plus/form"; 10 | //@use "./element-plus/table"; 11 | 12 | //custom 13 | @use "./custom/ct-css-vars"; 14 | -------------------------------------------------------------------------------- /src/views/redirect/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue' 2 | export default defineComponent({ 3 | setup() { 4 | const route = useRoute() 5 | const router = useRouter() 6 | onBeforeMount(() => { 7 | const { params, query } = route 8 | const { path } = params 9 | router.replace({ path: `/${path}`, query }) 10 | }) 11 | return () =>
12 | } 13 | }) 14 | -------------------------------------------------------------------------------- /src/directives/index.ts: -------------------------------------------------------------------------------- 1 | import buttonCodes from './button-codes' 2 | import codesPermission from './codes-permission' 3 | import rolesPermission from './roles-permission' 4 | import lang from './lang' 5 | export default function (app) { 6 | app.directive('ButtonCodes', buttonCodes) 7 | app.directive('CodesPermission', codesPermission) 8 | app.directive('RolesPermission', rolesPermission) 9 | app.directive('lang', lang) 10 | } 11 | -------------------------------------------------------------------------------- /src/icons/common/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/basic-demo/hook/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /mock/user.ts: -------------------------------------------------------------------------------- 1 | const user = { 2 | url: '/mock/login', 3 | method: 'post', 4 | response: () => { 5 | return { 6 | code: 20000, 7 | jwtToken:"666666" 8 | } 9 | } 10 | } 11 | 12 | const loginOut = { 13 | url: '/mock/loginOut', 14 | method: 'post', 15 | response: () => { 16 | return { 17 | code: 200, 18 | title: 'mock请求测试' 19 | } 20 | } 21 | } 22 | 23 | export default [ 24 | user,loginOut 25 | ] 26 | -------------------------------------------------------------------------------- /src/icons/common/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/views/redirect/index.jsx: -------------------------------------------------------------------------------- 1 | import { defineComponent } from 'vue'; 2 | export default defineComponent({ 3 | setup() { 4 | const route = useRoute(); 5 | const router = useRouter(); 6 | onBeforeMount(() => { 7 | const { params, query } = route; 8 | const { path } = params; 9 | router.replace({ path: `/${path}`, query }); 10 | }); 11 | return () =>
; 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /src/views/basic-demo/mock/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/views/basic-demo/pinia/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/icons/common/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/basic-demo/svg-icon/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-common.d.ts: -------------------------------------------------------------------------------- 1 | export declare const sleepTimeout: (time: number) => Promise; 2 | export declare const useCommon: () => { 3 | totalPage: import("vue").Ref; 4 | startEndArr: import("vue").Ref; 5 | searchForm: import("vue").Ref<{}>; 6 | dialogTitle: import("vue").Ref; 7 | detailDialog: import("vue").Ref; 8 | }; 9 | export declare function cloneDeep(value: any): any; 10 | export declare const copyValueToClipboard: (value: any) => void; 11 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/css-vars.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | 3 | @use './var' as *; 4 | @use '../../mixins/var' as *; 5 | @use '../../mixins/mixins' as *; 6 | 7 | html.china-red { 8 | color-scheme: china-red; 9 | @each $type in (primary, success, warning, danger, error, info) { 10 | @include set-css-color-rgb($colors, $type); 11 | } 12 | 13 | @each $type in (primary, success, warning, danger, error, info) { 14 | @include set-css-color-type($colors, $type); 15 | } 16 | //--el-color-primary: #c72210; 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/css-vars.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | 3 | @use './var' as *; 4 | @use '../../mixins/var' as *; 5 | @use '../../mixins/mixins' as *; 6 | 7 | html.china-red { 8 | color-scheme: china-red; 9 | @each $type in (primary, success, warning, danger, error, info) { 10 | @include set-css-color-rgb($colors, $type); 11 | } 12 | 13 | @each $type in (primary, success, warning, danger, error, info) { 14 | @include set-css-color-type($colors, $type); 15 | } 16 | //--el-color-primary: #c72210; 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/css-vars.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | 3 | @use './var' as *; 4 | @use '../../mixins/var' as *; 5 | @use '../../mixins/mixins' as *; 6 | 7 | html.china-red { 8 | color-scheme: china-red; 9 | @each $type in (primary, success, warning, danger, error, info) { 10 | @include set-css-color-rgb($colors, $type); 11 | } 12 | 13 | @each $type in (primary, success, warning, danger, error, info) { 14 | @include set-css-color-type($colors, $type); 15 | } 16 | //--el-color-primary: #c72210; 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/css-vars.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:map'; 2 | 3 | @use './var' as *; 4 | @use '../../mixins/var' as *; 5 | @use '../../mixins/mixins' as *; 6 | 7 | html.china-red { 8 | color-scheme: china-red; 9 | @each $type in (primary, success, warning, danger, error, info) { 10 | @include set-css-color-rgb($colors,$type); 11 | } 12 | 13 | @each $type in (primary, success, warning, danger, error, info) { 14 | @include set-css-color-type($colors, $type); 15 | } 16 | //--el-color-primary: #c72210; 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/index.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n' 2 | import en from './en' 3 | import zh from './zh' 4 | import settings from '@/settings' 5 | const messages = { en, zh } 6 | 7 | const localeData = { 8 | globalInjection: true, //如果设置true, $t() 函数将注册到全局 9 | legacy: false, //如果想在composition api中使用需要设置为false 10 | locale: settings.defaultLanguage, 11 | messages // set locale messages 12 | } 13 | 14 | export const i18n = createI18n(localeData) 15 | export const setupI18n = { 16 | install(app) { 17 | app.use(i18n) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/icons/common/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/tab-keep-alive.vue: -------------------------------------------------------------------------------- 1 | 7 | 20 | -------------------------------------------------------------------------------- /src/icons/common/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mock-prod-server.ts: -------------------------------------------------------------------------------- 1 | // vite-plugin-mock v3.0.1 版本 生产mock有问题 2 | // import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' 3 | // //https://cn.vitejs.dev/guide/features.html#glob-import 4 | // // @ts-ignore 5 | // const modulesFiles = import.meta.glob('../mock/*', { eager: true }) 6 | // let modules = [] 7 | // for (const filePath in modulesFiles) { 8 | // //读取文件内容到 modules 9 | // modules = modules.concat(modulesFiles[filePath].default) 10 | // } 11 | // export function setupProdMockServer() { 12 | // //创建prod mock server 13 | // createProdMockServer([...modules]) 14 | // } 15 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | import Vue from '@vitejs/plugin-vue' 3 | import VueJsx from '@vitejs/plugin-vue-jsx' 4 | import DefineOptions from 'unplugin-vue-define-options/vite' 5 | 6 | export default defineConfig({ 7 | // @ts-ignore 8 | plugins: [Vue(), VueJsx(), DefineOptions()], 9 | optimizeDeps: { 10 | disabled: true 11 | }, 12 | test: { 13 | clearMocks: true, 14 | environment: 'jsdom', 15 | //setup 文件的路径。它们将运行在每个测试文件之前。 16 | setupFiles: ['./vitest.setup.ts'], 17 | transformMode: { 18 | web: [/\.[jt]sx$/] 19 | } 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /ts-out-dir/src/settings.js: -------------------------------------------------------------------------------- 1 | const settings = { 2 | title: 'Vue3 Admin Template', 3 | sidebarLogo: true, 4 | showNavbarTitle: false, 5 | ShowDropDown: true, 6 | showHamburger: true, 7 | showLeftMenu: true, 8 | showTagsView: true, 9 | tagsViewNum: 6, 10 | showTopNavbar: true, 11 | mainNeedAnimation: true, 12 | isNeedNprogress: true, 13 | isNeedLogin: true, 14 | permissionMode: 'roles', 15 | openProdMock: true, 16 | errorLog: ['prod'], 17 | delWindowHeight: '210px', 18 | tmpToken: 'tmp_token', 19 | viteBasePath: './' 20 | }; 21 | export default settings; 22 | -------------------------------------------------------------------------------- /src/icons/common/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /typings/components.d.ts: -------------------------------------------------------------------------------- 1 | // generated by unplugin-vue-components 2 | // We suggest you to commit this file into source control 3 | // Read more: https://github.com/vuejs/core/pull/3399 4 | import '@vue/runtime-core' 5 | 6 | export {} 7 | 8 | declare module '@vue/runtime-core' { 9 | export interface GlobalComponents { 10 | ElSvgIcon: typeof import('./../src/components/ElSvgIcon.vue')['default'] 11 | RouterLink: typeof import('vue-router')['RouterLink'] 12 | RouterView: typeof import('vue-router')['RouterView'] 13 | SvgIcon: typeof import('./../src/icons/SvgIcon.vue')['default'] 14 | TestUnit: typeof import('./../src/components/TestUnit.vue')['default'] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/icons/common/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/layout/sidebar/MenuIcon.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= title %> 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/redio.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-radio { 3 | --el-radio-font-size: var(--el-font-size-base); 4 | --el-radio-text-color: #262626; 5 | --el-radio-font-weight: var(--el-font-weight-primary); 6 | --el-radio-input-height: 14px; 7 | --el-radio-input-width: 14px; 8 | --el-radio-input-border-radius: var(--el-border-radius-circle); 9 | --el-radio-input-bg-color: var(--el-fill-color-blank); 10 | --el-radio-input-border: var(--el-border); 11 | --el-radio-input-border-color: transparent; 12 | //--el-radio-input-border-color-hover: transparent; 13 | } 14 | 15 | .el-radio__input.is-checked + .el-radio__label { 16 | color: #262626; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/redio.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-radio { 3 | --el-radio-font-size: var(--el-font-size-base); 4 | --el-radio-text-color: #262626; 5 | --el-radio-font-weight: var(--el-font-weight-primary); 6 | --el-radio-input-height: 14px; 7 | --el-radio-input-width: 14px; 8 | --el-radio-input-border-radius: var(--el-border-radius-circle); 9 | --el-radio-input-bg-color: var(--el-fill-color-blank); 10 | --el-radio-input-border: var(--el-border); 11 | --el-radio-input-border-color: transparent; 12 | //--el-radio-input-border-color-hover: transparent; 13 | } 14 | 15 | .el-radio__input.is-checked + .el-radio__label { 16 | color: #262626; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/redio.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-radio { 3 | --el-radio-font-size: var(--el-font-size-base); 4 | --el-radio-text-color: #262626; 5 | --el-radio-font-weight: var(--el-font-weight-primary); 6 | --el-radio-input-height: 14px; 7 | --el-radio-input-width: 14px; 8 | --el-radio-input-border-radius: var(--el-border-radius-circle); 9 | --el-radio-input-bg-color: var(--el-fill-color-blank); 10 | --el-radio-input-border: var(--el-border); 11 | --el-radio-input-border-color: transparent; 12 | //--el-radio-input-border-color-hover: transparent; 13 | } 14 | 15 | .el-radio__input.is-checked + .el-radio__label { 16 | color: #262626; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/redio.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-radio { 3 | --el-radio-font-size: var(--el-font-size-base); 4 | --el-radio-text-color: #262626; 5 | --el-radio-font-weight: var(--el-font-weight-primary); 6 | --el-radio-input-height: 14px; 7 | --el-radio-input-width: 14px; 8 | --el-radio-input-border-radius: var(--el-border-radius-circle); 9 | --el-radio-input-bg-color: var(--el-fill-color-blank); 10 | --el-radio-input-border: var(--el-border); 11 | --el-radio-input-border-color: transparent; 12 | //--el-radio-input-border-color-hover: transparent; 13 | } 14 | 15 | .el-radio__input.is-checked + .el-radio__label { 16 | color: #262626; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/directives/button-codes.ts: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic' 2 | 3 | function checkPermission(el, { value }) { 4 | if (value && Array.isArray(value)) { 5 | if (value.length) { 6 | const permissionRoles = value 7 | const hasPermission = useBasicStore().buttonCodes?.some((code) => permissionRoles.includes(code)) 8 | if (!hasPermission) el.parentNode && el.parentNode.removeChild(el) 9 | } 10 | } else { 11 | throw new Error(`need roles! Like v-permission="['admin','editor']"`) 12 | } 13 | } 14 | export default { 15 | mounted(el, binding) { 16 | checkPermission(el, binding) 17 | }, 18 | componentUpdated(el, binding) { 19 | checkPermission(el, binding) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/directives/codes-permission.ts: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic' 2 | function checkPermission(el, { value }) { 3 | if (value && Array.isArray(value)) { 4 | if (value.length > 0) { 5 | const permissionRoles = value 6 | const hasPermission = useBasicStore().codes?.some((role) => permissionRoles.includes(role)) 7 | if (!hasPermission) el.parentNode && el.parentNode.removeChild(el) 8 | } 9 | } else { 10 | throw new Error(`need codes! Like v-codes-permission="['admin','editor']"`) 11 | } 12 | } 13 | export default { 14 | mounted(el, binding) { 15 | checkPermission(el, binding) 16 | }, 17 | componentUpdated(el, binding) { 18 | checkPermission(el, binding) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/directives/roles-permission.ts: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic' 2 | function checkPermission(el, { value }) { 3 | if (value && Array.isArray(value)) { 4 | if (value.length > 0) { 5 | const permissionRoles = value 6 | const hasPermission = useBasicStore().roles?.some((role) => permissionRoles.includes(role)) 7 | if (!hasPermission) el.parentNode && el.parentNode.removeChild(el) 8 | } 9 | } else { 10 | throw new Error(`need roles! Like v-roles-permission="['admin','editor']"`) 11 | } 12 | } 13 | export default { 14 | mounted(el, binding) { 15 | checkPermission(el, binding) 16 | }, 17 | componentUpdated(el, binding) { 18 | checkPermission(el, binding) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/icons/common/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/utils/common-util.d.ts: -------------------------------------------------------------------------------- 1 | declare const _default: { 2 | getWeek(): string; 3 | mobilePhone(str: any): boolean; 4 | toSplitNumFor(num: any, numToSpace: any): any; 5 | bankCardNo(str: any): boolean; 6 | regEmail(str: any): boolean; 7 | idCardNumber(str: any): boolean; 8 | deleteArrItem(arr: any, arrItem: any): void; 9 | arrToRepeat(arr: any): any; 10 | deRepeatArr(seriesArr: any): unknown[]; 11 | byArrObjDeleteArrObj2(arrObj: any, arrObj2: any, objKey: any): any; 12 | deleteArrObjByKey(arrObj: any, objKey: any, value: any): any; 13 | findArrObjByKey(arrObj: any, objKey: any, value: any): any; 14 | byArrObjFindArrObj2(arrObj: any, arrObj2: any, objKey: any): any[]; 15 | }; 16 | export default _default; 17 | -------------------------------------------------------------------------------- /src/api/user.ts: -------------------------------------------------------------------------------- 1 | //获取用户信息 2 | import axiosReq from 'axios' 3 | // export const userInfoReq = (): Promise => { 4 | // return new Promise((resolve) => { 5 | // const reqConfig = { 6 | // url: '/basis-func/user/getUserInfo', 7 | // params: { plateFormId: 2 }, 8 | // method: 'post' 9 | // } 10 | // axiosReq(reqConfig).then(({ data }) => { 11 | // resolve(data) 12 | // }) 13 | // }) 14 | // } 15 | 16 | //登录 17 | export const loginReq = (subForm) => { 18 | return axiosReq({ 19 | url: '/mock/login', 20 | params: subForm, 21 | method: 'post' 22 | }) 23 | } 24 | 25 | //退出登录 26 | export const loginOutReq = () => { 27 | return axiosReq({ 28 | url: '/mock/loginOut', 29 | method: 'post' 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-table.d.ts: -------------------------------------------------------------------------------- 1 | export declare const useTable: (searchForm: any, selectPageReq: any) => { 2 | pageNum: import("vue").Ref; 3 | pageSize: import("vue").Ref; 4 | totalPage: import("vue").Ref; 5 | tableListData: import("vue").Ref; 6 | tableListReq: (config: any) => import("axios").AxiosPromise; 7 | dateRangePacking: (timeArr: any) => void; 8 | multipleSelection: import("vue").Ref; 9 | handleSelectionChange: (val: any) => void; 10 | handleCurrentChange: (val: any) => void; 11 | handleSizeChange: (val: any) => void; 12 | resetPageReq: () => void; 13 | multiDelBtnDill: (reqConfig: any) => void; 14 | tableDelDill: (row: any, reqConfig: any) => void; 15 | }; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /dist-ssr 4 | /node_modules 5 | 6 | #lock 7 | pnpm-lock.yaml 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | pnpm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | lerna-debug.log* 17 | 18 | # OS 19 | .DS_Store 20 | 21 | # Tests 22 | /coverage 23 | /.nyc_output 24 | 25 | # IDEs and editors 26 | /.idea 27 | .project 28 | .classpath 29 | .c9/ 30 | *.launch 31 | .settings/ 32 | *.sublime-workspace 33 | 34 | # IDE - VSCode 35 | .vscode/* 36 | !.vscode/settings.json 37 | !.vscode/tasks.json 38 | !.vscode/launch.json 39 | !.vscode/extensions.json 40 | 41 | # Other 42 | .history 43 | *.local 44 | yarn* 45 | pnpm* 46 | 47 | 48 | #.eslintrc-auto-import.json 49 | #auto-imports.d.ts 50 | #components.d.ts 51 | stats.html 52 | -------------------------------------------------------------------------------- /ts-out-dir/src/api/user.js: -------------------------------------------------------------------------------- 1 | import axiosReq from '@/utils/axios-req'; 2 | export const userInfoReq = () => { 3 | return new Promise((resolve) => { 4 | const reqConfig = { 5 | url: '/basis-func/user/getUserInfo', 6 | params: { plateFormId: 2 }, 7 | method: 'post' 8 | }; 9 | axiosReq(reqConfig).then(({ data }) => { 10 | resolve(data); 11 | }); 12 | }); 13 | }; 14 | export const loginReq = (subForm) => { 15 | return axiosReq({ 16 | url: '/basis-func/user/loginValid', 17 | params: subForm, 18 | method: 'post' 19 | }); 20 | }; 21 | export const loginOutReq = () => { 22 | return axiosReq({ 23 | url: '/basis-func/user/loginValid', 24 | method: 'post' 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-permission.d.ts: -------------------------------------------------------------------------------- 1 | import type { RouterTypes } from '~/basic'; 2 | import 'nprogress/nprogress.css'; 3 | export declare const filterAsyncRoutesByMenuList: (menuList: any) => RouterTypes; 4 | export declare function filterAsyncRoutesByRoles(routes: any, roles: any): RouterTypes; 5 | export declare function filterAsyncRouterByCodes(codesRoutes: any, codes: any): RouterTypes; 6 | export declare function filterAsyncRouter({ menuList, roles, codes }: { 7 | menuList: any; 8 | roles: any; 9 | codes: any; 10 | }): void; 11 | export declare function resetRouter(): void; 12 | export declare function resetState(): void; 13 | export declare function freshRouter(data: any): void; 14 | export declare const progressStart: () => void; 15 | export declare const progressClose: () => void; 16 | -------------------------------------------------------------------------------- /src/icons/common/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | const app = createApp(App); 4 | import router from './router'; 5 | import '@/styles/index.scss'; 6 | import 'virtual:svg-icons-register'; 7 | import svgIcon from '@/icons/SvgIcon.vue'; 8 | import directive from '@/directives'; 9 | import './permission'; 10 | import './theme/index.scss'; 11 | import 'uno.css'; 12 | import ElementPlus from 'element-plus'; 13 | import 'element-plus/dist/index.css'; 14 | app.use(ElementPlus); 15 | app.component('SvgIcon', svgIcon); 16 | directive(app); 17 | import { createPinia } from 'pinia'; 18 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'; 19 | const pinia = createPinia(); 20 | pinia.use(piniaPluginPersistedstate); 21 | app.use(pinia); 22 | app.use(router).mount('#app'); 23 | -------------------------------------------------------------------------------- /src/icons/common/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/button-codes.js: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic'; 2 | function checkPermission(el, { value }) { 3 | if (value && Array.isArray(value)) { 4 | if (value.length) { 5 | const permissionRoles = value; 6 | const hasPermission = useBasicStore().buttonCodes?.some((code) => permissionRoles.includes(code)); 7 | if (!hasPermission) 8 | el.parentNode && el.parentNode.removeChild(el); 9 | } 10 | } 11 | else { 12 | throw new Error(`need roles! Like v-permission="['admin','editor']"`); 13 | } 14 | } 15 | export default { 16 | mounted(el, binding) { 17 | checkPermission(el, binding); 18 | }, 19 | componentUpdated(el, binding) { 20 | checkPermission(el, binding); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/icons/nav-bar/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/ElSvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/codes-permission.js: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic'; 2 | function checkPermission(el, { value }) { 3 | if (value && Array.isArray(value)) { 4 | if (value.length > 0) { 5 | const permissionRoles = value; 6 | const hasPermission = useBasicStore().codes?.some((role) => permissionRoles.includes(role)); 7 | if (!hasPermission) 8 | el.parentNode && el.parentNode.removeChild(el); 9 | } 10 | } 11 | else { 12 | throw new Error(`need codes! Like v-codes-permission="['admin','editor']"`); 13 | } 14 | } 15 | export default { 16 | mounted(el, binding) { 17 | checkPermission(el, binding); 18 | }, 19 | componentUpdated(el, binding) { 20 | checkPermission(el, binding); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /ts-out-dir/src/directives/roles-permission.js: -------------------------------------------------------------------------------- 1 | import { useBasicStore } from '@/store/basic'; 2 | function checkPermission(el, { value }) { 3 | if (value && Array.isArray(value)) { 4 | if (value.length > 0) { 5 | const permissionRoles = value; 6 | const hasPermission = useBasicStore().roles?.some((role) => permissionRoles.includes(role)); 7 | if (!hasPermission) 8 | el.parentNode && el.parentNode.removeChild(el); 9 | } 10 | } 11 | else { 12 | throw new Error(`need roles! Like v-roles-permission="['admin','editor']"`); 13 | } 14 | } 15 | export default { 16 | mounted(el, binding) { 17 | checkPermission(el, binding); 18 | }, 19 | componentUpdated(el, binding) { 20 | checkPermission(el, binding); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /src/layout/app-main/Hamburger.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 34 | -------------------------------------------------------------------------------- /src/views/basic-demo/vue3-template/Vue3Template.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/table.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-table { 3 | --el-table-border-color: #f0f0f0; 4 | --el-table-border: 1px solid #f0f0f0; 5 | --el-table-text-color: var(--el-text-color-regular); 6 | --el-table-header-text-color: var(--el-text-color-secondary); 7 | --el-table-row-hover-bg-color: #ffece6; 8 | --el-table-current-row-bg-color: var(--el-color-primary-light-9); 9 | --el-table-header-bg-color: #fafafa; 10 | --el-table-fixed-box-shadow: var(--el-box-shadow-light); 11 | --el-table-bg-color: var(--el-fill-color-blank); 12 | --el-table-tr-bg-color: var(--el-fill-color-blank); 13 | --el-table-expanded-cell-bg-color: var(--el-fill-color-blank); 14 | --el-table-fixed-left-column: inset 10px 0 10px -10px rgba(0, 0, 0, 0.15); 15 | --el-table-fixed-right-column: inset -10px 0 10px -10px rgba(0, 0, 0, 0.15); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/table.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-table { 3 | --el-table-border-color: #f0f0f0; 4 | --el-table-border: 1px solid #f0f0f0; 5 | --el-table-text-color: var(--el-text-color-regular); 6 | --el-table-header-text-color: var(--el-text-color-secondary); 7 | --el-table-row-hover-bg-color: #ffece6; 8 | --el-table-current-row-bg-color: var(--el-color-primary-light-9); 9 | --el-table-header-bg-color: #fafafa; 10 | --el-table-fixed-box-shadow: var(--el-box-shadow-light); 11 | --el-table-bg-color: var(--el-fill-color-blank); 12 | --el-table-tr-bg-color: var(--el-fill-color-blank); 13 | --el-table-expanded-cell-bg-color: var(--el-fill-color-blank); 14 | --el-table-fixed-left-column: inset 10px 0 10px -10px rgba(0, 0, 0, 0.15); 15 | --el-table-fixed-right-column: inset -10px 0 10px -10px rgba(0, 0, 0, 0.15); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/table.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-table { 3 | --el-table-border-color: #f0f0f0; 4 | --el-table-border: 1px solid #f0f0f0; 5 | --el-table-text-color: var(--el-text-color-regular); 6 | --el-table-header-text-color: var(--el-text-color-secondary); 7 | --el-table-row-hover-bg-color: #ffece6; 8 | --el-table-current-row-bg-color: var(--el-color-primary-light-9); 9 | --el-table-header-bg-color: #fafafa; 10 | --el-table-fixed-box-shadow: var(--el-box-shadow-light); 11 | --el-table-bg-color: var(--el-fill-color-blank); 12 | --el-table-tr-bg-color: var(--el-fill-color-blank); 13 | --el-table-expanded-cell-bg-color: var(--el-fill-color-blank); 14 | --el-table-fixed-left-column: inset 10px 0 10px -10px rgba(0, 0, 0, 0.15); 15 | --el-table-fixed-right-column: inset -10px 0 10px -10px rgba(0, 0, 0, 0.15); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/table.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-table { 3 | --el-table-border-color: #f0f0f0; 4 | --el-table-border: 1px solid #f0f0f0; 5 | --el-table-text-color: var(--el-text-color-regular); 6 | --el-table-header-text-color: var(--el-text-color-secondary); 7 | --el-table-row-hover-bg-color: #ffece6; 8 | --el-table-current-row-bg-color: var(--el-color-primary-light-9); 9 | --el-table-header-bg-color: #fafafa; 10 | --el-table-fixed-box-shadow: var(--el-box-shadow-light); 11 | --el-table-bg-color: var(--el-fill-color-blank); 12 | --el-table-tr-bg-color: var(--el-fill-color-blank); 13 | --el-table-expanded-cell-bg-color: var(--el-fill-color-blank); 14 | --el-table-fixed-left-column: inset 10px 0 10px -10px rgba(0, 0, 0, 0.15); 15 | --el-table-fixed-right-column: inset -10px 0 10px -10px rgba(0, 0, 0, 0.15); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/layout/sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /src/icons/common/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/form.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | //date 3 | .el-date-range-picker { 4 | --el-datepicker-text-color: var(--el-text-color-regular); 5 | --el-datepicker-off-text-color: var(--el-text-color-placeholder); 6 | --el-datepicker-header-text-color: var(--el-text-color-regular); 7 | --el-datepicker-icon-color: var(--el-text-color-primary); 8 | --el-datepicker-border-color: var(--el-disabled-border-color); 9 | --el-datepicker-inner-border-color: var(--el-border-color-light); 10 | --el-datepicker-inrange-bg-color: #ffece6; 11 | --el-datepicker-inrange-hover-bg-color: var(--el-border-color-extra-light); 12 | --el-datepicker-active-color: var(--el-color-primary); 13 | --el-datepicker-hover-text-color: var(--el-color-primary); 14 | } 15 | 16 | .el-select-dropdown__item.hover, 17 | .el-select-dropdown__item:hover { 18 | background-color: #ffece6; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/form.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | //date 3 | .el-date-range-picker { 4 | --el-datepicker-text-color: var(--el-text-color-regular); 5 | --el-datepicker-off-text-color: var(--el-text-color-placeholder); 6 | --el-datepicker-header-text-color: var(--el-text-color-regular); 7 | --el-datepicker-icon-color: var(--el-text-color-primary); 8 | --el-datepicker-border-color: var(--el-disabled-border-color); 9 | --el-datepicker-inner-border-color: var(--el-border-color-light); 10 | --el-datepicker-inrange-bg-color: #ffece6; 11 | --el-datepicker-inrange-hover-bg-color: var(--el-border-color-extra-light); 12 | --el-datepicker-active-color: var(--el-color-primary); 13 | --el-datepicker-hover-text-color: var(--el-color-primary); 14 | } 15 | 16 | .el-select-dropdown__item.hover, 17 | .el-select-dropdown__item:hover { 18 | background-color: #ffece6; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/form.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | //date 3 | .el-date-range-picker { 4 | --el-datepicker-text-color: var(--el-text-color-regular); 5 | --el-datepicker-off-text-color: var(--el-text-color-placeholder); 6 | --el-datepicker-header-text-color: var(--el-text-color-regular); 7 | --el-datepicker-icon-color: var(--el-text-color-primary); 8 | --el-datepicker-border-color: var(--el-disabled-border-color); 9 | --el-datepicker-inner-border-color: var(--el-border-color-light); 10 | --el-datepicker-inrange-bg-color: #ffece6; 11 | --el-datepicker-inrange-hover-bg-color: var(--el-border-color-extra-light); 12 | --el-datepicker-active-color: var(--el-color-primary); 13 | --el-datepicker-hover-text-color: var(--el-color-primary); 14 | } 15 | 16 | .el-select-dropdown__item.hover, 17 | .el-select-dropdown__item:hover { 18 | background-color: #ffece6; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/form.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | //date 3 | .el-date-range-picker { 4 | --el-datepicker-text-color: var(--el-text-color-regular); 5 | --el-datepicker-off-text-color: var(--el-text-color-placeholder); 6 | --el-datepicker-header-text-color: var(--el-text-color-regular); 7 | --el-datepicker-icon-color: var(--el-text-color-primary); 8 | --el-datepicker-border-color: var(--el-disabled-border-color); 9 | --el-datepicker-inner-border-color: var(--el-border-color-light); 10 | --el-datepicker-inrange-bg-color: #ffece6; 11 | --el-datepicker-inrange-hover-bg-color: var(--el-border-color-extra-light); 12 | --el-datepicker-active-color: var(--el-color-primary); 13 | --el-datepicker-hover-text-color: var(--el-color-primary); 14 | } 15 | 16 | .el-select-dropdown__item.hover, 17 | .el-select-dropdown__item:hover { 18 | background-color: #ffece6; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/icons/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 42 | -------------------------------------------------------------------------------- /src/icons/common/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/project-style.scss: -------------------------------------------------------------------------------- 1 | .project-page-style { 2 | background: #fff; 3 | padding: 20px; 4 | padding-top: 0; 5 | } 6 | 7 | .query-page-style { 8 | background: #fff; 9 | padding: 20px; 10 | } 11 | 12 | //汽包 13 | .triangle { 14 | position: relative; 15 | left: 150px; 16 | width: 0; 17 | height: 0; 18 | border: 6px solid transparent; 19 | border-bottom-color: #fafafa; 20 | } 21 | 22 | //table relative 23 | .table-operation-btn { 24 | span { 25 | cursor: pointer; 26 | color: var(--el-color-primary); 27 | display: inline-block; 28 | padding: 0 6px; 29 | } 30 | } 31 | //新增底部 btn 32 | .footer-btn { 33 | position: fixed; 34 | width: calc(100% - var(--side-bar-width)); 35 | text-align: center; 36 | border-top: 1px solid var(--lineB3); 37 | background: #ffffff; 38 | padding: 12px 0; 39 | margin-left: 0; 40 | bottom: 0; 41 | z-index: 11; 42 | left: var(--side-bar-width) px; 43 | } 44 | -------------------------------------------------------------------------------- /src/icons/nav-bar/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/hooks/use-self-router.ts: -------------------------------------------------------------------------------- 1 | import router from '@/router' 2 | export const getQueryParam = () => { 3 | const route: any = router.currentRoute 4 | if (route.value?.query.params) { 5 | return JSON.parse(route.value.query.params) 6 | } 7 | } 8 | // vue router 9 | export const routerPush = (name, params) => { 10 | let data = {} 11 | if (params) { 12 | data = { 13 | params: JSON.stringify(params) 14 | } 15 | } else { 16 | data = {} 17 | } 18 | router.push({ 19 | name, 20 | query: data 21 | }) 22 | } 23 | export const routerReplace = (name, params) => { 24 | let data = {} 25 | if (params) { 26 | data = { 27 | params: JSON.stringify(params) 28 | } 29 | } else { 30 | data = {} 31 | } 32 | router.replace({ 33 | name, 34 | query: data 35 | }) 36 | } 37 | 38 | export const routeInfo = () => { 39 | return router.currentRoute 40 | } 41 | export const routerBack = () => { 42 | router.go(-1) 43 | } 44 | -------------------------------------------------------------------------------- /src/icons/common/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-common.js: -------------------------------------------------------------------------------- 1 | import { reactive, toRefs } from 'vue'; 2 | import useClipboard from 'vue-clipboard3'; 3 | import { ElMessage } from 'element-plus'; 4 | export const sleepTimeout = (time) => { 5 | return new Promise((resolve) => { 6 | const timer = setTimeout(() => { 7 | clearTimeout(timer); 8 | resolve(null); 9 | }, time); 10 | }); 11 | }; 12 | export const useCommon = () => { 13 | const state = reactive({ 14 | totalPage: 0, 15 | startEndArr: [], 16 | searchForm: {}, 17 | dialogTitle: '', 18 | detailDialog: false 19 | }); 20 | return { 21 | ...toRefs(state) 22 | }; 23 | }; 24 | export function cloneDeep(value) { 25 | return JSON.parse(JSON.stringify(value)); 26 | } 27 | const { toClipboard } = useClipboard(); 28 | export const copyValueToClipboard = (value) => { 29 | toClipboard(JSON.stringify(value)); 30 | ElMessage.success('复制成功'); 31 | }; 32 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-self-router.js: -------------------------------------------------------------------------------- 1 | import router from '@/router'; 2 | export const getQueryParam = () => { 3 | const route = router.currentRoute; 4 | if (route.value?.query.params) { 5 | return JSON.parse(route.value.query.params); 6 | } 7 | }; 8 | export const routerPush = (name, params) => { 9 | let data = {}; 10 | if (params) { 11 | data = { 12 | params: JSON.stringify(params) 13 | }; 14 | } 15 | else { 16 | data = {}; 17 | } 18 | router.push({ 19 | name, 20 | query: data 21 | }); 22 | }; 23 | export const routerReplace = (name, params) => { 24 | let data = {}; 25 | if (params) { 26 | data = { 27 | params: JSON.stringify(params) 28 | }; 29 | } 30 | else { 31 | data = {}; 32 | } 33 | router.replace({ 34 | name, 35 | query: data 36 | }); 37 | }; 38 | export const routerBack = () => { 39 | router.go(-1); 40 | }; 41 | -------------------------------------------------------------------------------- /src/store/config.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import { langTitle } from '@/hooks/use-common' 3 | import settings from '@/settings' 4 | import { toggleHtmlClass } from '@/theme/utils' 5 | import { i18n } from '@/lang' 6 | export const useConfigStore = defineStore('config', { 7 | state: () => { 8 | return { 9 | language: settings.defaultLanguage, 10 | theme: settings.defaultTheme, 11 | size: settings.defaultSize 12 | } 13 | }, 14 | persist: { 15 | storage: localStorage, 16 | paths: ['language', 'theme', 'size'] 17 | }, 18 | actions: { 19 | setTheme(data: string) { 20 | this.theme = data 21 | toggleHtmlClass(data) 22 | }, 23 | setSize(data: string) { 24 | this.size = data 25 | }, 26 | setLanguage(lang: string, title) { 27 | const { locale }: any = i18n.global 28 | this.language = lang 29 | locale.value = lang 30 | document.title = langTitle(title) // i18 page title 31 | } 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /src/icons/common/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-error-log.js: -------------------------------------------------------------------------------- 1 | import { jsErrorCollection } from 'js-error-collection'; 2 | import pack from '../../package.json'; 3 | import settings from '@/settings'; 4 | import bus from '@/utils/bus'; 5 | import axiosReq from '@/utils/axios-req'; 6 | const reqUrl = '/integration-front/errorCollection/insert'; 7 | const errorLogReq = (errLog) => { 8 | axiosReq({ 9 | url: reqUrl, 10 | data: { 11 | pageUrl: window.location.href, 12 | errorLog: errLog, 13 | browserType: navigator.userAgent, 14 | version: pack.version 15 | }, 16 | method: 'post' 17 | }).then(() => { 18 | bus.emit('reloadErrorPage', {}); 19 | }); 20 | }; 21 | export const useErrorLog = () => { 22 | if (settings.errorLog?.includes(import.meta.env.VITE_APP_ENV)) { 23 | jsErrorCollection({ runtimeError: true, rejectError: true, consoleError: true }, (errLog) => { 24 | if (!errLog.includes(reqUrl)) 25 | errorLogReq(errLog); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/icons/common/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/styles/transition.scss: -------------------------------------------------------------------------------- 1 | // vue global transition css define 2 | /* sidebar-logo-fade */ 3 | .sidebar-logo-fade-enter-active { 4 | transition: opacity var(--logo-switch-duration); 5 | } 6 | .sidebar-logo-fade-enter-from, 7 | .sidebar-logo-fade-leave-to { 8 | opacity: 0; 9 | } 10 | 11 | /* fade-transform AppMain*/ 12 | .fade-transform-leave-active, 13 | .fade-transform-enter-active { 14 | transition: all var(--page-transform-duration); 15 | } 16 | 17 | .fade-transform-enter-from { 18 | opacity: 0; 19 | transform: translateX(-10px); 20 | } 21 | 22 | .fade-transform-leave-to { 23 | opacity: 0; 24 | transform: translateX(10px); 25 | } 26 | .fade-transform-active { 27 | position: absolute; 28 | } 29 | 30 | /* breadcrumb transition */ 31 | .breadcrumb-enter-active, 32 | .breadcrumb-leave-active { 33 | transition: all var(--breadcrumb-change-duration); 34 | } 35 | 36 | .breadcrumb-enter-from, 37 | .breadcrumb-leave-active { 38 | opacity: 0; 39 | transform: translateX(10px); 40 | } 41 | 42 | .breadcrumb-leave-active { 43 | position: absolute; 44 | } 45 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/third-child.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/SubChildren.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/icons/common/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from '@/router' 2 | import {progressClose, progressStart } from '@/hooks/use-permission' 3 | import { useBasicStore } from '@/store/basic' 4 | import { langTitle } from '@/hooks/use-common' 5 | import settings from "@/settings"; 6 | 7 | //路由进入前拦截 8 | //to:将要进入的页面 vue-router4.0 不推荐使用next() 9 | const whiteList = ['/login', '/404', '/401'] // no redirect whitelist 10 | router.beforeEach(async (to) => { 11 | progressStart() 12 | document.title = langTitle(to.meta?.title) // i18 page title 13 | const basicStore = useBasicStore() 14 | //not login 15 | if (!settings.isNeedLogin) { 16 | basicStore.setFilterAsyncRoutes([]) 17 | return true 18 | } 19 | //1.判断token 20 | if (basicStore.token) { 21 | if (to.path === '/login') { 22 | return '/' 23 | } else { 24 | basicStore.setFilterAsyncRoutes([]) 25 | return true 26 | } 27 | } else { 28 | if (!whiteList.includes(to.path)) { 29 | return `/login?redirect=${to.path}` 30 | } else { 31 | return true 32 | } 33 | } 34 | }) 35 | //路由进入后拦截 36 | router.afterEach(() => { 37 | progressClose() 38 | }) 39 | -------------------------------------------------------------------------------- /src/icons/common/hamburger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/plugins/vite-plugin-setup-extend/index.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '@vue/compiler-sfc' 2 | import { render } from 'ejs' 3 | import type { Plugin } from 'vite' 4 | export default ({ inject }): Plugin => { 5 | return { 6 | name: 'vite-plugin-setup-extend', 7 | enforce: 'pre', 8 | // configResolved(resolvedConfig) { 9 | // viteConfig = resolvedConfig 10 | // }, 11 | async transformIndexHtml(html) { 12 | const result = await render(html, { ...inject }) 13 | return result 14 | }, 15 | transform(code, id) { 16 | if (/\.vue$/.test(id)) { 17 | const { descriptor } = parse(code) 18 | if (!descriptor?.scriptSetup?.setup) { 19 | return null 20 | } 21 | const { lang, name } = descriptor.scriptSetup?.attrs || {} 22 | const dillStr = headString(lang, name) 23 | code += dillStr 24 | return code 25 | } 26 | } 27 | } 28 | } 29 | 30 | const headString = (lang, name) => { 31 | return `\n` 37 | } 38 | -------------------------------------------------------------------------------- /src/icons/common/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import { createPinia } from 'pinia' 3 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 4 | import ElementPlus from 'element-plus' 5 | import App from './App.vue' 6 | import router from './router' 7 | 8 | //import theme 9 | import './theme/index.scss' 10 | 11 | //import unocss 12 | import 'uno.css' 13 | 14 | //i18n 15 | import { setupI18n } from '@/lang' 16 | 17 | import '@/styles/index.scss' // global css 18 | 19 | //svg-icon 20 | import 'virtual:svg-icons-register' 21 | import svgIcon from '@/icons/SvgIcon.vue' 22 | import directive from '@/directives' 23 | 24 | //import router intercept 25 | import './permission' 26 | 27 | //import element-plus 28 | import 'element-plus/dist/index.css' 29 | 30 | //import element-plus svg icon 31 | import ElSvgIcon from "@/lib/el-svg-icon" 32 | const app = createApp(App) 33 | app.use(ElSvgIcon) 34 | 35 | 36 | //router 37 | app.use(router) 38 | 39 | //pinia 40 | const pinia = createPinia() 41 | pinia.use(piniaPluginPersistedstate) 42 | app.use(pinia) 43 | 44 | //i18n 45 | app.use(setupI18n) 46 | app.component('SvgIcon', svgIcon) 47 | directive(app) 48 | 49 | //element-plus 50 | app.use(ElementPlus) 51 | 52 | app.mount('#app') 53 | -------------------------------------------------------------------------------- /src/icons/common/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/pagination.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-pagination { 3 | --el-text-color-regular: #8c8c8c; 4 | --el-pagination-font-size: 14px; 5 | --el-pagination-bg-color: var(--el-fill-color-blank); 6 | --el-pagination-text-color: var(--el-text-color-primary); 7 | --el-pagination-border-radius: 3px; 8 | --el-pagination-button-color: var(--el-text-color-primary); 9 | --el-pagination-button-width: 32px; 10 | --el-pagination-button-height: 32px; 11 | --el-pagination-button-disabled-color: var(--el-text-color-placeholder); 12 | --el-pagination-button-disabled-bg-color: var(--el-fill-color-blank); 13 | --el-pagination-button-bg-color: var(--el-fill-color); 14 | --el-pagination-hover-color: var(--el-color-primary); 15 | --el-pagination-height-extra-small: 24px; 16 | --el-pagination-line-height-extra-small: var(--el-pagination-height-extra-small); 17 | white-space: nowrap; 18 | padding: 2px 5px; 19 | color: var(--el-pagination-text-color); 20 | font-weight: 400; 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .el-pagination__total { 26 | margin-right: 16px; 27 | font-weight: 400; 28 | color: var(--el-text-color-regular); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/pagination.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-pagination { 3 | --el-text-color-regular: #8c8c8c; 4 | --el-pagination-font-size: 14px; 5 | --el-pagination-bg-color: var(--el-fill-color-blank); 6 | --el-pagination-text-color: var(--el-text-color-primary); 7 | --el-pagination-border-radius: 3px; 8 | --el-pagination-button-color: var(--el-text-color-primary); 9 | --el-pagination-button-width: 32px; 10 | --el-pagination-button-height: 32px; 11 | --el-pagination-button-disabled-color: var(--el-text-color-placeholder); 12 | --el-pagination-button-disabled-bg-color: var(--el-fill-color-blank); 13 | --el-pagination-button-bg-color: var(--el-fill-color); 14 | --el-pagination-hover-color: var(--el-color-primary); 15 | --el-pagination-height-extra-small: 24px; 16 | --el-pagination-line-height-extra-small: var(--el-pagination-height-extra-small); 17 | white-space: nowrap; 18 | padding: 2px 5px; 19 | color: var(--el-pagination-text-color); 20 | font-weight: 400; 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .el-pagination__total { 26 | margin-right: 16px; 27 | font-weight: 400; 28 | color: var(--el-text-color-regular); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/pagination.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-pagination { 3 | --el-text-color-regular: #8c8c8c; 4 | --el-pagination-font-size: 14px; 5 | --el-pagination-bg-color: var(--el-fill-color-blank); 6 | --el-pagination-text-color: var(--el-text-color-primary); 7 | --el-pagination-border-radius: 3px; 8 | --el-pagination-button-color: var(--el-text-color-primary); 9 | --el-pagination-button-width: 32px; 10 | --el-pagination-button-height: 32px; 11 | --el-pagination-button-disabled-color: var(--el-text-color-placeholder); 12 | --el-pagination-button-disabled-bg-color: var(--el-fill-color-blank); 13 | --el-pagination-button-bg-color: var(--el-fill-color); 14 | --el-pagination-hover-color: var(--el-color-primary); 15 | --el-pagination-height-extra-small: 24px; 16 | --el-pagination-line-height-extra-small: var(--el-pagination-height-extra-small); 17 | white-space: nowrap; 18 | padding: 2px 5px; 19 | color: var(--el-pagination-text-color); 20 | font-weight: 400; 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .el-pagination__total { 26 | margin-right: 16px; 27 | font-weight: 400; 28 | color: var(--el-text-color-regular); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/pagination.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-pagination { 3 | --el-text-color-regular: #8c8c8c; 4 | --el-pagination-font-size: 14px; 5 | --el-pagination-bg-color: var(--el-fill-color-blank); 6 | --el-pagination-text-color: var(--el-text-color-primary); 7 | --el-pagination-border-radius: 3px; 8 | --el-pagination-button-color: var(--el-text-color-primary); 9 | --el-pagination-button-width: 32px; 10 | --el-pagination-button-height: 32px; 11 | --el-pagination-button-disabled-color: var(--el-text-color-placeholder); 12 | --el-pagination-button-disabled-bg-color: var(--el-fill-color-blank); 13 | --el-pagination-button-bg-color: var(--el-fill-color); 14 | --el-pagination-hover-color: var(--el-color-primary); 15 | --el-pagination-height-extra-small: 24px; 16 | --el-pagination-line-height-extra-small: var(--el-pagination-height-extra-small); 17 | white-space: nowrap; 18 | padding: 2px 5px; 19 | color: var(--el-pagination-text-color); 20 | font-weight: 400; 21 | display: flex; 22 | align-items: center; 23 | } 24 | 25 | .el-pagination__total { 26 | margin-right: 16px; 27 | font-weight: 400; 28 | color: var(--el-text-color-regular); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/second-child.vue: -------------------------------------------------------------------------------- 1 | 16 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/hooks/use-layout.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 判断是否是外链 3 | * @param {string} path 4 | * @returns {Boolean} 5 | */ 6 | import { onBeforeMount, onBeforeUnmount, onMounted } from 'vue' 7 | import { useBasicStore } from '@/store/basic' 8 | export function isExternal(path) { 9 | return /^(https?:|mailto:|tel:)/.test(path) 10 | } 11 | 12 | /*判断窗口变化控制侧边栏收起或展开*/ 13 | export function resizeHandler() { 14 | const { body } = document 15 | const WIDTH = 992 16 | const basicStore = useBasicStore() 17 | const isMobile = () => { 18 | const rect = body.getBoundingClientRect() 19 | return rect.width - 1 < WIDTH 20 | } 21 | const resizeHandler = () => { 22 | if (!document.hidden) { 23 | if (isMobile()) { 24 | /*此处只做根据window尺寸关闭sideBar功能*/ 25 | basicStore.setSidebarOpen(false) 26 | } else { 27 | basicStore.setSidebarOpen(true) 28 | } 29 | } 30 | } 31 | onBeforeMount(() => { 32 | window.addEventListener('resize', resizeHandler) 33 | }) 34 | onMounted(() => { 35 | if (isMobile()) { 36 | basicStore.setSidebarOpen(false) 37 | } else { 38 | basicStore.setSidebarOpen(true) 39 | } 40 | }) 41 | onBeforeUnmount(() => { 42 | window.removeEventListener('resize', resizeHandler) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /ts-out-dir/src/hooks/use-layout.js: -------------------------------------------------------------------------------- 1 | import { onBeforeMount, onBeforeUnmount, onMounted } from 'vue'; 2 | import { useBasicStore } from '@/store/basic'; 3 | export function isExternal(path) { 4 | return /^(https?:|mailto:|tel:)/.test(path); 5 | } 6 | export function resizeHandler() { 7 | const { body } = document; 8 | const WIDTH = 992; 9 | const basicStore = useBasicStore(); 10 | const isMobile = () => { 11 | const rect = body.getBoundingClientRect(); 12 | return rect.width - 1 < WIDTH; 13 | }; 14 | const resizeHandler = () => { 15 | if (!document.hidden) { 16 | if (isMobile()) { 17 | basicStore.setSidebarOpen(false); 18 | } 19 | else { 20 | basicStore.setSidebarOpen(true); 21 | } 22 | } 23 | }; 24 | onBeforeMount(() => { 25 | window.addEventListener('resize', resizeHandler); 26 | }); 27 | onMounted(() => { 28 | if (isMobile()) { 29 | basicStore.setSidebarOpen(false); 30 | } 31 | else { 32 | basicStore.setSidebarOpen(true); 33 | } 34 | }); 35 | onBeforeUnmount(() => { 36 | window.removeEventListener('resize', resizeHandler); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /src/hooks/use-error-log.ts: -------------------------------------------------------------------------------- 1 | /*js 错误日志收集*/ 2 | import { jsErrorCollection } from 'js-error-collection' 3 | import axiosReq from 'axios' 4 | import pack from '../../package.json' 5 | import settings from '@/settings' 6 | import bus from '@/utils/bus' 7 | //此处不要使用utils下的axios 8 | const reqUrl = '/integration-front/errorCollection/insert' 9 | let repeatErrorLogJudge = '' 10 | const errorLogReq = (errLog: string) => { 11 | axiosReq({ 12 | url: import.meta.env.VITE_APP_BASE_URL+reqUrl, 13 | data: { 14 | pageUrl: window.location.href, 15 | errorLog: errLog, 16 | browserType: navigator.userAgent, 17 | version: pack.version 18 | }, 19 | method: 'post' 20 | }).then(() => { 21 | //通知错误列表页面更新数据 22 | bus.emit('reloadErrorPage', {}) 23 | }) 24 | } 25 | 26 | export const useErrorLog = () => { 27 | //判断该环境是否需要收集错误日志,由settings配置决定 28 | if (settings.errorLog?.includes(import.meta.env.VITE_APP_ENV)) { 29 | jsErrorCollection({ runtimeError: true, rejectError: true, consoleError: true }, (errLog) => { 30 | if (!repeatErrorLogJudge || !errLog.includes(repeatErrorLogJudge)) { 31 | errorLogReq(errLog) 32 | //移除重复日志,fix重复提交错误日志,避免造成死循环 33 | repeatErrorLogJudge = errLog.slice(0, 20) 34 | } 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/icons/common/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/checkbox.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-checkbox { 3 | --el-checkbox-font-size: 14px; 4 | --el-checkbox-font-weight: var(--el-font-weight-primary); 5 | --el-checkbox-text-color: #262626; 6 | --el-checkbox-input-height: 14px; 7 | --el-checkbox-input-width: 14px; 8 | --el-checkbox-border-radius: var(--el-border-radius-small); 9 | --el-checkbox-bg-color: var(--el-fill-color-blank); 10 | --el-checkbox-input-border: var(--el-border); 11 | 12 | //disabled 13 | --el-checkbox-disabled-border-color: var(--el-border-color); 14 | --el-checkbox-disabled-input-fill: var(--el-fill-color-light); 15 | --el-checkbox-disabled-icon-color: var(--el-text-color-placeholder); 16 | --el-checkbox-disabled-checked-input-fill: var(--el-border-color-extra-light); 17 | --el-checkbox-disabled-checked-input-border-color: var(--el-border-color); 18 | --el-checkbox-disabled-checked-icon-color: var(--el-text-color-placeholder); 19 | 20 | //check 21 | --el-checkbox-checked-text-color: #262626; 22 | --el-checkbox-checked-input-border-color: transparent; 23 | --el-checkbox-checked-bg-color: #c72210; 24 | --el-checkbox-checked-icon-color: #ffffff; 25 | --el-checkbox-input-border-color-hover: #c72210; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/checkbox.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-checkbox { 3 | --el-checkbox-font-size: 14px; 4 | --el-checkbox-font-weight: var(--el-font-weight-primary); 5 | --el-checkbox-text-color: #262626; 6 | --el-checkbox-input-height: 14px; 7 | --el-checkbox-input-width: 14px; 8 | --el-checkbox-border-radius: var(--el-border-radius-small); 9 | --el-checkbox-bg-color: var(--el-fill-color-blank); 10 | --el-checkbox-input-border: var(--el-border); 11 | 12 | //disabled 13 | --el-checkbox-disabled-border-color: var(--el-border-color); 14 | --el-checkbox-disabled-input-fill: var(--el-fill-color-light); 15 | --el-checkbox-disabled-icon-color: var(--el-text-color-placeholder); 16 | --el-checkbox-disabled-checked-input-fill: var(--el-border-color-extra-light); 17 | --el-checkbox-disabled-checked-input-border-color: var(--el-border-color); 18 | --el-checkbox-disabled-checked-icon-color: var(--el-text-color-placeholder); 19 | 20 | //check 21 | --el-checkbox-checked-text-color: #262626; 22 | --el-checkbox-checked-input-border-color: transparent; 23 | --el-checkbox-checked-bg-color: #c72210; 24 | --el-checkbox-checked-icon-color: #ffffff; 25 | --el-checkbox-input-border-color-hover: #c72210; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/hooks/use-common.ts: -------------------------------------------------------------------------------- 1 | //复制文本 2 | import useClipboard from 'vue-clipboard3' 3 | import { ElMessage } from 'element-plus' 4 | 5 | // i18n language match title 6 | import { i18n } from '@/lang' 7 | // the keys using zh file 8 | import langEn from '@/lang/zh' 9 | import settings from '@/settings' 10 | 11 | export const sleepTimeout = (time: number) => { 12 | return new Promise((resolve) => { 13 | const timer = setTimeout(() => { 14 | clearTimeout(timer) 15 | resolve(null) 16 | }, time) 17 | }) 18 | } 19 | 20 | //深拷贝 21 | export function cloneDeep(value) { 22 | return JSON.parse(JSON.stringify(value)) 23 | } 24 | 25 | //copyValueToClipboard 26 | const { toClipboard } = useClipboard() 27 | export const copyValueToClipboard = (value: any) => { 28 | toClipboard(JSON.stringify(value)) 29 | ElMessage.success('复制成功') 30 | } 31 | const { t, te } = i18n.global 32 | export const langTitle = (title) => { 33 | if (!title) { 34 | return settings.title 35 | } 36 | for (const key of Object.keys(langEn)) { 37 | if (te(`${key}.${title}`) && t(`${key}.${title}`)) { 38 | return t(`${key}.${title}`) 39 | } 40 | } 41 | return title 42 | } 43 | 44 | //get i18n instance 45 | export const getLangInstance = () => { 46 | return i18n.global as ObjKeys 47 | } 48 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/checkbox.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-checkbox { 3 | --el-checkbox-font-size: 14px; 4 | --el-checkbox-font-weight: var(--el-font-weight-primary); 5 | --el-checkbox-text-color: #262626; 6 | --el-checkbox-input-height: 14px; 7 | --el-checkbox-input-width: 14px; 8 | --el-checkbox-border-radius: var(--el-border-radius-small); 9 | --el-checkbox-bg-color: var(--el-fill-color-blank); 10 | --el-checkbox-input-border: var(--el-border); 11 | 12 | //disabled 13 | --el-checkbox-disabled-border-color: var(--el-border-color); 14 | --el-checkbox-disabled-input-fill: var(--el-fill-color-light); 15 | --el-checkbox-disabled-icon-color: var(--el-text-color-placeholder); 16 | --el-checkbox-disabled-checked-input-fill: var(--el-border-color-extra-light); 17 | --el-checkbox-disabled-checked-input-border-color: var(--el-border-color); 18 | --el-checkbox-disabled-checked-icon-color: var(--el-text-color-placeholder); 19 | 20 | //check 21 | --el-checkbox-checked-text-color: #262626; 22 | --el-checkbox-checked-input-border-color: transparent; 23 | --el-checkbox-checked-bg-color: #c72210; 24 | --el-checkbox-checked-icon-color: #ffffff; 25 | --el-checkbox-input-border-color-hover: #c72210; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/checkbox.scss: -------------------------------------------------------------------------------- 1 | html.china-red { 2 | .el-checkbox { 3 | --el-checkbox-font-size: 14px; 4 | --el-checkbox-font-weight: var(--el-font-weight-primary); 5 | --el-checkbox-text-color: #262626; 6 | --el-checkbox-input-height: 14px; 7 | --el-checkbox-input-width: 14px; 8 | --el-checkbox-border-radius: var(--el-border-radius-small); 9 | --el-checkbox-bg-color: var(--el-fill-color-blank); 10 | --el-checkbox-input-border: var(--el-border); 11 | 12 | //disabled 13 | --el-checkbox-disabled-border-color: var(--el-border-color); 14 | --el-checkbox-disabled-input-fill: var(--el-fill-color-light); 15 | --el-checkbox-disabled-icon-color: var(--el-text-color-placeholder); 16 | --el-checkbox-disabled-checked-input-fill: var(--el-border-color-extra-light); 17 | --el-checkbox-disabled-checked-input-border-color: var(--el-border-color); 18 | --el-checkbox-disabled-checked-icon-color: var(--el-text-color-placeholder); 19 | 20 | //check 21 | --el-checkbox-checked-text-color: #262626; 22 | --el-checkbox-checked-input-border-color: transparent; 23 | --el-checkbox-checked-bg-color: #c72210; 24 | --el-checkbox-checked-icon-color: #ffffff; 25 | --el-checkbox-input-border-color-hover: #c72210; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/views/basic-demo/worker/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 47 | -------------------------------------------------------------------------------- /src/theme/mixins/mixins.scss: -------------------------------------------------------------------------------- 1 | //input function 2 | @use 'function' as *; 3 | @forward 'function'; 4 | 5 | @forward 'config'; 6 | @use 'config' as *; 7 | // el-button{} 8 | @mixin b($block) { 9 | $B: $namespace + '-' + $block !global; 10 | 11 | .#{$B} { 12 | @content; 13 | } 14 | } 15 | @mixin e($element) { 16 | $E: $element !global; 17 | $selector: &; 18 | $currentSelector: ''; 19 | @each $unit in $element { 20 | //el-button__text 21 | $currentSelector: #{$currentSelector + '.' + $B + $element-separator + $unit + ','}; 22 | } 23 | 24 | @if hitAllSpecialNestRule($selector) { 25 | @at-root { 26 | #{$selector} { 27 | #{$currentSelector} { 28 | @content; 29 | } 30 | } 31 | } 32 | } @else { 33 | @at-root { 34 | #{$currentSelector} { 35 | @content; 36 | } 37 | } 38 | } 39 | } 40 | 41 | @mixin m($modifier) { 42 | $selector: &; 43 | $currentSelector: ''; 44 | @each $unit in $modifier { 45 | $currentSelector: #{$currentSelector + $selector + $modifier-separator + $unit + ','}; 46 | } 47 | 48 | @at-root { 49 | #{$currentSelector} { 50 | @content; 51 | } 52 | } 53 | } 54 | 55 | @mixin when($state) { 56 | @at-root { 57 | &.#{$state-prefix + $state} { 58 | @content; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | //scss 语法糖包含常用的布局方式 flex column 2 | @import './scss-suger.scss'; 3 | //重置 element-plus 样式 4 | @import 'reset-elemenet-plus.scss'; 5 | //动画文件 6 | @import './transition.scss'; 7 | @import './project-style.scss'; 8 | 9 | //reset style 10 | body { 11 | height: 100%; 12 | margin: 0; 13 | padding: 0; 14 | font-size: 14px; 15 | } 16 | * { 17 | box-sizing: border-box; 18 | } 19 | *::before, 20 | *::after { 21 | box-sizing: border-box; 22 | } 23 | a:focus, 24 | a:active { 25 | outline: none; 26 | } 27 | a, 28 | a:focus, 29 | a:hover { 30 | cursor: pointer; 31 | color: inherit; 32 | text-decoration: none; 33 | } 34 | 35 | h1, 36 | h2, 37 | h3, 38 | h4, 39 | h5, 40 | h6 { 41 | line-height: 1; 42 | font-weight: 400; 43 | margin: 0; 44 | padding: 0; 45 | } 46 | span, 47 | output { 48 | display: inline-block; 49 | line-height: 1; 50 | } 51 | 52 | //scroll 53 | @mixin main-show-wh() { 54 | /* css 声明 */ 55 | height: calc(90vh - #{var(--nav-bar-height)} - #{var(--tag-view-height)} - #{calc(var(--app-main-padding) * 2)}); 56 | width: 100%; 57 | } 58 | .scroll-y { 59 | @include main-show-wh(); 60 | overflow-y: auto; 61 | } 62 | .scroll-x { 63 | @include main-show-wh(); 64 | overflow-x: auto; 65 | } 66 | .scroll-xy { 67 | @include main-show-wh(); 68 | overflow: auto; 69 | } 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ts-out-dir/src/store/basic.d.ts: -------------------------------------------------------------------------------- 1 | import type { RouterTypes } from '~/basic'; 2 | export declare const useBasicStore: import("pinia").StoreDefinition<"basic", { 3 | token: string; 4 | getUserInfo: boolean; 5 | userInfo: { 6 | username: string; 7 | avatar: string; 8 | }; 9 | allRoutes: RouterTypes; 10 | buttonCodes: never[]; 11 | filterAsyncRoutes: never[]; 12 | roles: string[]; 13 | codes: number[]; 14 | cachedViews: string[]; 15 | cachedViewsDeep: string[]; 16 | sidebar: { 17 | opened: boolean; 18 | }; 19 | axiosPromiseArr: ObjKeys[]; 20 | settings: import("~/basic").SettingsConfig; 21 | }, {}, { 22 | setToken(data: any): void; 23 | setFilterAsyncRoutes(routes: any): void; 24 | setUserInfo({ userInfo, roles, codes }: { 25 | userInfo: any; 26 | roles: any; 27 | codes: any; 28 | }): void; 29 | resetState(): void; 30 | resetStateAndToLogin(): void; 31 | M_settings(data: any): void; 32 | setSidebarOpen(data: any): void; 33 | setToggleSideBar(): void; 34 | addCachedView(view: any): void; 35 | delCachedView(view: any): void; 36 | M_RESET_CACHED_VIEW(): void; 37 | addCachedViewDeep(view: any): void; 38 | setCacheViewDeep(view: any): void; 39 | M_RESET_CACHED_VIEW_DEEP(): void; 40 | A_sidebar_opened(data: any): void; 41 | }>; 42 | -------------------------------------------------------------------------------- /src/layout/app-main/component/SizeSelect.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/icons/common/sidebar-logo.svg: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 39 | -------------------------------------------------------------------------------- /src/layout/app-main/component/ScreenFull.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/second-keep-alive.vue: -------------------------------------------------------------------------------- 1 | 16 | 39 | -------------------------------------------------------------------------------- /src/layout/app-main/component/LangSelect.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/styles/reset-elemenet-plus.scss: -------------------------------------------------------------------------------- 1 | //leave the padding of el-scroll sidebar 2 | #Sidebar{ 3 | .el-scrollbar__view { 4 | padding-bottom: 80px; 5 | } 6 | .el-scrollbar{ 7 | --el-menu-base-level-padding:15px; 8 | } 9 | 10 | } 11 | 12 | 13 | 14 | .el-dialog__body{ 15 | padding-top:0!important; 16 | } 17 | 18 | 19 | //.el-loading-spinner .circular { 20 | // display: none !important; 21 | //} 22 | //.el-loading-spinner { 23 | // background: url('../assets/gif/loading.gif') no-repeat; 24 | // background-size: 300px 300px; 25 | // width: 300px!important; 26 | // height: 300px!important; 27 | // position: fixed; 28 | // top: 50%; 29 | // left: 50%; 30 | // transform: translate(-50%, -50%); 31 | // z-index: 10; 32 | //} 33 | 34 | 35 | /* loading loading遮罩层 36 | ------------------------------- */ 37 | .el-loading-mask { 38 | // position: relative !important; 39 | .el-loading-spinner { 40 | position: fixed !important; 41 | background: #ffffff !important; 42 | width: 200px !important; 43 | height: 120px !important; 44 | border-radius: 8px !important; 45 | left: 50% !important; 46 | transform: translate(-50%, -50%) !important; 47 | padding-top: 20px !important; 48 | box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); 49 | i { 50 | font-size: 20px; 51 | } 52 | .el-loading-text { 53 | margin: 12px 0 0 0 !important; 54 | //color: set-color(brand) !important; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/directives/lang.ts: -------------------------------------------------------------------------------- 1 | import { watch } from 'vue' 2 | import { storeToRefs } from 'pinia/dist/pinia' 3 | import { langTitle } from '@/hooks/use-common' 4 | import { useConfigStore } from '@/store/config' 5 | //element-plus 6 | const componentToProps = { 7 | ElInput: 'placeholder', 8 | ElTableColumn: 'label' 9 | } 10 | 11 | function checkPermission(el, { value }) { 12 | let saveOriginTitle = '' 13 | const { language } = storeToRefs(useConfigStore()) 14 | //save the original title 15 | const name = el.__vueParentComponent?.type?.name 16 | const nameTitle = el.__vueParentComponent?.props[componentToProps[name]] 17 | saveOriginTitle = nameTitle || el.innerText 18 | watch( 19 | () => language.value, 20 | () => { 21 | //element tag or component 22 | if (name?.startsWith('EL')) { 23 | //self cunstrom 24 | if (Object.keys(componentToProps).includes(name)) { 25 | const props = el.__vueParentComponent.props 26 | props[componentToProps[name]] = langTitle(saveOriginTitle) 27 | } else { 28 | el.innerText = langTitle(saveOriginTitle) 29 | } 30 | } else { 31 | //common tag such as div span output so on; 32 | if (el.__vnode?.type) { 33 | el.innerText = langTitle(saveOriginTitle) 34 | } 35 | } 36 | }, 37 | { immediate: true } 38 | ) 39 | } 40 | export default { 41 | mounted(el, binding) { 42 | checkPermission(el, binding) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/layout/sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 38 | 47 | -------------------------------------------------------------------------------- /ts-out-dir/src/permission.js: -------------------------------------------------------------------------------- 1 | import router from '@/router'; 2 | import { filterAsyncRouter, progressClose, progressStart } from '@/hooks/use-permission'; 3 | import { useBasicStore } from '@/store/basic'; 4 | import { userInfoReq } from '@/api/system.ts'; 5 | const whiteList = ['/login', '/404', '/401']; 6 | router.beforeEach(async (to) => { 7 | progressStart(); 8 | const basicStore = useBasicStore(); 9 | if (basicStore.token) { 10 | if (to.path === '/login') { 11 | return '/'; 12 | } 13 | else { 14 | if (!basicStore.getUserInfo) { 15 | try { 16 | const userData = await userInfoReq(); 17 | filterAsyncRouter(userData); 18 | basicStore.setUserInfo(userData); 19 | return { ...to, replace: true }; 20 | } 21 | catch (e) { 22 | console.error(`route permission error${e}`); 23 | basicStore.resetState(); 24 | progressClose(); 25 | return `/login?redirect=${to.path}`; 26 | } 27 | } 28 | else { 29 | return true; 30 | } 31 | } 32 | } 33 | else { 34 | if (!whiteList.includes(to.path)) { 35 | return `/login?redirect=${to.path}`; 36 | } 37 | else { 38 | return true; 39 | } 40 | } 41 | }); 42 | router.afterEach(() => { 43 | progressClose(); 44 | }); 45 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 40 | 46 | -------------------------------------------------------------------------------- /src/icons/common/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/demo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | //设置files为空,则不会自动扫描默认目录,也就是只会扫描include配置的目录 3 | "files": [], 4 | "compilerOptions": { 5 | "target": "esnext", 6 | "module": "esnext", 7 | //启用所有严格类型检查选项。 8 | //启用 --strict相当于启用 --noImplicitAny, --noImplicitThis, --alwaysStrict, --strictNullChecks和 --strictFunctionTypes和--strictPropertyInitialization。 9 | "strict": true, 10 | // 允许编译器编译JS,JSX文件 11 | "allowJs": false, 12 | // 允许在JS文件中报错,通常与allowJS一起使用 13 | "checkJs": false, 14 | // 允许使用jsx 15 | "jsx": "preserve", 16 | "declaration": true, 17 | //移除注解 18 | "removeComments": true, 19 | //不可以忽略any 20 | "noImplicitAny": false, 21 | //关闭 this 类型注解提示 22 | "noImplicitThis": true, 23 | //null/undefined不能作为其他类型的子类型: 24 | //let a: number = null; //这里会报错. 25 | "strictNullChecks": true, 26 | //生成枚举的映射代码 27 | "preserveConstEnums": true, 28 | //根目录 29 | //输出目录 30 | "outDir": "./ts-out-dir", 31 | //是否输出src2.js.map文件 32 | "sourceMap": false, 33 | //变量定义了但是未使用 34 | "noUnusedLocals": false, 35 | //是否允许把json文件当做模块进行解析 36 | "resolveJsonModule": true, 37 | //和noUnusedLocals一样,针对func 38 | "noUnusedParameters": false, 39 | // 模块解析策略,ts默认用node的解析策略,即相对的方式导入 40 | "moduleResolution": "node", 41 | //允许export=导出,由import from 导入 42 | "esModuleInterop": true, 43 | //忽略所有的声明文件( *.d.ts)的类型检查。 44 | "skipLibCheck": true, 45 | "baseUrl": ".", 46 | //指定默认读取的目录 47 | //"typeRoots": ["./node_modules/@types/", "./types"], 48 | "lib": ["ES2018", "DOM"] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 55 | -------------------------------------------------------------------------------- /typings/basic.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * 声明.d.ts文件规范 3 | * 导出的类型以大写开头 4 | * 对象:config 5 | * 数组:options 6 | * 枚举:emu 7 | * 函数:Fn 8 | * 属性:props 9 | * 实例:instance 10 | * */ 11 | 12 | /*router*/ 13 | import type { RouteRecordRaw } from 'vue-router' 14 | export interface rawConfig { 15 | hidden?: boolean 16 | alwaysShow?: boolean 17 | code?: number 18 | name?: string 19 | fullPath?: string 20 | path?: string 21 | meta?: { 22 | title: string 23 | icon?: string 24 | affix?: boolean 25 | activeMenu?: string 26 | breadcrumb?: boolean 27 | roles?: Array 28 | elSvgIcon?: string 29 | code?: number 30 | cachePage?: boolean 31 | leaveRmCachePage?: boolean 32 | closeTabRmCache?: boolean 33 | } 34 | children?: RouterOptions 35 | redirect?: string 36 | } 37 | export type RouteRawConfig = RouteRecordRaw & rawConfig 38 | export type RouterTypes = Array 39 | 40 | /*settings*/ 41 | export interface SettingsConfig { 42 | title: string 43 | sidebarLogo: boolean 44 | showLeftMenu: boolean 45 | ShowDropDown: boolean 46 | showHamburger: boolean 47 | isNeedLogin: boolean 48 | isNeedNprogress: boolean 49 | showTagsView: boolean 50 | tagsViewNum: number 51 | openProdMock: boolean 52 | errorLog: string | Array 53 | permissionMode: string 54 | delWindowHeight: string 55 | tmpToken: string 56 | showNavbarTitle: boolean 57 | showTopNavbar: boolean 58 | mainNeedAnimation: boolean 59 | viteBasePath: string 60 | defaultLanguage: string 61 | defaultSize: string 62 | defaultTheme: string 63 | plateFormId: number 64 | } 65 | 66 | export {} 67 | -------------------------------------------------------------------------------- /src/layout/app-main/component/ThemeSelect.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 40 | 41 | 53 | -------------------------------------------------------------------------------- /src/icons/common/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/mixins/function.scss: -------------------------------------------------------------------------------- 1 | @use 'config'; 2 | 3 | // getCssVarName('button', 'text-color') => '--el-button-text-color' 4 | @function getCssVarName($args...) { 5 | @return joinVarName($args); 6 | } 7 | 8 | // getCssVar('button', 'text-color') => var(--el-button-text-color) 9 | @function getCssVar($args...) { 10 | @return var(#{joinVarName($args)}); 11 | } 12 | 13 | @function selectorToString($selector) { 14 | $selector: inspect($selector); 15 | $selector: str-slice($selector, 2, -2); 16 | @return $selector; 17 | } 18 | 19 | @function containsModifier($selector) { 20 | $selector: selectorToString($selector); 21 | 22 | @if str-index($selector, config.$modifier-separator) { 23 | @return true; 24 | } @else { 25 | @return false; 26 | } 27 | } 28 | @function hitAllSpecialNestRule($selector) { 29 | @return containsModifier($selector) or containWhenFlag($selector) or containPseudoClass($selector); 30 | } 31 | @function containWhenFlag($selector) { 32 | $selector: selectorToString($selector); 33 | 34 | @if str-index($selector, '.' + config.$state-prefix) { 35 | @return true; 36 | } @else { 37 | @return false; 38 | } 39 | } 40 | @function containPseudoClass($selector) { 41 | $selector: selectorToString($selector); 42 | 43 | @if str-index($selector, ':') { 44 | @return true; 45 | } @else { 46 | @return false; 47 | } 48 | } 49 | 50 | // join var name 51 | // joinVarName(('button', 'text-color')) => '--el-button-text-color' 52 | @function joinVarName($list) { 53 | $name: '--' + config.$namespace; 54 | @each $item in $list { 55 | @if $item != '' { 56 | $name: $name + '-' + $item; 57 | } 58 | } 59 | @return $name; 60 | } 61 | -------------------------------------------------------------------------------- /ts-out-dir/src/utils/axios-req.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { ElMessage, ElMessageBox } from 'element-plus'; 3 | import { useBasicStore } from '@/store/basic'; 4 | const service = axios.create(); 5 | service.interceptors.request.use((req) => { 6 | const { token, axiosPromiseArr } = useBasicStore(); 7 | req.cancelToken = new axios.CancelToken((cancel) => { 8 | axiosPromiseArr.push({ 9 | url: req.url, 10 | cancel 11 | }); 12 | }); 13 | req.headers['AUTHORIZE_TOKEN'] = token; 14 | if ('get'.includes(req.method?.toLowerCase())) 15 | req.params = req.data; 16 | return req; 17 | }, (err) => { 18 | Promise.reject(err); 19 | }); 20 | service.interceptors.response.use((res) => { 21 | const { code } = res.data; 22 | const successCode = '0,200,20000'; 23 | const noAuthCode = '401,403'; 24 | if (successCode.includes(code)) { 25 | return res.data; 26 | } 27 | else { 28 | if (noAuthCode.includes(code) && !location.href.includes('/login')) { 29 | ElMessageBox.confirm('请重新登录', { 30 | confirmButtonText: '重新登录', 31 | cancelButtonText: '取消', 32 | type: 'warning' 33 | }).then(() => { 34 | useBasicStore().resetStateAndToLogin(); 35 | }); 36 | } 37 | return Promise.reject(res.data); 38 | } 39 | }, (err) => { 40 | ElMessage.error({ 41 | message: err, 42 | duration: 2 * 1000 43 | }); 44 | return Promise.reject(err); 45 | }); 46 | export default function axiosReq(config) { 47 | return service({ 48 | baseURL: import.meta.env.VITE_APP_BASE_URL, 49 | timeout: 8000, 50 | ...config 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /src/icons/common/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/setting-switch/index.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /src/views/setting-switch/SettingSwitch.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /src/theme/base/element-plus/var.scss: -------------------------------------------------------------------------------- 1 | /* Element Chalk Variables */ 2 | @use 'sass:math'; 3 | @use 'sass:map'; 4 | @use '../../mixins/function.scss' as *; 5 | 6 | // types 7 | $types: primary, success, warning, danger, error, info; 8 | 9 | // change color 10 | $colors: () !default; 11 | $colors: map.deep-merge( 12 | ( 13 | 'white': #ffffff, 14 | 'black': #000000, 15 | 'primary': ( 16 | 'base': #c72210//#409eff 17 | ), 18 | 'success': ( 19 | 'base': #45b207 20 | ), 21 | 'warning': ( 22 | 'base': #ec8828 23 | ), 24 | 'danger': ( 25 | 'base': #f56c6c 26 | ), 27 | 'error': ( 28 | 'base': #d24934 29 | ), 30 | 'info': ( 31 | 'base': #909399 32 | ) 33 | ), 34 | $colors 35 | ); 36 | 37 | $color-white: map.get($colors, 'white') !default; 38 | $color-black: map.get($colors, 'black') !default; 39 | $color-primary: map.get($colors, 'primary', 'base') !default; 40 | $color-success: map.get($colors, 'success', 'base') !default; 41 | $color-warning: map.get($colors, 'warning', 'base') !default; 42 | $color-danger: map.get($colors, 'danger', 'base') !default; 43 | $color-error: map.get($colors, 'error', 'base') !default; 44 | $color-info: map.get($colors, 'info', 'base') !default; 45 | 46 | //$colors添加 --el-color-primary-light-7 47 | @mixin set-color-mix-level($type, $number, $mode: 'light', $mix-color: $color-white) { 48 | $colors: map.deep-merge( 49 | ( 50 | $type: ( 51 | '#{$mode}-#{$number}': mix($mix-color, map.get($colors, $type, 'base'), math.percentage(math.div($number, 10))) 52 | ) 53 | ), 54 | $colors 55 | ) !global; 56 | } 57 | 58 | // $colors.primary.light-i 59 | @each $type in $types { 60 | @for $i from 1 through 9 { 61 | @include set-color-mix-level($type, $i, 'light', $color-white); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/theme/dark/element-plus/var.scss: -------------------------------------------------------------------------------- 1 | /* Element Chalk Variables */ 2 | @use 'sass:math'; 3 | @use 'sass:map'; 4 | @use '../../mixins/function.scss' as *; 5 | 6 | // types 7 | $types: primary, success, warning, danger, error, info; 8 | 9 | // change color 10 | $colors: () !default; 11 | $colors: map.deep-merge( 12 | ( 13 | 'white': #ffffff, 14 | 'black': #000000, 15 | 'primary': ( 16 | 'base': #c72210//#409eff 17 | ), 18 | 'success': ( 19 | 'base': #45b207 20 | ), 21 | 'warning': ( 22 | 'base': #ec8828 23 | ), 24 | 'danger': ( 25 | 'base': #f56c6c 26 | ), 27 | 'error': ( 28 | 'base': #d24934 29 | ), 30 | 'info': ( 31 | 'base': #909399 32 | ) 33 | ), 34 | $colors 35 | ); 36 | 37 | $color-white: map.get($colors, 'white') !default; 38 | $color-black: map.get($colors, 'black') !default; 39 | $color-primary: map.get($colors, 'primary', 'base') !default; 40 | $color-success: map.get($colors, 'success', 'base') !default; 41 | $color-warning: map.get($colors, 'warning', 'base') !default; 42 | $color-danger: map.get($colors, 'danger', 'base') !default; 43 | $color-error: map.get($colors, 'error', 'base') !default; 44 | $color-info: map.get($colors, 'info', 'base') !default; 45 | 46 | //$colors添加 --el-color-primary-light-7 47 | @mixin set-color-mix-level($type, $number, $mode: 'light', $mix-color: $color-white) { 48 | $colors: map.deep-merge( 49 | ( 50 | $type: ( 51 | '#{$mode}-#{$number}': mix($mix-color, map.get($colors, $type, 'base'), math.percentage(math.div($number, 10))) 52 | ) 53 | ), 54 | $colors 55 | ) !global; 56 | } 57 | 58 | // $colors.primary.light-i 59 | @each $type in $types { 60 | @for $i from 1 through 9 { 61 | @include set-color-mix-level($type, $i, 'light', $color-white); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/theme/lighting/element-plus/var.scss: -------------------------------------------------------------------------------- 1 | /* Element Chalk Variables */ 2 | @use 'sass:math'; 3 | @use 'sass:map'; 4 | @use '../../mixins/function.scss' as *; 5 | 6 | // types 7 | $types: primary, success, warning, danger, error, info; 8 | 9 | // change color 10 | $colors: () !default; 11 | $colors: map.deep-merge( 12 | ( 13 | 'white': #ffffff, 14 | 'black': #000000, 15 | 'primary': ( 16 | 'base': #c72210//#409eff 17 | ), 18 | 'success': ( 19 | 'base': #45b207 20 | ), 21 | 'warning': ( 22 | 'base': #ec8828 23 | ), 24 | 'danger': ( 25 | 'base': #f56c6c 26 | ), 27 | 'error': ( 28 | 'base': #d24934 29 | ), 30 | 'info': ( 31 | 'base': #909399 32 | ) 33 | ), 34 | $colors 35 | ); 36 | 37 | $color-white: map.get($colors, 'white') !default; 38 | $color-black: map.get($colors, 'black') !default; 39 | $color-primary: map.get($colors, 'primary', 'base') !default; 40 | $color-success: map.get($colors, 'success', 'base') !default; 41 | $color-warning: map.get($colors, 'warning', 'base') !default; 42 | $color-danger: map.get($colors, 'danger', 'base') !default; 43 | $color-error: map.get($colors, 'error', 'base') !default; 44 | $color-info: map.get($colors, 'info', 'base') !default; 45 | 46 | //$colors添加 --el-color-primary-light-7 47 | @mixin set-color-mix-level($type, $number, $mode: 'light', $mix-color: $color-white) { 48 | $colors: map.deep-merge( 49 | ( 50 | $type: ( 51 | '#{$mode}-#{$number}': mix($mix-color, map.get($colors, $type, 'base'), math.percentage(math.div($number, 10))) 52 | ) 53 | ), 54 | $colors 55 | ) !global; 56 | } 57 | 58 | // $colors.primary.light-i 59 | @each $type in $types { 60 | @for $i from 1 through 9 { 61 | @include set-color-mix-level($type, $i, 'light', $color-white); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/theme/china-red/element-plus/var.scss: -------------------------------------------------------------------------------- 1 | /* Element Chalk Variables */ 2 | @use 'sass:math'; 3 | @use 'sass:map'; 4 | @use '../../mixins/function.scss' as *; 5 | 6 | // types 7 | $types: primary, success, warning, danger, error, info; 8 | 9 | // change color 10 | $colors: () !default; 11 | $colors: map.deep-merge( 12 | ( 13 | 'white': #ffffff, 14 | 'black': #000000, 15 | 'primary': ( 16 | 'base': #c72210//#409eff 17 | ), 18 | 'success': ( 19 | 'base': #45b207 20 | ), 21 | 'warning': ( 22 | 'base': #ec8828 23 | ), 24 | 'danger': ( 25 | 'base': #f56c6c 26 | ), 27 | 'error': ( 28 | 'base': #d24934 29 | ), 30 | 'info': ( 31 | 'base': #909399 32 | ) 33 | ), 34 | $colors 35 | ); 36 | 37 | $color-white: map.get($colors, 'white') !default; 38 | $color-black: map.get($colors, 'black') !default; 39 | $color-primary: map.get($colors, 'primary', 'base') !default; 40 | $color-success: map.get($colors, 'success', 'base') !default; 41 | $color-warning: map.get($colors, 'warning', 'base') !default; 42 | $color-danger: map.get($colors, 'danger', 'base') !default; 43 | $color-error: map.get($colors, 'error', 'base') !default; 44 | $color-info: map.get($colors, 'info', 'base') !default; 45 | 46 | //$colors添加 --el-color-primary-light-7 47 | @mixin set-color-mix-level($type, $number, $mode: 'light', $mix-color: $color-white) { 48 | $colors: map.deep-merge( 49 | ( 50 | $type: ( 51 | '#{$mode}-#{$number}': mix($mix-color, map.get($colors, $type, 'base'), math.percentage(math.div($number, 10))) 52 | ) 53 | ), 54 | $colors 55 | ) !global; 56 | } 57 | 58 | // $colors.primary.light-i 59 | @each $type in $types { 60 | @for $i from 1 through 9 { 61 | @include set-color-mix-level($type, $i, 'light', $color-white); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/theme/mixins/_var.scss: -------------------------------------------------------------------------------- 1 | /*var mixin*/ 2 | @use 'sass:map'; 3 | 4 | @use 'config'; 5 | @use 'function' as *; 6 | 7 | // set css var value, because we need translate value to string 8 | // for example: 9 | // @include set-css-var-value(('color', 'primary'), red); 10 | // --el-color: red; 11 | // --el-$name-: $value; 12 | @mixin set-css-var-value($name, $value) { 13 | #{joinVarName($name)}: #{$value}; 14 | } 15 | 16 | @mixin set-css-color-type($colors, $type) { 17 | @include set-css-var-value(('color', $type), map.get($colors, $type, 'base')); 18 | @each $i in (3, 5, 7, 8, 9) { 19 | // --el-color-primary-light-7: #c6e2ff; 20 | @include set-css-var-value(('color', $type, 'light', $i), map.get($colors, $type, 'light-#{$i}')); 21 | } 22 | 23 | //@include set-css-var-value( 24 | // ('color', $type, 'dark-2'), 25 | // map.get($colors, $type, 'dark-2') 26 | //); 27 | } 28 | 29 | //el-$name-$attribute-$value 30 | @mixin set-component-css-var($name, $variables) { 31 | @each $attribute, $value in $variables { 32 | @if $attribute == 'default' { 33 | #{getCssVarName($name)}: #{$value}; 34 | } @else { 35 | #{getCssVarName($name, $attribute)}: #{$value}; 36 | } 37 | } 38 | } 39 | 40 | // --el-color-error-rgb: 245, 108, 108; 41 | // --el-color-$type-rgb: 245, 108, 108; 42 | @mixin set-css-color-rgb($colors, $type) { 43 | $color: map.get($colors, $type, 'base'); 44 | @include set-css-var-value(('color', $type, 'rgb'), #{red($color), green($color), blue($color)}); 45 | } 46 | 47 | // generate css var from existing css var 48 | // for example: 49 | // @include css-var-from-global(('button', 'text-color'), ('color', $type)) 50 | // --el-button-text-color: var(--el-color-#{$type}); 51 | @mixin css-var-from-global($var, $gVar) { 52 | $varName: joinVarName($var); 53 | $gVarName: joinVarName($gVar); 54 | #{$varName}: var(#{$gVarName}); 55 | } 56 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 30 | 31 | 68 | -------------------------------------------------------------------------------- /src/icons/nav-bar/theme-icon.svg: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-admin-ts 2 | 3 | the typescript version of vue3 admin template 4 | 5 | suggestion the Node.js >= v16.20 6 | 7 | [Recommended node](https://nodejs.org/download/release/v16.20.2/) 8 | 9 | 10 | ## Documents 11 | 12 | - [Official Documentation](https://github.jzfai.top/vue3-admin-doc/) 13 | 14 | - [中文官网](https://github.jzfai.top/vue3-admin-cn-doc/) 15 | 16 | 17 | 18 | ## Online experience 19 | 20 | [Access address](https://github.jzfai.top/vue3-admin-ts) 21 | 22 | [国内体验地址](https://github.jzfai.top/vue3-admin-ts) 23 | 24 | 25 | 26 | ## Related items 27 | 28 | The framework is available in js,ts, plus and electron versions 29 | - js version:[vue3-admin-template](https://github.com/jzfai/vue3-admin-template.git) -- basic version 30 | - ts version:[vue3-element-ts](https://github.com/jzfai/vue3-admin-ts.git) 31 | - ts version for plus:[vue3-element-plus](https://github.com/jzfai/vue3-admin-plus.git) 32 | - ts version for electron:[vue3-element-electron](https://github.com/jzfai/vue3-admin-electron.git) 33 | - java Micro-service background data:[micro-service-plus](https://github.com/jzfai/micro-service-plus) 34 | 35 | 36 | ## Build Setup 37 | 38 | ```bash 39 | # clone the project 40 | git clone https://github.com/jzfai/vue3-admin-ts.git 41 | 42 | # enter the project directory 43 | cd vue3-admin-ts 44 | 45 | # pnpm address https://pnpm.io/zh/motivation 46 | # install dependency(Recommend use pnpm) 47 | # you can use "npm -g i pnpm@7.9.0" to install pnpm 48 | pnpm i 49 | 50 | # develop 51 | pnpm run dev 52 | ``` 53 | 54 | 55 | ## Build 56 | 57 | ```bash 58 | # build for test environment 59 | pnpm run build-test 60 | 61 | # build for production environment 62 | pnpm run build 63 | ``` 64 | 65 | ## Others 66 | 67 | ```bash 68 | # preview the release environment effect 69 | pnpm run preview 70 | 71 | # code format check 72 | pnpm run lint 73 | 74 | ``` 75 | 76 | 77 | ## Browsers support 78 | 79 | Note: Vue3 is not supported the Internet Explorer 80 | 81 | 82 | ## Discussion and Communication 83 | [WeChat group](https://github.jzfai.top/file/images/wx-groud.png) 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/Children.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 77 | -------------------------------------------------------------------------------- /src/store/tags-view.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import setting from '@/settings' 3 | export const useTagsViewStore = defineStore('tagsView', { 4 | state: () => { 5 | return { 6 | visitedViews: [] //tag标签数组 7 | } 8 | }, 9 | actions: { 10 | addVisitedView(view) { 11 | this.$patch((state: any) => { 12 | //判断添加的标签存在直接返回 13 | if (state.visitedViews.some((v) => v.path === view.path)) return 14 | //添加的数量如果大于 setting.tagsViewNum,则替换最后一个元素,否则在visitedViews数组后插入一个元素 15 | if (state.visitedViews.length >= setting.tagsViewNum) { 16 | state.visitedViews.pop() 17 | state.visitedViews.push( 18 | Object.assign({}, view, { 19 | title: view.meta.title || 'no-name' 20 | }) 21 | ) 22 | } else { 23 | state.visitedViews.push( 24 | Object.assign({}, view, { 25 | title: view.meta.title || 'no-name' 26 | }) 27 | ) 28 | } 29 | }) 30 | }, 31 | delVisitedView(view) { 32 | return new Promise((resolve) => { 33 | this.$patch((state: any) => { 34 | //匹配view.path元素将其删除 35 | for (const [i, v] of state.visitedViews.entries()) { 36 | if (v.path === view.path) { 37 | state.visitedViews.splice(i, 1) 38 | break 39 | } 40 | } 41 | resolve([...state.visitedViews]) 42 | }) 43 | }) 44 | }, 45 | delOthersVisitedViews(view) { 46 | return new Promise((resolve) => { 47 | this.$patch((state) => { 48 | state.visitedViews = state.visitedViews.filter((v: ObjKeys) => { 49 | return v.meta.affix || v.path === view.path 50 | }) 51 | resolve([...state.visitedViews]) 52 | }) 53 | }) 54 | }, 55 | delAllVisitedViews() { 56 | return new Promise((resolve) => { 57 | this.$patch((state) => { 58 | // keep affix tags 59 | state.visitedViews = state.visitedViews.filter((tag: ObjKeys) => tag.meta?.affix) 60 | resolve([...state.visitedViews]) 61 | }) 62 | }) 63 | } 64 | } 65 | }) 66 | -------------------------------------------------------------------------------- /src/icons/common/shopping.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ts-out-dir/src/utils/common-util.js: -------------------------------------------------------------------------------- 1 | export default { 2 | getWeek() { 3 | return `星期${'日一二三四五六'.charAt(new Date().getDay())}`; 4 | }, 5 | mobilePhone(str) { 6 | const reg = /^0?1[0-9]{10}$/; 7 | return reg.test(str); 8 | }, 9 | toSplitNumFor(num, numToSpace) { 10 | return num.replace(/(.{4})/g, '$1 '); 11 | }, 12 | bankCardNo(str) { 13 | const reg = /^\d{15,20}$/; 14 | return reg.test(str); 15 | }, 16 | regEmail(str) { 17 | const reg = /^([a-zA-Z]|[0-9])(\w|-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/; 18 | return reg.test(str); 19 | }, 20 | idCardNumber(str) { 21 | const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/; 22 | return reg.test(str); 23 | }, 24 | deleteArrItem(arr, arrItem) { 25 | arr.splice(arr.indexOf(arrItem), 1); 26 | }, 27 | arrToRepeat(arr) { 28 | return arr.filter((ele, index, thisArr) => { 29 | return thisArr.indexOf(ele) === index; 30 | }); 31 | }, 32 | deRepeatArr(seriesArr) { 33 | return [...new Set(seriesArr)]; 34 | }, 35 | byArrObjDeleteArrObj2(arrObj, arrObj2, objKey) { 36 | arrObj 37 | .map((value) => { 38 | return value[objKey]; 39 | }) 40 | .forEach((value2) => { 41 | arrObj2.splice(arrObj2.findIndex((item) => item[objKey] === value2), 1); 42 | }); 43 | return arrObj2; 44 | }, 45 | deleteArrObjByKey(arrObj, objKey, value) { 46 | arrObj.splice(arrObj.findIndex((item) => item[objKey] === value), 1); 47 | return arrObj; 48 | }, 49 | findArrObjByKey(arrObj, objKey, value) { 50 | return arrObj[arrObj.findIndex((item) => item[objKey] == value)]; 51 | }, 52 | byArrObjFindArrObj2(arrObj, arrObj2, objKey) { 53 | const arrObj3 = []; 54 | arrObj 55 | .map((value) => { 56 | return value[objKey]; 57 | }) 58 | .forEach((value2) => { 59 | const arrIndex = arrObj2.findIndex((item) => item[objKey] === value2); 60 | if (arrIndex !== -1) { 61 | arrObj3.push(arrObj2[arrIndex]); 62 | } 63 | }); 64 | return arrObj3; 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /src/icons/common/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/theme/base/custom/ct-css-vars.scss: -------------------------------------------------------------------------------- 1 | html.base-theme { 2 | /*element-plus section */ 3 | --el-menu-active-color: #409eff; 4 | --el-menu-text-color: #bfcbd9; 5 | --el-menu-hover-text-color: var(--el-color-primary); 6 | --el-menu-bg-color: #304156; 7 | --el-menu-hover-bg-color: #263445; 8 | --el-menu-item-height: 56px; 9 | --el-menu-border-color: none; 10 | /*layout section*/ 11 | //layout 12 | --layout-border-left-color: #ddd; 13 | //Breadcrumb 14 | --breadcrumb-no-redirect: #97a8be; 15 | //Hamburger 16 | --hamburger-color: #2b2f3a; 17 | --hamburger-width: 20px; 18 | --hamburger-height: 20px; 19 | //Sidebar 20 | --sidebar-el-icon-size: 20px; 21 | --sidebar-logo-background: #2b2f3a; 22 | --sidebar-logo-color: #ff9901; 23 | --sidebar-logo-width: 32px; 24 | --sidebar-logo-height: 32px; 25 | --sidebar-logo-title-color: #fff; 26 | --side-bar-width: 210px; 27 | --side-bar-border-right-color: #eee; 28 | //TagsView 29 | --tags-view-background: #fff; 30 | --tags-view-border-bottom: #eee; 31 | --tags-view-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); 32 | --tags-view-item-background: #fff; 33 | --tags-view-item-border-color: #d8dce5; 34 | --tags-view-item-color: #495060; 35 | --tag-view-height: 32px; 36 | --tags-view-item-active-background: #42b983; 37 | --tags-view-item-active-color: #fff; 38 | --tags-view-item-active-border-color: #42b983; 39 | --tags-view-contextmenu-background: #fff; 40 | --tags-view-contextmenu-color: #333; 41 | --tags-view-contextmenu-box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); 42 | --tags-view-contextmenu-hover-background: #eee; 43 | //close-icon 44 | --tags-view-close-icon-hover-background: #b4bccc; 45 | --tags-view-close-icon-hover-color: #fff; 46 | //AppMain.vue 47 | --app-main-padding: 20px; 48 | --app-main-background: #fff; 49 | //Navbar.vue 50 | --nav-bar-height: 50px; 51 | --nav-bar-background: #fff; 52 | --nav-bar-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 53 | --nav-bar-right-menu-background: #fff; 54 | 55 | //transition 动画 56 | //侧边栏切换动画时长 57 | --sideBar-switch-duration: 0.2s; 58 | //logo切换动画时长 59 | --logo-switch-duration: 1s; 60 | //页面动画时长 61 | --page-transform-duration: 0.2s; 62 | //面包屑导航动画时长 63 | --breadcrumb-change-duration: 0.2s; 64 | 65 | //进度条颜色 66 | --pregress-bar-color: transparent; 67 | } 68 | -------------------------------------------------------------------------------- /ts-out-dir/src/store/tagsView.js: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import setting from '@/settings'; 3 | export const useTagsViewStore = defineStore('tagsView', { 4 | state: () => { 5 | return { 6 | visitedViews: [] 7 | }; 8 | }, 9 | actions: { 10 | addVisitedView(view) { 11 | this.$patch((state) => { 12 | if (state.visitedViews.some((v) => v.path === view.path)) 13 | return; 14 | if (state.visitedViews.length >= setting.tagsViewNum) { 15 | state.visitedViews.pop(); 16 | state.visitedViews.push(Object.assign({}, view, { 17 | title: view.meta.title || 'no-name' 18 | })); 19 | } 20 | else { 21 | state.visitedViews.push(Object.assign({}, view, { 22 | title: view.meta.title || 'no-name' 23 | })); 24 | } 25 | }); 26 | }, 27 | delVisitedView(view) { 28 | return new Promise((resolve) => { 29 | this.$patch((state) => { 30 | for (const [i, v] of state.visitedViews.entries()) { 31 | if (v.path === view.path) { 32 | state.visitedViews.splice(i, 1); 33 | break; 34 | } 35 | } 36 | resolve([...state.visitedViews]); 37 | }); 38 | }); 39 | }, 40 | delOthersVisitedViews(view) { 41 | return new Promise((resolve) => { 42 | this.$patch((state) => { 43 | state.visitedViews = state.visitedViews.filter((v) => { 44 | return v.meta.affix || v.path === view.path; 45 | }); 46 | resolve([...state.visitedViews]); 47 | }); 48 | }); 49 | }, 50 | delAllVisitedViews() { 51 | return new Promise((resolve) => { 52 | this.$patch((state) => { 53 | state.visitedViews = state.visitedViews.filter((tag) => tag.meta?.affix); 54 | resolve([...state.visitedViews]); 55 | }); 56 | }); 57 | } 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /src/theme/lighting/custom/ct-css-vars.scss: -------------------------------------------------------------------------------- 1 | html.lighting-theme { 2 | /*element-plus section */ 3 | //--el-menu-active-color: #409eff; 4 | //--el-menu-text-color: #bfcbd9; 5 | //--el-menu-hover-text-color: var(--el-color-primary); 6 | //--el-menu-bg-color: #304156; 7 | //--el-menu-hover-bg-color: #263445; 8 | //--el-menu-item-height: 56px; 9 | --el-menu-border-color: none; 10 | /*layout section*/ 11 | //layout 12 | --layout-border-left-color: #ddd; 13 | //Breadcrumb 14 | --breadcrumb-no-redirect: #97a8be; 15 | //Hamburger 16 | --hamburger-color: #2b2f3a; 17 | --hamburger-width: 20px; 18 | --hamburger-height: 20px; 19 | //Sidebar 20 | --sidebar-el-icon-size: 20px; 21 | --sidebar-logo-background: #fff; 22 | --sidebar-logo-color: #ff9901; 23 | --sidebar-logo-width: 32px; 24 | --sidebar-logo-height: 32px; 25 | --sidebar-logo-title-color: #2b2f3a; 26 | --side-bar-width: 210px; 27 | --side-bar-border-right-color: #eee; 28 | //TagsView 29 | --tags-view-background: #fff; 30 | --tags-view-border-bottom: #d8dce5; 31 | --tags-view-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); 32 | --tags-view-item-background: #fff; 33 | --tags-view-item-border-color: #d8dce5; 34 | --tags-view-item-color: #495060; 35 | --tag-view-height: 32px; 36 | --tags-view-item-active-background: #42b983; 37 | --tags-view-item-active-color: #fff; 38 | --tags-view-item-active-border-color: #42b983; 39 | --tags-view-contextmenu-background: #fff; 40 | --tags-view-contextmenu-color: #333; 41 | --tags-view-contextmenu-box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); 42 | --tags-view-contextmenu-hover-background: #eee; 43 | //close-icon 44 | --tags-view-close-icon-hover-background: #b4bccc; 45 | --tags-view-close-icon-hover-color: #fff; 46 | //AppMain.vue 47 | --app-main-padding: 20px; 48 | --app-main-background: #fff; 49 | //Navbar.vue 50 | --nav-bar-height: 50px; 51 | --nav-bar-background: #fff; 52 | --nav-bar-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 53 | --nav-bar-right-menu-background: #fff; 54 | 55 | //transition 动画 56 | //侧边栏切换动画时长 57 | --sideBar-switch-duration: 0.2s; 58 | //logo切换动画时长 59 | --logo-switch-duration: 0.5s; 60 | //页面动画时长 61 | --page-transform-duration: 0.2s; 62 | //面包屑导航动画时长 63 | --breadcrumb-change-duration: 0.2s; 64 | 65 | //进度条颜色 66 | --pregress-bar-color: transparent; 67 | } 68 | -------------------------------------------------------------------------------- /src/layout/sidebar/Logo.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | 39 | 79 | -------------------------------------------------------------------------------- /src/theme/dark/custom/ct-css-vars.scss: -------------------------------------------------------------------------------- 1 | html.dark { 2 | /*element-plus section */ 3 | --el-menu-active-color: #409eff; 4 | --el-menu-text-color: #bfcbd9; 5 | --el-menu-hover-text-color: var(--el-color-primary); 6 | --el-menu-bg-color: #304156; 7 | --el-menu-hover-bg-color: #263445; 8 | --el-menu-item-height: 56px; 9 | --el-menu-border-color: none; 10 | /*layout section*/ 11 | //layout 12 | --layout-border-left-color: #ddd; 13 | //Breadcrumb 14 | --breadcrumb-no-redirect: #97a8be; 15 | //Hamburger 16 | --hamburger-color: #fff; 17 | --hamburger-width: 20px; 18 | --hamburger-height: 20px; 19 | --el-text-color-primary: #fff; 20 | //Sidebar 21 | --sidebar-el-icon-size: 20px; 22 | --sidebar-logo-background: #2b2f3a; 23 | --sidebar-logo-color: #ff9901; 24 | --sidebar-logo-width: 32px; 25 | --sidebar-logo-height: 32px; 26 | --sidebar-logo-title-color: #fff; 27 | --side-bar-width: 210px; 28 | --side-bar-border-right-color: #2b2f3a; 29 | //TagsView 30 | --tags-view-background: #304156; 31 | --tags-view-border-bottom: #2b2f3a; 32 | --tags-view-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04); 33 | --tags-view-item-background: #fff; 34 | --tags-view-item-border-color: #d8dce5; 35 | --tags-view-item-color: #495060; 36 | --tag-view-height: 32px; 37 | --tags-view-item-active-background: #42b983; 38 | --tags-view-item-active-color: #fff; 39 | --tags-view-item-active-border-color: #42b983; 40 | --tags-view-contextmenu-background: #fff; 41 | --tags-view-contextmenu-color: #333; 42 | --tags-view-contextmenu-box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3); 43 | --tags-view-contextmenu-hover-background: #eee; 44 | //close-icon 45 | --tags-view-close-icon-hover-background: #b4bccc; 46 | --tags-view-close-icon-hover-color: #fff; 47 | //AppMain.vue 48 | --app-main-padding: 0px; 49 | --app-main-background: #304156; 50 | //Navbar.vue 51 | --nav-bar-height: 50px; 52 | --nav-bar-background: #2b2f3a; 53 | --nav-bar-box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 54 | --nav-bar-right-menu-background: #2b2f3a; 55 | 56 | //transition 动画 57 | //侧边栏切换动画时长 58 | --sideBar-switch-duration: 0.2s; 59 | //logo切换动画时长 60 | --logo-switch-duration: 1s; 61 | //页面动画时长 62 | --page-transform-duration: 0.2s; 63 | //面包屑导航动画时长 64 | --breadcrumb-change-duration: 0.2s; 65 | 66 | //进度条颜色 67 | --pregress-bar-color: transparent; 68 | } 69 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 39 | 52 | --------------------------------------------------------------------------------