├── .editorconfig ├── .env.build ├── .env.build-test ├── .env.serve-dev ├── .env.serve-test ├── .eslintignore ├── .eslintrc-auto-import.json ├── .eslintrc.json ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .npmrc ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── .yarnrc ├── App.vue ├── README.md ├── auto-imports.d.ts ├── build ├── afterPack.js ├── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico └── script │ └── installer.nsh ├── electron-main.ts ├── electron ├── html │ └── renderer2.html ├── main │ ├── MainRendererComm.js │ ├── globalShortcut.js │ ├── menu.js │ └── tray.js ├── renderer │ └── renderer2.js ├── static │ ├── empty.ico │ ├── favicon2.ico │ └── lover.png └── utils │ ├── commentUtils.js │ └── node-fs.js ├── eslintrc ├── .eslintrc-auto-import.json └── eslint-config.js ├── index.html ├── loading.html ├── main.ts ├── mock-prod-server.ts ├── mock ├── example.ts └── system.ts ├── nedbStore.db ├── nodemon.json ├── optimize-include.ts ├── package.json ├── public └── favicon.ico ├── settings.ts ├── src ├── App.vue ├── api │ └── system.ts ├── assets │ ├── 401_images │ │ └── 401.gif │ ├── 404_images │ │ ├── 404.png │ │ └── 404_cloud.png │ └── gif │ │ └── dianchi.gif ├── components │ ├── ElSvgIcon.vue │ ├── TestUnit.vue │ └── __tests__ │ │ └── el-svgIcon.test.jsx ├── directives │ ├── button-codes.ts │ ├── codes-permission.ts │ ├── index.ts │ ├── lang.ts │ └── roles-permission.ts ├── hooks │ ├── use-common.ts │ ├── use-element.ts │ ├── use-error-log.ts │ ├── use-layout.ts │ ├── use-permission.ts │ ├── use-self-router.ts │ └── use-table.ts ├── icons │ ├── SvgIcon.vue │ ├── common │ │ ├── 404.svg │ │ ├── bug.svg │ │ ├── chart.svg │ │ ├── clipboard.svg │ │ ├── component.svg │ │ ├── dashboard.svg │ │ ├── demo.svg │ │ ├── documentation.svg │ │ ├── drag.svg │ │ ├── edit.svg │ │ ├── education.svg │ │ ├── email.svg │ │ ├── example.svg │ │ ├── excel.svg │ │ ├── exit-fullscreen.svg │ │ ├── eye-open.svg │ │ ├── eye.svg │ │ ├── form.svg │ │ ├── fullscreen.svg │ │ ├── guide.svg │ │ ├── hamburger.svg │ │ ├── icon.svg │ │ ├── international.svg │ │ ├── language.svg │ │ ├── link.svg │ │ ├── list.svg │ │ ├── lock.svg │ │ ├── message.svg │ │ ├── money.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── pdf.svg │ │ ├── people.svg │ │ ├── peoples.svg │ │ ├── qq.svg │ │ ├── search.svg │ │ ├── shopping.svg │ │ ├── sidebar-logo.svg │ │ ├── size.svg │ │ ├── skill.svg │ │ ├── star.svg │ │ ├── tab.svg │ │ ├── table.svg │ │ ├── theme.svg │ │ ├── tree-table.svg │ │ ├── tree.svg │ │ ├── user.svg │ │ ├── wechat.svg │ │ └── zip.svg │ └── nav-bar │ │ ├── dashboard.svg │ │ ├── example.svg │ │ ├── eye-open.svg │ │ ├── eye.svg │ │ ├── form.svg │ │ ├── language.svg │ │ ├── link.svg │ │ ├── nested.svg │ │ ├── password.svg │ │ ├── table.svg │ │ ├── theme-icon.svg │ │ ├── tree.svg │ │ └── user.svg ├── lang │ ├── en.ts │ ├── index.ts │ └── zh.ts ├── layout │ ├── app-main │ │ ├── Breadcrumb.vue │ │ ├── Hamburger.vue │ │ ├── Navbar.vue │ │ ├── TagsView.vue │ │ └── index.vue │ ├── index.vue │ └── sidebar │ │ ├── Link.vue │ │ ├── Logo.vue │ │ ├── MenuIcon.vue │ │ ├── SidebarItem.vue │ │ └── index.vue ├── lib │ ├── el-svg-icon.ts │ └── element-plus.ts ├── main.ts ├── mock-prod-server.ts ├── permission.ts ├── plugins │ └── vite-plugin-setup-extend │ │ └── index.ts ├── router │ ├── index.ts │ └── modules │ │ ├── basic-demo.ts │ │ └── electron.ts ├── settings.ts ├── store │ ├── basic.ts │ ├── config.ts │ └── tags-view.ts ├── styles │ ├── index.scss │ ├── init-loading.css │ ├── reset-elemenet-plus-style.scss │ ├── scss-suger.scss │ └── transition.scss ├── theme │ ├── base │ │ ├── custom │ │ │ └── ct-css-vars.scss │ │ ├── element-plus │ │ │ ├── button.scss │ │ │ ├── checkbox.scss │ │ │ ├── css-vars.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ └── var.scss │ │ └── index.scss │ ├── china-red │ │ ├── custom │ │ │ └── ct-css-vars.scss │ │ ├── element-plus │ │ │ ├── button.scss │ │ │ ├── checkbox.scss │ │ │ ├── css-vars.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ └── var.scss │ │ └── index.scss │ ├── dark │ │ ├── custom │ │ │ └── ct-css-vars.scss │ │ ├── element-plus │ │ │ ├── button.scss │ │ │ ├── checkbox.scss │ │ │ ├── css-vars.css │ │ │ ├── css-vars.css.map │ │ │ ├── css-vars.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ └── var.scss │ │ └── index.scss │ ├── index.scss │ ├── lighting │ │ ├── custom │ │ │ └── ct-css-vars.scss │ │ ├── element-plus │ │ │ ├── button.scss │ │ │ ├── checkbox.scss │ │ │ ├── css-vars.css │ │ │ ├── css-vars.css.map │ │ │ ├── css-vars.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── redio.scss │ │ │ ├── table.scss │ │ │ └── var.scss │ │ └── index.scss │ ├── mixins │ │ ├── _var.scss │ │ ├── config.scss │ │ ├── function.scss │ │ └── mixins.scss │ └── utils │ │ ├── change-theme.ts │ │ └── index.ts ├── utils │ ├── axios-req.ts │ ├── bus.ts │ └── common-util.ts └── views │ ├── basic-demo │ ├── hook │ │ └── index.vue │ ├── keep-alive │ │ ├── index.vue │ │ ├── second-child.vue │ │ ├── tab-keep-alive.vue │ │ ├── third-child.vue │ │ ├── third-children │ │ │ ├── SecondChildren.vue │ │ │ └── ThirdChildren.vue │ │ └── third-keep-alive.vue │ ├── mock │ │ └── index.vue │ ├── parent-children │ │ ├── Children.vue │ │ ├── SubChildren.vue │ │ └── index.vue │ ├── pinia │ │ └── index.vue │ ├── svg-icon │ │ └── index.vue │ ├── vue3-template │ │ └── Vue3Template.vue │ └── worker │ │ └── index.vue │ ├── dashboard │ └── index.vue │ ├── electron │ ├── ElectronDemo.vue │ ├── ElectronDemoBak.vue │ ├── FsExtra.vue │ ├── IndexDbDemo.vue │ ├── MainRendererComm.vue │ ├── NedbDemo.vue │ └── NotifyNetListen.vue │ ├── error-page │ ├── 401.vue │ └── 404.vue │ ├── login │ └── index.vue │ ├── nested │ ├── menu1 │ │ ├── index.vue │ │ ├── menu1-1 │ │ │ └── index.vue │ │ ├── menu1-2 │ │ │ ├── index.vue │ │ │ ├── menu1-2-1 │ │ │ │ └── index.vue │ │ │ └── menu1-2-2 │ │ │ │ └── index.vue │ │ └── menu1-3 │ │ │ └── index.vue │ └── menu2 │ │ └── index.vue │ ├── redirect │ └── index.tsx │ └── setting-switch │ ├── SettingSwitch.vue │ └── index.vue ├── tsconfig.base.json ├── tsconfig.json ├── typings ├── auto-imports.d.ts ├── basic.d.ts ├── common.d.ts ├── components.d.ts ├── env.d.ts ├── global.d.ts └── shims-vue.d.ts ├── vite.config.ts ├── vitest.config.ts └── vitest.setup.ts /.editorconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/.editorconfig -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | public 2 | node_modules 3 | .history 4 | .husky 5 | dist 6 | *.d.ts 7 | -------------------------------------------------------------------------------- /.eslintrc-auto-import.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "axiosReq": true, 4 | "computed": true, 5 | "createApp": true, 6 | "createLogger": true, 7 | "createNamespacedHelpers": true, 8 | "createStore": true, 9 | "customRef": true, 10 | "defineAsyncComponent": true, 11 | "defineComponent": true, 12 | "effectScope": true, 13 | "EffectScope": true, 14 | "getCurrentInstance": true, 15 | "getCurrentScope": true, 16 | "h": true, 17 | "inject": true, 18 | "isReadonly": true, 19 | "isRef": true, 20 | "mapActions": true, 21 | "mapGetters": true, 22 | "mapMutations": true, 23 | "mapState": true, 24 | "markRaw": true, 25 | "nextTick": true, 26 | "onActivated": true, 27 | "onBeforeMount": true, 28 | "onBeforeUnmount": true, 29 | "onBeforeUpdate": true, 30 | "onDeactivated": true, 31 | "onErrorCaptured": true, 32 | "onMounted": true, 33 | "onRenderTracked": true, 34 | "onRenderTriggered": true, 35 | "onScopeDispose": true, 36 | "onServerPrefetch": true, 37 | "onUnmounted": true, 38 | "onUpdated": true, 39 | "provide": true, 40 | "reactive": true, 41 | "readonly": true, 42 | "ref": true, 43 | "resolveComponent": true, 44 | "shallowReactive": true, 45 | "shallowReadonly": true, 46 | "shallowRef": true, 47 | "toRaw": true, 48 | "toRef": true, 49 | "toRefs": true, 50 | "triggerRef": true, 51 | "unref": true, 52 | "useAttrs": true, 53 | "useCommon": true, 54 | "useCssModule": true, 55 | "useCssVars": true, 56 | "useElement": true, 57 | "useRoute": true, 58 | "useRouter": true, 59 | "useSlots": true, 60 | "useStore": true, 61 | "useVueRouter": true, 62 | "watch": true, 63 | "watchEffect": true 64 | } 65 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": ["./eslintrc/eslint-config.js", "./eslintrc/.eslintrc-auto-import.json"] 4 | } 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | 4 | ###aliyun address 5 | registry = https://registry.npmmirror.com 6 | 7 | 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar", "esbenp.prettier-vscode","dbaeumer.vscode-eslint"] 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "npm.packageManager": "yarn" 4 | } 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | registry "https://registry.npm.taobao.org" 2 | 3 | sass_binary_site "https://npm.taobao.org/mirrors/node-sass/" 4 | phantomjs_cdnurl "http://cnpmjs.org/downloads" 5 | electron_mirror "https://npm.taobao.org/mirrors/electron/" 6 | sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/" 7 | profiler_binary_host_mirror "https://npm.taobao.org/mirrors/node-inspector/" 8 | chromedriver_cdnurl "https://cdn.npm.taobao.org/dist/chromedriver" 9 | -------------------------------------------------------------------------------- /App.vue: -------------------------------------------------------------------------------- 1 | 6 | 40 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-admin-electron 2 | 3 | > This is a basic vue3 admin electron desktop platform. Contains the most basic electron development and construction steps and the use of demo, a set of codes can be packaged for win, mac, linux platform applications at the same time. 4 | 5 | A new generation Cross-desktop framework using electron13+vue3(setup-script)+vite2+element-plus ,It's fast! 6 | 7 | Use eslint+prettier+gitHooks format and verification code to improve code standardization and development efficiency 8 | 9 | 10 | 11 | ## Related items 12 | 13 | The framework is available in js, ts, plus and electron versions 14 | - electron version: [vue3-admin-electron](https://github.com/jzfai/vue3-admin-electron.git) 15 | - js version:[vue3-element-admin](https://github.com/jzfai/vue3-admin-electron.git) 16 | - ts version:[vue3-element-ts](https://github.com/jzfai/vue3-admin-ts.git) 17 | - js version for plus:[vue3-element-plus](https://github.com/jzfai/vue3-admin-plus.git) 18 | - java Micro-service background data:[micro-service-single](https://github.com/jzfai/micro-service-single) 19 | > development and experience:two words Really fragrant!!!!! 20 | 21 | ## Build Setup 22 | 23 | ```bash 24 | git clone https://github.com/jzfai/vue3-admin-electron.git 25 | 26 | cd vue3-admin-electron 27 | 28 | # install dependency(Recommend use yarn) 29 | yarn 30 | 31 | yarn run electron:dev 32 | ``` 33 | 34 | ## Build 35 | 36 | ```bash 37 | # build for production environment 38 | yarn run electron:build 39 | > Note: The packaged exe is packaged with the windows system, and the packaged dmg is packaged with the mac system. Separate as much as possible 40 | ``` 41 | 42 | ## Others 43 | 44 | ```bash 45 | # code format check 46 | yarn run lint 47 | 48 | ``` 49 | 50 | ## Extra 51 | 52 | Architecture development is not easy. If you feel good, please give me a compliment. The architecture is still being improved. Welcome to join me in development and become Contributors together! ! ! ! 53 | 54 | 55 | ## License 56 | 57 | [MIT](https://github.com/jzfai/vue3-admin-electron/blob/master/LICENSE) license. 58 | 59 | Copyright (c) 2023-present kuanghua 60 | -------------------------------------------------------------------------------- /build/afterPack.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | async function afterPack(context) { 4 | // 删除 README 文件,使其不加入 Setup 包中。 5 | let readmePath = path.join(context.appOutDir, 'resources/app.asar.unpacked/README.md') 6 | if (fs.existsSync(readmePath)) { 7 | fs.unlinkSync(readmePath) 8 | } 9 | } 10 | module.exports = afterPack 11 | -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/build/icons/icon.icns -------------------------------------------------------------------------------- /build/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/build/icons/icon.ico -------------------------------------------------------------------------------- /build/script/installer.nsh: -------------------------------------------------------------------------------- 1 | ; Script generated by the HM NIS Edit Script Wizard. 2 | 3 | ; HM NIS Edit Wizard helper defines custom install default dir 4 | !macro preInit 5 | SetRegView 64 6 | WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\electron-builder-start-exe" 7 | WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\electron-builder-start-exe" 8 | SetRegView 32 9 | WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\electron-builder-start-exe" 10 | WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\electron-builder-start-exe" 11 | !macroend -------------------------------------------------------------------------------- /electron-main.ts: -------------------------------------------------------------------------------- 1 | const { BrowserWindow, app } = require('electron') 2 | const isDev = require('electron-is-dev') 3 | const createWindow = () => { 4 | const mainWindow = new BrowserWindow({ 5 | width: 1280, 6 | height: 800, 7 | webPreferences: { 8 | nodeIntegration: true, //渲染进程中使用nodejs 9 | contextIsolation: false, 10 | enableRemoteModule: true //渲染线程中使用remote模块 11 | } 12 | }) 13 | //dev development 14 | const waitOn = require('wait-on') 15 | if (isDev) { 16 | mainWindow.loadFile('loading.html') 17 | // wait for http://localhost:5006 to load 18 | // detail to using look https://github.com/jeffbski/wait-on 19 | const opts = { 20 | resources: ['http://127.0.0.1:5008/index.html'], 21 | delay: 1000, // initial delay in ms, default 0 22 | timeout: 6000 // timeout in ms, default Infinity 23 | } 24 | waitOn(opts, (err) => { 25 | if (err) { 26 | console.error(err) 27 | return 28 | } 29 | mainWindow.loadURL('http://localhost:5008') 30 | //open devTools 31 | mainWindow.webContents.openDevTools({ mode: 'bottom' }) 32 | }) 33 | } else { 34 | mainWindow.loadFile('dist/index.html') 35 | } 36 | //开启remote 37 | require('@electron/remote/main').initialize() 38 | require('@electron/remote/main').enable(mainWindow.webContents) 39 | //引入相应的主线程 40 | require('./electron/main/MainRendererComm') 41 | //import menu 42 | //require('./electron/main/menu') 43 | //import tray 44 | require('./electron/main/tray') 45 | //import tray 46 | require('./electron/main/globalShortcut') 47 | } 48 | app.on('ready', createWindow) 49 | //监听窗口关闭的事件,关闭的时候退出应用,macOs 需要排除(mac相关) 50 | app.on('window-all-closed', () => { 51 | if (process.platform !== 'darwin') { 52 | app.quit() 53 | } 54 | }) 55 | //Macos 中点击 dock 中的应用图标的时候重新创建窗口 56 | app.on('activate', () => { 57 | if (BrowserWindow.getAllWindows().length === 0) { 58 | createWindow() 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /electron/html/renderer2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |

renderer2页面

10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /electron/main/MainRendererComm.js: -------------------------------------------------------------------------------- 1 | const { BrowserWindow, ipcMain } = require('electron') 2 | const path = require('path') 3 | 4 | //first way 5 | ipcMain.on('send1', (event, args) => { 6 | console.log(args) 7 | event.sender.send('reply1', 'reply1') 8 | }) 9 | 10 | //send way(sync) 11 | ipcMain.on('send2', (event) => { 12 | event.returnValue = 'send2' 13 | }) 14 | 15 | //three way 16 | let renderer1Id = null 17 | ipcMain.on('renderer2', () => { 18 | //在打开renderer2窗口前保存renderer1的id 19 | renderer1Id = BrowserWindow.getFocusedWindow().id 20 | const newsWindow = new BrowserWindow({ 21 | width: 800, 22 | height: 500, 23 | webPreferences: { 24 | nodeIntegration: true, //开启渲染进程中使用nodejs 25 | contextIsolation: false, //开启渲染进程中使用nodejs In Electron 12, the default will bechanged to `true 26 | enableRemoteModule: true //启用Remote模块 27 | } 28 | }) 29 | newsWindow.loadFile(path.join(__dirname, '../html/renderer2.html')) 30 | //open devTools 31 | newsWindow.webContents.openDevTools({ mode: 'bottom' }) 32 | newsWindow.webContents.on('did-finish-load', () => { 33 | BrowserWindow.getFocusedWindow().webContents.send('toRenderer2', 'toRenderer2') 34 | }) 35 | }) 36 | ipcMain.on('renderer1', () => { 37 | let renderer1Win = BrowserWindow.fromId(renderer1Id) 38 | renderer1Win.webContents.send('toRenderer1', 'toRenderer1') 39 | }) 40 | -------------------------------------------------------------------------------- /electron/main/globalShortcut.js: -------------------------------------------------------------------------------- 1 | const { globalShortcut, app } = require('electron') 2 | const commentUtils = require('../utils/commentUtils') 3 | app.whenReady().then(() => { 4 | //注册全局快捷键 5 | globalShortcut.register('ctrl+e', () => { 6 | commentUtils.dialogMessage('快捷键 "ctrl+e被点击了').then(() => {}) 7 | //检测快捷键是否注册功能 8 | console.log(globalShortcut.isRegistered('ctrl+e')) 9 | }) 10 | }) 11 | 12 | //注销全局快捷键的监听 13 | app.on('will-quit', () => { 14 | globalShortcut.unregister('ctrl+e') 15 | }) 16 | -------------------------------------------------------------------------------- /electron/main/menu.js: -------------------------------------------------------------------------------- 1 | const { Menu } = require('electron') 2 | let menuTemplate = [ 3 | { 4 | label: '文件', 5 | submenu: [ 6 | { 7 | label: '新建文件', 8 | accelerator: 'ctrl+n', 9 | click: function () { 10 | console.log('ctrl+n') 11 | } 12 | }, 13 | { 14 | label: '新建窗口', 15 | click: function () { 16 | console.log('new window') 17 | } 18 | } 19 | ] 20 | }, 21 | { 22 | label: '编辑', 23 | submenu: [ 24 | { label: '复制', role: 'copy' }, 25 | { label: '截切', role: 'cut' } 26 | ] 27 | } 28 | ] 29 | 30 | const menuBuilder = Menu.buildFromTemplate(menuTemplate) 31 | Menu.setApplicationMenu(menuBuilder) 32 | -------------------------------------------------------------------------------- /electron/main/tray.js: -------------------------------------------------------------------------------- 1 | const { Menu, Tray, app, BrowserWindow } = require('electron') 2 | const path = require('path') 3 | 4 | const appIcon = new Tray(path.join(__dirname, '../static/lover.png')) 5 | const menu = Menu.buildFromTemplate([ 6 | { label: '设置', click: function () {} }, 7 | { label: '帮助', click: function () {} }, 8 | { label: '关于', click: function () {} }, 9 | { 10 | label: '退出', 11 | click: function () { 12 | app.quit() 13 | } 14 | } 15 | ]) 16 | let timeout = setTimeout(() => { 17 | appIcon.setImage(path.join(__dirname, '../static/lover.png')) 18 | clearTimeout(timeout) 19 | }, 1000) 20 | appIcon.setToolTip('vue3-admin-electron') 21 | appIcon.setContextMenu(menu) 22 | 23 | 24 | 25 | //win relative 26 | if(process.platform==="win32"){ 27 | //监听任务栏图标的单击、双击事件 28 | let win = BrowserWindow.getFocusedWindow() 29 | appIcon.on('double-click', () => { 30 | console.log(win) 31 | win.show() 32 | }) 33 | 34 | //Electron 点击右上角关闭按钮隐藏任务栏图标 35 | win.on('close', (e) => { 36 | console.log(win.isFocused()) 37 | if (!win.isFocused()) { 38 | win = null 39 | } else { 40 | e.preventDefault() 41 | win.hide() 42 | } 43 | }) 44 | 45 | //Electron 实现任务栏闪烁图标 46 | let count = 0 47 | const timer = setInterval(function () { 48 | count++ 49 | if (count % 2 === 0) { 50 | appIcon.setPressedImage(path.join(__dirname, '../static/lover.png')) 51 | // appIcon.setImage(path.join(__dirname, '../static/lover.ico')) 52 | } else { 53 | appIcon.setImage(path.join(__dirname, '../static/lover.png')) 54 | } 55 | }, 500) 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /electron/renderer/renderer2.js: -------------------------------------------------------------------------------- 1 | const { ipcRenderer } = require('electron') 2 | ipcRenderer.on('toRenderer2', (e, data) => { 3 | console.log(data) 4 | }) 5 | 6 | window.onload = () => { 7 | let btnDom = document.querySelector('#btn') 8 | btnDom.onclick = () => { 9 | ipcRenderer.send('renderer1', 'renderer1') 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /electron/static/empty.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/electron/static/empty.ico -------------------------------------------------------------------------------- /electron/static/favicon2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/electron/static/favicon2.ico -------------------------------------------------------------------------------- /electron/static/lover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/electron/static/lover.png -------------------------------------------------------------------------------- /electron/utils/commentUtils.js: -------------------------------------------------------------------------------- 1 | const { dialog } = require('electron') 2 | 3 | module.exports = { 4 | dialogError(content, title) { 5 | dialog.showErrorBox(title || 'error', content) 6 | }, 7 | dialogMessage(content, type, title) { 8 | return new Promise((resolve, reject) => { 9 | return dialog 10 | .showMessageBox({ type: type || 'info', title: title || '提示', message: content, buttons: ['ok', 'no'] }) 11 | .then((res) => { 12 | if (res.response === 0) { 13 | resolve() 14 | } else { 15 | reject() 16 | } 17 | }) 18 | .catch((err) => { 19 | console.log('catch', err) 20 | }) 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /electron/utils/node-fs.js: -------------------------------------------------------------------------------- 1 | //node fs 模块 对文件的增删改查 2 | const fs = require('fs') 3 | /** 4 | * 异步使用流的形式写文件,该方案比上述方法更为安全 5 | * filePath:文件存放地址 6 | * jsonstring 所以文件内容对象 7 | * */ 8 | export const writeFileWithStream = (filePath, jsonstring, cb) => { 9 | let streamWrite = fs.createWriteStream(filePath) 10 | streamWrite.write(jsonstring) 11 | streamWrite.end() 12 | streamWrite.on('error', (err) => { 13 | cb(`写入${filePath}异常:${err}`) 14 | }) 15 | streamWrite.on('finish', () => { 16 | cb() 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= title %> 9 | 10 | 11 |
12 |
13 | 14 |
正在加载系统资源,请耐心等待
15 |
16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 服务启动中...... 9 | 10 | 11 | -------------------------------------------------------------------------------- /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 | const app = createApp(App) 30 | 31 | //router 32 | app.use(router) 33 | 34 | //pinia 35 | const pinia = createPinia() 36 | pinia.use(piniaPluginPersistedstate) 37 | app.use(pinia) 38 | 39 | //i18n 40 | app.use(setupI18n) 41 | app.component('SvgIcon', svgIcon) 42 | directive(app) 43 | 44 | //element-plus 45 | app.use(ElementPlus) 46 | 47 | app.mount('#app') 48 | -------------------------------------------------------------------------------- /mock-prod-server.ts: -------------------------------------------------------------------------------- 1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' 2 | //https://cn.vitejs.dev/guide/features.html#glob-import 3 | // @ts-ignore 4 | const modulesFiles = import.meta.glob('../mock/*', { eager: true }) 5 | let modules = [] 6 | for (const filePath in modulesFiles) { 7 | //读取文件内容到 modules 8 | modules = modules.concat(modulesFiles[filePath].default) 9 | } 10 | export function setupProdMockServer() { 11 | //创建prod mock server 12 | createProdMockServer([...modules]) 13 | } 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /mock/system.ts: -------------------------------------------------------------------------------- 1 | const system = { 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 | system,loginOut 25 | ] 26 | -------------------------------------------------------------------------------- /nedbStore.db: -------------------------------------------------------------------------------- 1 | {"name":"","age":"","_id":"2a9H6ypIAD6ZVDia"} 2 | {"name":"","age":"","_id":"CdQJoiYSP7gWobjQ"} 3 | {"name":"","age":"","_id":"PpvyYzNUwXdnX3MB"} 4 | {"name":"","age":"","_id":"Vbz5Qy8j8kc1xH6d"} 5 | {"name":"","age":"","_id":"ciMTWeWe1pQIZGMr"} 6 | {"name":"","age":"","_id":"h2IbNY5Yv65JPZaf"} 7 | {"name":"","age":"","_id":"yYymdGWiUIJziEns"} 8 | -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch":["electron","electron-main.js"], 3 | "ext": "ts,js,html,css", 4 | "ignore": ["node_modules","src","dist",".husky","mock",".husky"] 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 40 | 46 | -------------------------------------------------------------------------------- /src/api/system.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 | -------------------------------------------------------------------------------- /src/assets/401_images/401.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/assets/401_images/401.gif -------------------------------------------------------------------------------- /src/assets/404_images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/assets/404_images/404.png -------------------------------------------------------------------------------- /src/assets/404_images/404_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/assets/404_images/404_cloud.png -------------------------------------------------------------------------------- /src/assets/gif/dianchi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/assets/gif/dianchi.gif -------------------------------------------------------------------------------- /src/components/ElSvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /src/components/TestUnit.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /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/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/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/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/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/hooks/use-error-log.ts: -------------------------------------------------------------------------------- 1 | /*js 错误日志收集*/ 2 | import { jsErrorCollection } from 'js-error-collection' 3 | import pack from '../../package.json' 4 | import settings from '@/settings' 5 | import bus from '@/utils/bus' 6 | //此处不要使用utils下的axios 7 | import axiosReq from '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/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 | -------------------------------------------------------------------------------- /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/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 42 | -------------------------------------------------------------------------------- /src/icons/common/404.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/icons/common/404.svg -------------------------------------------------------------------------------- /src/icons/common/bug.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/chart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/component.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/demo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/documentation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/drag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/education.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/excel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/exit-fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/fullscreen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/guide.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/hamburger.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/international.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/language.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/list.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/money.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/pdf.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/people.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/peoples.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/shopping.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/sidebar-logo.svg: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/icons/common/size.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/skill.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/theme.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/tree-table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/wechat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/common/zip.svg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/icons/common/zip.svg -------------------------------------------------------------------------------- /src/icons/nav-bar/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/example.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/eye-open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/form.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/link.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/nested.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/password.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/theme-icon.svg: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /src/icons/nav-bar/tree.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/nav-bar/user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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/layout/app-main/Hamburger.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 34 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 13 | 30 | 31 | 69 | -------------------------------------------------------------------------------- /src/layout/sidebar/Link.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | -------------------------------------------------------------------------------- /src/layout/sidebar/Logo.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | 39 | 79 | -------------------------------------------------------------------------------- /src/layout/sidebar/MenuIcon.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 15 | 16 | 25 | -------------------------------------------------------------------------------- /src/layout/sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 83 | -------------------------------------------------------------------------------- /src/layout/sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 38 | 47 | 48 | 49 | 52 | 53 | -------------------------------------------------------------------------------- /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/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/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 | const app = createApp(App) 30 | 31 | //import element-plus svg icon 32 | import ElSvgIcon from "@/lib/el-svg-icon" 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/mock-prod-server.ts: -------------------------------------------------------------------------------- 1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer' 2 | //https://cn.vitejs.dev/guide/features.html#glob-import 3 | // @ts-ignore 4 | const modulesFiles = import.meta.glob('../mock/*', { eager: true }) 5 | let modules = [] 6 | for (const filePath in modulesFiles) { 7 | //读取文件内容到 modules 8 | modules = modules.concat(modulesFiles[filePath].default) 9 | } 10 | export function setupProdMockServer() { 11 | //创建prod mock server 12 | createProdMockServer([...modules]) 13 | } 14 | -------------------------------------------------------------------------------- /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/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/router/modules/electron.ts: -------------------------------------------------------------------------------- 1 | import Layout from '@/layout/index.vue' 2 | const electron = { 3 | path: '/electron', 4 | component: Layout, 5 | meta: { title: 'electron', icon: 'user' }, 6 | alwaysShow: true, 7 | children: [ 8 | { 9 | path: 'main-renderer-comm', 10 | component: () => import('@/views/electron/MainRendererComm.vue'), 11 | name: 'MainRenderer', 12 | meta: { title: 'Main Renderer' } 13 | }, 14 | { 15 | path: 'electron-demo', 16 | component: () => import('@/views/electron/ElectronDemo.vue'), 17 | name: 'ElectronDemo', 18 | meta: { title: 'Electron Demo' } 19 | }, 20 | { 21 | path: 'fs-extra', 22 | component: () => import('@/views/electron/FsExtra.vue'), 23 | name: 'FsExtra', 24 | meta: { title: 'fs-extra' } 25 | }, 26 | { 27 | path: 'notify-netListen', 28 | component: () => import('@/views/electron/NotifyNetListen.vue'), 29 | name: 'NotifyNetListen', 30 | meta: { title: 'Notify Net' } 31 | }, 32 | { 33 | path: 'nedb-demo', 34 | component: () => import('@/views/electron/NedbDemo.vue'), 35 | name: 'NedbDemo', 36 | meta: { title: 'NedbDemo' } 37 | }, 38 | { 39 | path: 'indexDb-demo.vue', 40 | component: () => import('@/views/electron/IndexDbDemo.vue'), 41 | name: 'IndexDbDemo', 42 | meta: { title: 'IndexDbDemo' } 43 | } 44 | ] 45 | } 46 | 47 | export default electron 48 | -------------------------------------------------------------------------------- /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/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/styles/index.scss: -------------------------------------------------------------------------------- 1 | //scss 语法糖包含常用的布局方式 flex column 2 | @import './scss-suger.scss'; 3 | //重置 element-plus 样式 4 | @import './reset-elemenet-plus-style.scss'; 5 | //动画文件 6 | @import './transition.scss'; 7 | 8 | //reset style 9 | body { 10 | height: 100%; 11 | margin: 0; 12 | padding: 0; 13 | font-size: 14px; 14 | } 15 | * { 16 | box-sizing: border-box; 17 | } 18 | *::before, 19 | *::after { 20 | box-sizing: border-box; 21 | } 22 | a:focus, 23 | a:active { 24 | outline: none; 25 | } 26 | a, 27 | a:focus, 28 | a:hover { 29 | cursor: pointer; 30 | color: inherit; 31 | text-decoration: none; 32 | } 33 | 34 | h1, 35 | h2, 36 | h3, 37 | h4, 38 | h5, 39 | h6 { 40 | line-height: 1; 41 | font-weight: 400; 42 | margin: 0; 43 | padding: 0; 44 | } 45 | span, 46 | output { 47 | display: inline-block; 48 | line-height: 1; 49 | } 50 | 51 | //scroll 52 | @mixin main-show-wh() { 53 | /* css 声明 */ 54 | //height: calc(100vh - #{$navBarHeight} - #{$tagViewHeight} - #{$appMainPadding * 2}); 55 | height: calc(100vh - #{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 | -------------------------------------------------------------------------------- /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/styles/reset-elemenet-plus-style.scss: -------------------------------------------------------------------------------- 1 | //leave the padding of el-scroll sidebar 2 | .el-scrollbar__view { 3 | padding-bottom: 80px; 4 | } 5 | 6 | 7 | .el-scrollbar{ 8 | --el-menu-base-level-padding:15px; 9 | } 10 | -------------------------------------------------------------------------------- /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/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: 10px; 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: #409eff; 67 | } 68 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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: 10px; 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: #409eff; 68 | } 69 | -------------------------------------------------------------------------------- /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/theme/dark/element-plus/css-vars.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/theme/dark/element-plus/css-vars.css -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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: 10px; 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: #409eff; 67 | } 68 | -------------------------------------------------------------------------------- /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/theme/lighting/element-plus/css-vars.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jzfai/vue3-admin-electron/130309705772261805160c23c3b9dd0424c68fe1/src/theme/lighting/element-plus/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/theme/utils/change-theme.ts: -------------------------------------------------------------------------------- 1 | export const toggleHtmlClass = (className) => { 2 | document.querySelectorAll('html')[0].className = className 3 | } 4 | -------------------------------------------------------------------------------- /src/theme/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './change-theme' 2 | -------------------------------------------------------------------------------- /src/utils/bus.ts: -------------------------------------------------------------------------------- 1 | //bus even 2 | import mitt from 'mitt' 3 | export default mitt() 4 | -------------------------------------------------------------------------------- /src/views/basic-demo/hook/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 39 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/second-child.vue: -------------------------------------------------------------------------------- 1 | 16 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/tab-keep-alive.vue: -------------------------------------------------------------------------------- 1 | 7 | 20 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/third-child.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/third-children/SecondChildren.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/third-children/ThirdChildren.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/views/basic-demo/keep-alive/third-keep-alive.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/views/basic-demo/mock/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/Children.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 77 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/SubChildren.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 35 | -------------------------------------------------------------------------------- /src/views/basic-demo/parent-children/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 55 | -------------------------------------------------------------------------------- /src/views/basic-demo/pinia/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/views/basic-demo/svg-icon/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /src/views/basic-demo/vue3-template/Vue3Template.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/views/basic-demo/worker/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 46 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 39 | 52 | -------------------------------------------------------------------------------- /src/views/electron/ElectronDemo.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/views/electron/ElectronDemoBak.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/views/electron/NedbDemo.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/views/electron/NotifyNetListen.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/views/nested/menu1/index.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-1/index.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/views/nested/menu1/menu1-2/index.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /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/menu1/menu1-3/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/nested/menu2/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /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/views/setting-switch/SettingSwitch.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /src/views/setting-switch/index.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /typings/common.d.ts: -------------------------------------------------------------------------------- 1 | //common type file, you can not export the type in common.d.ts 2 | //not export can use 3 | interface ObjTy { 4 | [propName: string]: any 5 | } 6 | 7 | /*axiosReq请求配置*/ 8 | import { AxiosRequestConfig } from 'axios' 9 | interface AxiosReqTy extends AxiosRequestConfig { 10 | url?: string 11 | method?: string 12 | data?: ObjTy 13 | isParams?: boolean 14 | bfLoading?: boolean 15 | afHLoading?: boolean 16 | isUploadFile?: boolean 17 | isDownLoadFile?: boolean 18 | isAlertErrorMsg?: boolean 19 | baseURL?: string 20 | timeout?: number 21 | } 22 | interface AxiosConfigTy { 23 | url?: string 24 | method?: string 25 | data?: ObjTy 26 | isParams?: boolean 27 | bfLoading?: boolean 28 | afHLoading?: boolean 29 | isUploadFile?: boolean 30 | isDownLoadFile?: boolean 31 | isAlertErrorMsg?: boolean 32 | baseURL?: string 33 | timeout?: number 34 | } 35 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | export default defineConfig({ 6 | plugins: [Vue(), VueJsx(), DefineOptions()], 7 | optimizeDeps: { 8 | disabled: true 9 | }, 10 | test: { 11 | clearMocks: true, 12 | environment: 'jsdom', 13 | //setup 文件的路径。它们将运行在每个测试文件之前。 14 | setupFiles: ['./vitest.setup.ts'], 15 | transformMode: { 16 | web: [/\.[jt]sx$/] 17 | } 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------