├── .editorconfig ├── .env.dev ├── .env.pro ├── .env.proxy ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .prettierignore ├── .prettierrc.js ├── .stylelintignore ├── .stylelintrc.js ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── babel.config.js ├── commitlint.config.js ├── index.html ├── package.json ├── public ├── config.js └── favicon.ico ├── src ├── App.vue ├── api │ ├── abstract.ts │ ├── config.ts │ ├── index.ts │ ├── intercept.ts │ ├── modules │ │ └── basic.ts │ └── types │ │ ├── basic.ts │ │ └── index.ts ├── assets │ ├── scss │ │ ├── _base.scss │ │ ├── _common.scss │ │ ├── _config.scss │ │ ├── _reset.scss │ │ ├── component │ │ │ ├── _custom.scss │ │ │ ├── _element.scss │ │ │ └── _richtext.scss │ │ └── utils │ │ │ ├── _animation.scss │ │ │ ├── _background.scss │ │ │ ├── _border.scss │ │ │ ├── _box.scss │ │ │ ├── _class.scss │ │ │ ├── _css3.scss │ │ │ ├── _font.scss │ │ │ ├── _function.scss │ │ │ ├── _group.scss │ │ │ ├── _mixin.scss │ │ │ ├── _position.scss │ │ │ ├── _size.scss │ │ │ └── _text.scss │ └── theme │ │ ├── _element-iconfont.scss │ │ ├── _element-theme.scss │ │ └── _element-variables.scss ├── components │ ├── basic │ │ ├── index.ts │ │ ├── modify-pass.vue │ │ └── serve-error.vue │ └── custom │ │ ├── custom-chart.vue │ │ ├── custom-dialog.vue │ │ ├── custom-drawer.vue │ │ ├── custom-image.vue │ │ ├── custom-message.ts │ │ ├── custom-pagination.vue │ │ ├── custom-query.vue │ │ ├── custom-scroll.vue │ │ ├── custom-toast │ │ ├── index.vue │ │ ├── toast.ts │ │ └── types.ts │ │ └── index.ts ├── config │ ├── global.ts │ ├── menu.ts │ └── microApps.ts ├── layout │ ├── Demo.vue │ ├── components │ │ ├── LayoutAside.vue │ │ ├── LayoutHeader.vue │ │ ├── LayoutNav.vue │ │ ├── SidebarItem.vue │ │ ├── SidebarLink.vue │ │ └── index.ts │ └── index.vue ├── locale │ ├── en.json │ └── zh-CN.json ├── main.ts ├── plugin │ ├── dayjs.ts │ ├── echarts.ts │ ├── element.ts │ ├── i18n.ts │ ├── index.ts │ └── lodash.ts ├── router │ ├── index.ts │ └── intercept.ts ├── shims-vue.d.ts ├── store │ ├── index.ts │ ├── modules │ │ └── basic.ts │ └── types │ │ └── index.ts ├── utils │ ├── directives │ │ ├── designform.ts │ │ ├── index.ts │ │ ├── inputLimit.ts │ │ ├── loadmore.ts │ │ ├── onlyNumber.ts │ │ └── permission.ts │ ├── filters.ts │ ├── pager.ts │ ├── storage │ │ ├── cookie.ts │ │ ├── index.ts │ │ ├── localstorage.ts │ │ └── sessionstorage.ts │ └── tools.ts └── views │ ├── 404.vue │ ├── Login.vue │ └── components │ ├── TaskList.vue │ └── index.ts ├── static ├── img │ ├── 403.png │ ├── 404.png │ ├── 503.png │ ├── login_bg1.png │ └── login_bg2.png └── svg │ └── default_user.svg ├── tsconfig.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{js,jsx,ts,tsx,vue,scss}] 4 | charset = utf-8 5 | indent_size = 2 6 | end_of_line = lf 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.env.dev: -------------------------------------------------------------------------------- 1 | # .env.dev 2 | NODE_ENV=development 3 | 4 | VITE_VERSION = 'v1.0.0' 5 | VITE_BASEURL = '//dev.backendapi.aid.connext.net.cn/' 6 | VITE_VILEURL = '//dev.backendapi.aid.connext.net.cn/' 7 | VITE_WEBURL = '//dev.aid.connext.net.cn/' 8 | -------------------------------------------------------------------------------- /.env.pro: -------------------------------------------------------------------------------- 1 | # .env.pro 2 | NODE_ENV=production 3 | VITE_BUILD_ENV = 'shell' 4 | 5 | VITE_VERSION = 'v1.0.0' 6 | VITE_BASEURL = '//test.backendapi.aid.connext.net.cn/' 7 | VITE_VILEURL = '//test.backendapi.aid.connext.net.cn/' 8 | VITE_WEBURL = '//test.aid.connext.net.cn/' 9 | -------------------------------------------------------------------------------- /.env.proxy: -------------------------------------------------------------------------------- 1 | # .env.proxy 2 | NODE_ENV=development 3 | 4 | VITE_VERSION = 'v1.0.0' 5 | VITE_BASEURL = '//localhost:7000/' 6 | VITE_VILEURL = '//localhost:7000/' 7 | VITE_WEBURL = '//dev.aid.connext.net.cn/' 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /dist-ssr/ 3 | /public/config.js 4 | /static/ 5 | .eslintrc.js 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | 7 | # Log files 8 | yarn.lock 9 | yarn-debug.log 10 | yarn-error.log 11 | npm-debug.log 12 | package-lock.json 13 | 14 | # Editor directories and files 15 | .idea 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw? -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /dist-ssr/ 3 | /public/config.js 4 | /static/ 5 | .eslintrc.js 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | singleQuote: true, 4 | // // 1.一行代码的最大字符数,默认是80(printWidth: ) 5 | // printWidth: 80, 6 | // // 2.tab宽度为2空格(tabWidth: ) 7 | // tabWidth: 2, 8 | // // 3.是否使用tab来缩进,我们使用空格(useTabs: ) 9 | // useTabs: false, 10 | // // 4.结尾是否添加分号,false的情况下只会在一些导致ASI错误的其工况下在开头加分号,我选择无分号结尾的风格(semi: ) 11 | // semi: true, 12 | // // 5.使用单引号(singleQuote: ) 13 | // singleQuote: true, 14 | // // 6.object对象中key值是否加引号(quoteProps: "")as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号 15 | // quoteProps: 'as-needed', 16 | // // 7.在jsx文件中的引号需要单独设置(jsxSingleQuote: ) 17 | // jsxSingleQuote: false, 18 | // // 8.尾部逗号设置,es5是尾部逗号兼容es5,none就是没有尾部逗号,all是指所有可能的情况,需要node8和es2017以上的环境。(trailingComma: "") 19 | // trailingComma: 'es5', 20 | // // 9.object对象里面的key和value值和括号间的空格(bracketSpacing: ) 21 | // bracketSpacing: true, 22 | // // 10.jsx标签多行属性写法时,尖括号是否另起一行(jsxBracketSameLine: ) 23 | // // jsxBracketSameLine: false, 24 | // // 11.箭头函数单个参数的情况是否省略括号,默认always是总是带括号(arrowParens: "") 25 | // arrowParens: 'always', 26 | // // 12.range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件(rangeStart: rangeEnd: ) 27 | // rangeStart: 0, 28 | // rangeEnd: Infinity, 29 | // // 18. vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠 30 | // vueIndentScriptAndStyle: false, 31 | // // 19. endOfLine: "" 行尾换行符,默认是lf, 32 | // endOfLine: 'lf', 33 | // // 20.embeddedLanguageFormatting: "off",默认是auto,控制被引号包裹的代码是否进行格式化 34 | // embeddedLanguageFormatting: 'auto', 35 | }; 36 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | src/assets 2 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | const { off } = require('process'); 2 | 3 | module.exports = { 4 | extends: ['stylelint-config-standard', 'stylelint-config-sass-guidelines'], 5 | plugins: ['stylelint-order'], 6 | rules: { 7 | 'max-nesting-depth': 10, // 限制嵌套深度 8 | 'selector-max-id': 6, // 限制选择器中 ID 选择器的数量 9 | 'selector-max-compound-selectors': 10, // 限制选择器中复合选择器的数量 10 | 'selector-class-pattern': '^[a-z][a-zA-Z0-9_-]+$|^el-|^mz-', // 为类选择器指定一个匹配模式 11 | 'selector-pseudo-element-no-unknown': [ 12 | true, 13 | { ignorePseudoElements: ['v-deep'] }, 14 | ], // 禁止未知的伪类选择器 15 | 16 | // "at-rule-empty-line-before": "never", // 在at-rules之前要求或禁止空行(Autofixable) 17 | // "at-rule-name-case": "lower", // 为at-rules名称指定小写或大写(Autofixable) 18 | // "at-rule-name-space-after": "always", // 在规则名称后面需要一个空格(Autofixable) 19 | // // "at-rule-no-unknown": true, // 禁止未知的规则 20 | // "at-rule-no-vendor-prefix": true, // 禁止at-rules的供应商前缀 21 | // "at-rule-semicolon-newline-after": "always", // 在at-rules分号后面需要换行符(Autofixable) 22 | // "at-rule-semicolon-space-before": "never", // 在at-rules的分号前需要单个空格或禁止空格 23 | // "block-closing-brace-empty-line-before": "never", // 在块的右大括号(Autofixable)之前需要或禁止空行 24 | // "block-no-empty": true, // 禁止空块 25 | // "color-hex-case": "lower", // 为十六进制颜色指定小写或大写(自动固定) 26 | // "color-hex-length": "short", // 为十六进制颜色指定短或长符号(自动固定) 27 | // "color-named": "never", // 要求(如果可能)或禁止命名颜色 28 | // "color-no-invalid-hex": true, // 禁止无效的十六进制颜色 29 | // "comment-no-empty": true, // 禁止空评论 30 | // "declaration-bang-space-after": "never", // 声明爆炸后需要一个空格或不允许空格(Autofixable) 31 | // "declaration-block-no-duplicate-properties": true, // 禁止声明块中的重复属性 32 | // "declaration-block-no-redundant-longhand-properties": true, // 禁止可以合并为一个速记属性的longhand属性 33 | // "declaration-block-no-shorthand-property-overrides": true, // 禁止覆盖相关的手写属性的速记属性 34 | // "declaration-block-semicolon-newline-before": "never-multi-line", // 在声明块的分号之前需要换行符或禁止空格 35 | // "declaration-block-trailing-semicolon": "always", // 在声明块(Autofixable)中要求或禁止使用尾随分号. 36 | // "declaration-empty-line-before": "never", // 声明前需要或禁止空行(Autofixable) 37 | // "font-family-no-duplicate-names": true, // 禁止重复的字体系列名称 38 | // "font-weight-notation": "numeric", // 需要数字或命名(如果可能)font-weight值.此外,当需要命名值时,只需要有效名称. 39 | // "function-calc-no-invalid": true, // 禁止calc函数内的无效表达式 40 | // "function-calc-no-unspaced-operator": true, // 禁止calc函数内的未空格运算符 41 | // "function-linear-gradient-no-nonstandard-direction": true, // 禁用linear-gradient()根据标准语法无效的调用中的方向值 42 | // "function-max-empty-lines": 0, // 限制函数内的相邻空行数(自动固定) 43 | // "function-name-case": "lower", // 为函数名称指定小写或大写(Autofixable) 44 | // "function-url-quotes": "always", // 要求或禁止网址的引号. 45 | // "function-whitespace-after": "always", // 在函数(Autofixable)之后需要或禁止空格 46 | // "indentation": 4, // 指定缩进(Autofixable) 47 | // "length-zero-no-unit": true, // 禁止零长度的单位(自动固定) 48 | // "linebreaks": "unix", // 指定unix或windows linebreaks(Autofixable) 49 | // "max-empty-lines": 3, // 限制相邻空行的数量 50 | // "max-nesting-depth": 10, // 限制嵌套深度 51 | // "media-feature-name-case": "lower", // 为媒体功能名称指定小写或大写(自动固定) 52 | // "media-feature-name-no-unknown": true, // 禁止未知的媒体功能名称 53 | // "media-feature-name-no-vendor-prefix": true, // 禁止媒体功能名称的供应商前缀. 54 | // "no-descending-specificity": true, // 不允许选择具有较低特异性的选择子来覆盖更高特异性的选择者 55 | // "no-duplicate-at-import-rules": true, // 禁止@import样式表中的重复规则 56 | // "no-duplicate-selectors": true, // 禁止样式表中的重复选择器 57 | // "no-empty-source": true, // 禁止空来源 58 | // "no-extra-semicolons": true, // 禁止使用额外的分号(Autofixable) 59 | // "no-unknown-animations": true, // 禁止未知的动画 60 | // "number-leading-zero": "always", // 要求或禁止小于1的小数的前导零(自动固定) 61 | // "number-max-precision": 3, // 限制数字中允许的小数位数 62 | // "number-no-trailing-zeros": true, // 禁止数字尾随零(自动固定) 63 | // "property-case": "lower", // 为属性指定小写或大写(Autofixable) 64 | // "property-no-unknown": true, // 禁止未知属性 65 | // "property-no-vendor-prefix": true, // 禁止属性的供应商前缀 66 | // "selector-no-vendor-prefix": true, // 禁止选择器的供应商前缀 67 | // "selector-pseudo-class-case": "lower", // 为伪类选择器指定小写或大写(Autofixable) 68 | 69 | // "selector-pseudo-element-case": "lower", // 为伪元素选择器指定小写或大写 70 | // "selector-pseudo-element-no-unknown": true, // 禁止未知的伪元素选择器 71 | // "selector-type-case": "lower", // 为类型选择器指定小写或大写(Autofixable) 72 | // "selector-type-no-unknown": true, // 禁止未知类型选择器 73 | // "shorthand-property-no-redundant-values": true, // 禁止速记属性中的冗余值(自动固定) 74 | // "string-no-newline": true, // 禁止(未转义)字符串中的换行符 75 | // "unit-case": "lower", // 为单位指定小写或大写(自动固定) 76 | // // "unit-no-unknown": true, // 禁止未知单位 77 | // "value-keyword-case": "lower", // 为关键字值指定小写或大写(自动固定) 78 | // "value-list-max-empty-lines": 0, // 限制值列表中相邻空行的数量(自动固定) 79 | // "value-no-vendor-prefix": true // 禁止值的供应商前缀 80 | }, 81 | }; 82 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.multiCursorModifier": "ctrlCmd", 3 | "editor.snippetSuggestions": "top", 4 | "editor.formatOnPaste": true, 5 | "editor.minimap.enabled": true, 6 | "editor.tabSize": 4, 7 | "editor.detectIndentation": false, 8 | "explorer.confirmDelete": false, 9 | "files.eol": "\n", 10 | "files.associations": { 11 | "*.vue": "vue" 12 | }, 13 | "eslint.options": { 14 | "plugins": ["html", "vue", "react"] 15 | }, 16 | "eslint.alwaysShowStatus": true, 17 | "eslint.validate": [ 18 | "vue", 19 | "html", 20 | "javascript", 21 | "typescript", 22 | "javascriptreact", 23 | "typescriptreact" 24 | ], 25 | "window.zoomLevel": 0, 26 | "team.showWelcomeMessage": false, 27 | "breadcrumbs.enabled": false, 28 | "stylelint.enable": true, 29 | "scss.validate": false, 30 | "less.validate": false, 31 | "css.validate": false, 32 | "vsicons.dontShowNewVersionMessage": true, 33 | "workbench.iconTheme": "vscode-icons", 34 | "diffEditor.ignoreTrimWhitespace": false, 35 | "terminal.integrated.rendererType": "dom", 36 | "editor.codeActionsOnSave": { 37 | "source.fixAll.eslint": false, 38 | "source.fixAll.stylelint": false 39 | }, 40 | "editor.formatOnSave": true, 41 | "editor.defaultFormatter": "esbenp.prettier-vscode", 42 | "files.exclude": { 43 | // "dist": true, 44 | "src/**/*.wxss": true 45 | // "node_modules": true, 46 | // ".vscode": true 47 | }, 48 | "javascript.updateImportsOnFileMove.enabled": "always", 49 | "git.ignoreMissingGitWarning": true, 50 | "workbench.editor.enablePreview": false, 51 | "team.showFarewellMessage": false, 52 | "i18n-ally.sourceLanguage": "zh-CN", 53 | "i18n-ally.displayLanguage": "zh-CN", 54 | "i18n-ally.enabledParsers": ["json"], 55 | "i18n-ally.extract.targetPickingStrategy": "file-previous", 56 | "liveServer.settings.donotShowInfoMsg": true, 57 | "terminal.integrated.tabs.enabled": true, 58 | "git.confirmSync": false, 59 | "gitlens.advanced.messages": { 60 | "suppressGitMissingWarning": true 61 | }, 62 | "i18n-ally.localesPaths": ["src/locale"] 63 | } 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Vite logo 4 | 5 |

6 |
7 |

8 | node compatility 9 | npm package 10 | vue package 11 | vite package 12 |

13 | 14 | # vite vue3 temp 15 | 16 | ## 简介 17 | 18 | > 使用最新的`vue3`,`vite2`,`typescript`等主流技术搭建的一个供学习参考的模版工程。 19 | 20 | ## 包含 21 | 22 | - **技术栈**:使用 `vue3`,`vite`,`typescript`等前沿技术开发 23 | - **ajax**:二次封装`axios`,统一管理接口 24 | - **主题**:可自行修改`element-plus`主题样式 25 | - **国际化**:完善的国际化方案 26 | - **路由**:动态路由生成方案 27 | - **组件**:二次封装了常用的组件 28 | - **工具**:常用的指令,过滤器,`storage`存储,工具函数 29 | 30 | ## 目录结构 31 | 32 | ```md 33 | |-- public # 静态资源 34 | | |-- config.js # 配置文件 35 | | |-- favicon.ico # favicon 图标 36 | |-- src # 源代码 37 | | |-- api # api 请求 38 | | | |-- modules # 模块 39 | | | |-- types # 接口定义 40 | | | |-- abstract.ts # 基类 41 | | | |-- config.ts # 字典表 42 | | | |-- index.ts # 入口文件 43 | | | |-- intercept.ts # 拦截器 44 | | |-- assets # 主题 变量等资源 45 | | | |-- scss # scss 变量 46 | | | |-- theme # elemet 主题 47 | | |-- components # 全局公共组件 48 | | |-- config # 全局公共配置 49 | | |-- layout # 全局 layout 50 | | |-- locale # 国际化 51 | | |-- plugin # 三方插件 52 | | |-- router # 全局路由 53 | | |-- store # 全局 vuex 54 | | |-- utils # 全局公用方法 55 | | | |-- directives # 指令 56 | | | |-- storage # 持久化 57 | | | |-- filters.ts # 过滤器 58 | | | |-- pager.ts # 发布订阅 59 | | | |-- tools.ts # 工具函数 60 | | |-- views # 所有页面 61 | | |-- App.vue # 入口页面 62 | | |-- main.ts # 入口文件 63 | | |-- shims-vue.d.ts # ts 声明文件 64 | |-- static # 静态资源 65 | | |-- img # img 66 | | |-- svg # svg 67 | |-- .editorconfig # editorconfig 68 | |-- .env.dev # 环境变量 开发 69 | |-- .env.pro # 环境变量 生产 70 | |-- .env.proxy # 环境变量 代理 71 | |-- .eslintignore # eslintignore 72 | |-- .eslintrc.js # eslint 配置项 73 | |-- .gitignore # gitignore 74 | |-- babel.config.js # babel 配置项 75 | |-- index.html # html 模板 76 | |-- package.json # package.json 77 | |-- README.md # README 78 | |-- tsconfig.json # tsconfig 79 | |-- vite.config.ts # vite 配置文件 80 | ``` 81 | 82 | ## Project setup 83 | 84 | ``` 85 | yarn 86 | ``` 87 | 88 | ### Compiles and hot-reloads for development 89 | 90 | ``` 91 | yarn serve dev 92 | ``` 93 | 94 | ### Compiles and minifies for production 95 | 96 | ``` 97 | yarn build pro 98 | ``` 99 | 100 | ### Lints and fixes files 101 | 102 | ``` 103 | yarn lint 104 | ``` 105 | 106 | ## 浏览器支持 107 | 108 | 本地开发推荐使用`Chrome 80+` 浏览器 109 | 110 | 支持现代浏览器, 不支持 IE 111 | 112 | | [ Edge](http://godban.github.io/browsers-support-badges/)
IE | [ Edge](http://godban.github.io/browsers-support-badges/)
Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | 113 | | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | 114 | | not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions | 115 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [], 3 | }; 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'type-enum': [ 5 | 2, 6 | 'always', 7 | [ 8 | 'build', 9 | 'ci', 10 | 'perf', 11 | 'feat', 12 | 'fix', 13 | 'refactor', 14 | 'docs', 15 | 'chore', 16 | 'style', 17 | 'revert', 18 | 'test', 19 | ], 20 | ], 21 | 'type-case': [0], 22 | 'type-empty': [0], 23 | 'scope-empty': [0], 24 | 'scope-case': [0], 25 | 'subject-full-stop': [0], 26 | 'subject-case': [0, 'never'], 27 | 'header-max-length': [0, 'always', 72], 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Vite App 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-vue3-temp", 3 | "version": "1.0.0", 4 | "description": "vite and vue3.0 template", 5 | "private": true, 6 | "license": "MIT", 7 | "scripts": { 8 | "serve": "vite --mode", 9 | "build": "vue-tsc --noEmit && vite build --mode", 10 | "preview": "vite preview --port 8888", 11 | "format": "prettier --check .", 12 | "lint": "npm run lint:script && npm run lint:style && npm run format", 13 | "lint:script": "eslint src --ext .js,.ts,.jsx,.tsx,.vue", 14 | "lint:style": "stylelint 'src/**/*.{css,scss,less,vue}'", 15 | "prepare": "husky install" 16 | }, 17 | "dependencies": { 18 | "axios": "^0.21.1", 19 | "dayjs": "^1.10.6", 20 | "default-passive-events": "^2.0.0", 21 | "echarts": "^5.1.2", 22 | "element-plus": "^1.3.0-beta.9", 23 | "js-md5": "^0.7.3", 24 | "lodash": "^4.17.21", 25 | "nprogress": "^0.2.0", 26 | "vue": "^3.2.4", 27 | "vue-i18n": "^9.1.7", 28 | "vue-router": "^4.0.11", 29 | "vuex": "^4.0.2", 30 | "vuex-persistedstate": "^4.0.0", 31 | "xlsx": "^0.17.0" 32 | }, 33 | "devDependencies": { 34 | "@commitlint/cli": "^13.2.0", 35 | "@commitlint/config-conventional": "^13.2.0", 36 | "@intlify/vite-plugin-vue-i18n": "^2.1.2", 37 | "@types/node": "^14.14.37", 38 | "@typescript-eslint/eslint-plugin": "^4.21.0", 39 | "@typescript-eslint/parser": "^4.21.0", 40 | "@vitejs/plugin-vue": "^1.2.1", 41 | "@vitejs/plugin-vue-jsx": "^1.1.4", 42 | "@vue/compiler-sfc": "^3.0.5", 43 | "@vuedx/typecheck": "^0.6.3", 44 | "@vuedx/typescript-plugin-vue": "^0.6.3", 45 | "eslint": "^7.32.0", 46 | "eslint-config-airbnb-base": "^14.2.1", 47 | "eslint-config-prettier": "^8.3.0", 48 | "eslint-import-resolver-alias": "^1.1.2", 49 | "eslint-plugin-html": "^6.1.2", 50 | "eslint-plugin-import": "^2.24.2", 51 | "eslint-plugin-prettier": "^4.0.0", 52 | "eslint-plugin-react": "^7.24.0", 53 | "eslint-plugin-vue": "^7.16.0", 54 | "husky": "^7.0.0", 55 | "lint-staged": "^11.1.2", 56 | "path-browserify": "^1.0.1", 57 | "prettier": "^2.4.1", 58 | "sass": "^1.38.1", 59 | "stylelint": "^13.13.1", 60 | "stylelint-config-sass-guidelines": "^8.0.0", 61 | "stylelint-config-standard": "^22.0.0", 62 | "stylelint-order": "^4.1.0", 63 | "typescript": "4.3.5", 64 | "vite": "^2.5.0", 65 | "vite-plugin-compression": "^0.2.4", 66 | "vite-plugin-style-import": "^0.9.2", 67 | "vue-tsc": "^0.0.15" 68 | }, 69 | "lint-staged": { 70 | "src/**/*.{js,jsx,ts,tsx,vue}": [ 71 | "npm run lint:script" 72 | ], 73 | "src/**/*.{css,scss,less,vue}": [ 74 | "npm run lint:style" 75 | ] 76 | }, 77 | "engines": { 78 | "node": ">=14.17.0", 79 | "npm": ">=6.13.4" 80 | }, 81 | "browserslist": [ 82 | "defaults", 83 | "> 1%", 84 | "not ie <= 11", 85 | "last 2 versions" 86 | ] 87 | } -------------------------------------------------------------------------------- /public/config.js: -------------------------------------------------------------------------------- 1 | window.env = { 2 | NODE_ENV: 'production', 3 | VITE_VERSION: 'v1.0.0', 4 | VITE_BASEURL: '//test.backendapi.aid.connext.net.cn/', 5 | VITE_VILEURL: '//test.backendapi.aid.connext.net.cn/', 6 | VITE_WEBURL: '//test.aid.connext.net.cn/', 7 | VITE_MICROAPPS: 'common,rights,pim,oms,crm' 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 33 | 53 | -------------------------------------------------------------------------------- /src/api/abstract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * axios基础构建 3 | * @date 2021-4-9 4 | */ 5 | 6 | import { AxiosRequestConfig } from 'axios'; 7 | import getUrl from './config'; 8 | import storage from '@/utils/storage'; 9 | import instance from './intercept'; 10 | import { CustomResponse } from './types'; 11 | import { element, i18n } from '@/plugin'; 12 | import { CustomToast } from '@/components/custom'; 13 | 14 | class Abstract { 15 | protected baseURL: string = import.meta.env.VITE_BUILD_ENV 16 | ? window.env.VITE_BASEURL 17 | : import.meta.env.VITE_BASEURL; 18 | 19 | protected headers = { 20 | ContentType: 'application/json;charset=UTF-8', 21 | }; 22 | 23 | private apiAxios({ 24 | baseURL = this.baseURL, 25 | headers = this.headers, 26 | method, 27 | url, 28 | data, 29 | params, 30 | responseType, 31 | }: AxiosRequestConfig): Promise { 32 | Object.assign(headers, { 33 | token: storage().get('token') || storage('localstorage').get('token'), 34 | 'x-language': storage('localstorage').get('i18n'), 35 | }); 36 | 37 | // url解析 38 | const urlArr = (url as string).split('.'); 39 | url = getUrl(urlArr[0], urlArr[1]); 40 | 41 | return new Promise((resolve, reject) => { 42 | instance({ 43 | baseURL, 44 | headers, 45 | method, 46 | url, 47 | params, 48 | data, 49 | responseType, 50 | }) 51 | .then((res) => { 52 | // 200:服务端业务处理正常结束 53 | const message = 54 | res.data?.errorMessage || 55 | `${url}${i18n.global.t('baseAbstract.t0')}`; 56 | if (res.status === 200) { 57 | if (res.data.success) { 58 | resolve({ 59 | status: true, 60 | message: 'success', 61 | data: res.data?.data, 62 | origin: res.data, 63 | }); 64 | } else { 65 | element.plugins.ElMessage.error(message); 66 | resolve({ 67 | status: false, 68 | message, 69 | data: res.data?.data, 70 | origin: res.data, 71 | }); 72 | } 73 | } else { 74 | resolve({ status: false, message, data: null }); 75 | } 76 | }) 77 | .catch((err) => { 78 | let message; 79 | switch (err.status) { 80 | case 401: 81 | message = `${i18n.global.t('baseAbstract.t1')}`; 82 | break; 83 | default: 84 | message = 85 | err?.data?.errorMessage || 86 | err?.message || 87 | `${url}${i18n.global.t('baseAbstract.t0')}`; 88 | break; 89 | } 90 | CustomToast({ message }); 91 | // eslint-disable-next-line 92 | reject({ status: false, message, data: null }); 93 | }); 94 | }); 95 | } 96 | 97 | /** 98 | * GET类型的网络请求 99 | */ 100 | protected getReq({ 101 | baseURL, 102 | headers, 103 | url, 104 | data, 105 | params, 106 | responseType, 107 | }: AxiosRequestConfig): Promise { 108 | return this.apiAxios({ 109 | baseURL, 110 | headers, 111 | method: 'GET', 112 | url, 113 | data, 114 | params, 115 | responseType, 116 | }); 117 | } 118 | 119 | /** 120 | * POST类型的网络请求 121 | */ 122 | protected postReq({ 123 | baseURL, 124 | headers, 125 | url, 126 | data, 127 | params, 128 | responseType, 129 | }: AxiosRequestConfig): Promise { 130 | return this.apiAxios({ 131 | baseURL, 132 | headers, 133 | method: 'POST', 134 | url, 135 | data, 136 | params, 137 | responseType, 138 | }); 139 | } 140 | 141 | /** 142 | * PUT类型的网络请求 143 | */ 144 | protected putReq({ 145 | baseURL, 146 | headers, 147 | url, 148 | data, 149 | params, 150 | responseType, 151 | }: AxiosRequestConfig): Promise { 152 | return this.apiAxios({ 153 | baseURL, 154 | headers, 155 | method: 'PUT', 156 | url, 157 | data, 158 | params, 159 | responseType, 160 | }); 161 | } 162 | 163 | /** 164 | * DELETE类型的网络请求 165 | */ 166 | protected deleteReq({ 167 | baseURL, 168 | headers, 169 | url, 170 | data, 171 | params, 172 | responseType, 173 | }: AxiosRequestConfig): Promise { 174 | return this.apiAxios({ 175 | baseURL, 176 | headers, 177 | method: 'DELETE', 178 | url, 179 | data, 180 | params, 181 | responseType, 182 | }); 183 | } 184 | } 185 | 186 | export default Abstract; 187 | -------------------------------------------------------------------------------- /src/api/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API 相关配置文件 3 | * API URL Dict api 字典 4 | */ 5 | 6 | const urlDict: Record> = { 7 | Basic: { 8 | AuthLogin: 'userDomain/admin/v1/user/login', // 登录 9 | AuthLoginOut: 'userDomain/admin/v1/user/logout', // 登出 10 | ModifyPass: 'userDomain/admin/v1/user/changePassword', // 修改密码 11 | UserInfo: 'userDomain/admin/v1/user/profile', // 用户信息 12 | GraphicCode: 'userDomain/admin/v1/user/captcha', // 图形验证码 13 | MenuList: 'userDomain/admin/v1/resource/findMenus', // 菜单 14 | GetDict: 'oms/admin/v1/masterData/getDict', // 获取数据字典 15 | TaskList: 'taskDomain/admin/v1/taskJob/getTaskJobPageList', // 查询所有任务Job带有分页列表 16 | TaskDetailList: 'taskDomain/admin/v1/taskJob/getTaskDetailList', // 查询所有任务Job详情带有分页列表 17 | }, 18 | }; 19 | 20 | const getUrl = (biz: string, UrlName: string): string => { 21 | try { 22 | const bizKeys = Object.keys(urlDict); 23 | if (bizKeys.indexOf(biz) < 0) { 24 | throw new Error('biz not in Dict'); 25 | } 26 | let hostname = urlDict[biz][UrlName]; 27 | if (!hostname) { 28 | throw new Error('url not in Dict'); 29 | } 30 | if (hostname?.startsWith('/')) { 31 | hostname = hostname.substr(1); 32 | } 33 | return hostname; 34 | } catch (err) { 35 | console.error(err); 36 | return ''; 37 | } 38 | }; 39 | 40 | export default getUrl; 41 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import basicApi from './modules/basic'; 2 | 3 | export { basicApi }; 4 | -------------------------------------------------------------------------------- /src/api/intercept.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosRequestConfig, Method, Canceler } from 'axios'; 2 | import { ElLoading } from 'element-plus'; 3 | import { LoadingInstance } from 'element-plus/lib/components/loading/src/loading'; 4 | import storage from '@/utils/storage'; 5 | import store from '@/store'; 6 | import { i18n } from '@/plugin'; 7 | 8 | // 定义接口 9 | interface PendingType { 10 | url?: string; 11 | method?: Method; 12 | params?: Record; 13 | data?: Record; 14 | cancel: Canceler; 15 | } 16 | 17 | // 取消重复请求 18 | let requestCount = 0; 19 | const pending: Array = []; 20 | const { CancelToken } = axios; 21 | // axios 实例 22 | const instance = axios.create({ 23 | timeout: 100000, 24 | responseType: 'json', 25 | }); 26 | let loadingInstance: LoadingInstance; 27 | 28 | // 移除重复请求 29 | const removePending = (config: AxiosRequestConfig) => { 30 | Object.keys(pending) 31 | .reverse() 32 | .forEach((key) => { 33 | const item: number = +key; 34 | const list: PendingType = pending[+key]; 35 | // 当前请求在数组中存在时执行函数体 36 | 37 | if ( 38 | list.url === config.url && 39 | list.method === config.method && 40 | JSON.stringify(list.params) === JSON.stringify(config.params) && 41 | JSON.stringify(list.data) === JSON.stringify(config.data) 42 | ) { 43 | // 执行取消操作 44 | list.cancel(`${i18n.global.t('baseIntercept.t2')}`); 45 | // 从数组中移除记录 46 | pending.splice(item, 1); 47 | } 48 | }); 49 | }; 50 | 51 | // 添加请求拦截器 52 | instance.interceptors.request.use( 53 | (request) => { 54 | requestCount++; 55 | loadingInstance = ElLoading.service({ 56 | customClass: 'global-loading', 57 | text: `${i18n.global.t('baseIntercept.t3')}`, 58 | fullscreen: true, 59 | background: 'rgba(0, 0, 0, 0.3)', 60 | }); 61 | 62 | removePending(request); 63 | request.cancelToken = new CancelToken((c) => { 64 | pending.push({ 65 | url: request.url, 66 | method: request.method, 67 | params: request.params, 68 | data: request.data, 69 | cancel: c, 70 | }); 71 | }); 72 | return request; 73 | }, 74 | (error) => Promise.reject(error) 75 | ); 76 | 77 | // 添加响应拦截器 78 | instance.interceptors.response.use( 79 | (response) => { 80 | requestCount--; 81 | if (!requestCount) { 82 | loadingInstance.close(); 83 | } 84 | 85 | removePending(response.config); 86 | 87 | const errorCode = response?.data?.errorCode; 88 | switch (errorCode) { 89 | case '1020007': 90 | store.dispatch('basic/toggleModifyPass', { 91 | status: true, 92 | type: 'required', 93 | }); 94 | break; 95 | default: 96 | break; 97 | } 98 | 99 | return response; 100 | }, 101 | (error) => { 102 | requestCount--; 103 | if (!requestCount) { 104 | loadingInstance.close(); 105 | } 106 | 107 | const { response } = error; 108 | // 根据返回的code值来做不同的处理(和后端约定) 109 | switch (response?.status) { 110 | case 401: 111 | // token失效 112 | storage().remove('token'); 113 | storage('localstorage').remove('token'); 114 | storage('localstorage').remove('vuex'); 115 | window.location.href = `/login?redirect=${encodeURIComponent( 116 | window.location.href 117 | )}`; 118 | break; 119 | case 403: 120 | // 没有权限 121 | store.dispatch('basic/activeErrorPage', { 122 | status: true, 123 | type: '403', 124 | }); 125 | break; 126 | case 404: 127 | // 接口地址错误 128 | store.dispatch('basic/activeErrorPage', { 129 | status: true, 130 | type: '404', 131 | }); 132 | break; 133 | case 500: 134 | // 服务端错误 135 | store.dispatch('basic/activeErrorPage', { 136 | status: true, 137 | type: '500', 138 | }); 139 | break; 140 | case 503: 141 | // 服务端错误 142 | store.dispatch('basic/activeErrorPage', { 143 | status: true, 144 | type: '503', 145 | }); 146 | break; 147 | default: 148 | break; 149 | } 150 | 151 | // 超时重新请求 152 | const { config } = error; 153 | // 全局的请求次数,请求的间隙 154 | const [RETRY_COUNT, RETRY_DELAY] = [0, 1000]; 155 | 156 | if (config && RETRY_COUNT) { 157 | // 设置用于跟踪重试计数的变量 158 | config.retryCount = config.retryCount || 0; 159 | // 检查是否已经把重试的总数用完 160 | if (config.retryCount >= RETRY_COUNT) { 161 | return Promise.reject(response || { message: error.message }); 162 | } 163 | // 增加重试计数 164 | config.retryCount++; 165 | // 创造新的Promise来处理指数后退 166 | const backoff = new Promise((resolve) => { 167 | setTimeout(() => { 168 | resolve(null); 169 | }, RETRY_DELAY || 1); 170 | }); 171 | // instance重试请求的Promise 172 | return backoff.then(() => instance(config)); 173 | } 174 | 175 | // eslint-disable-next-line 176 | return Promise.reject(response || { message: error.message }); 177 | } 178 | ); 179 | 180 | export default instance; 181 | -------------------------------------------------------------------------------- /src/api/modules/basic.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 基础 API 集合类 3 | * 集成Abstract 4 | * @date 2020-5-29 5 | */ 6 | import Abstract from '../abstract'; 7 | import { AuthLogin, ModifyPass, TaskList, TaskDetailList } from '../types'; 8 | 9 | class Basic extends Abstract { 10 | /** 11 | * 登录 12 | * @param {string} account 用户 13 | * @param {string} password 密码 14 | * @param {string} captchaCode 图形码 15 | * @param {string} captchaCodeToken 图形码token 16 | */ 17 | authLogin(data: AuthLogin) { 18 | return this.postReq({ url: 'Basic.AuthLogin', data }); 19 | } 20 | 21 | /** 22 | * 登出 23 | */ 24 | authLoginOut(data = {}) { 25 | return this.postReq({ url: 'Basic.AuthLoginOut', data }); 26 | } 27 | 28 | /** 29 | * 修改密码 30 | * @param {string} password 原密码 31 | * @param {string} newPassword 新密码 32 | */ 33 | modifyPass(data: ModifyPass) { 34 | return this.postReq({ url: 'Basic.ModifyPass', data }); 35 | } 36 | 37 | /** 38 | * 用户信息 39 | */ 40 | getUserInfo() { 41 | return this.getReq({ url: 'Basic.UserInfo' }); 42 | } 43 | 44 | /** 45 | * 获取图形验证码 46 | */ 47 | getGraphicCode() { 48 | return this.getReq({ url: 'Basic.GraphicCode' }); 49 | } 50 | 51 | /** 52 | * 菜单列表 53 | */ 54 | getMenuList(data = {}) { 55 | return this.getReq({ url: 'Basic.MenuList', data }); 56 | } 57 | 58 | /** 59 | * 获取数据字典 60 | * @params {string[]} dictTypeList 61 | * @params {string} systemProjectCode 62 | */ 63 | getDict( 64 | data: { systemProjectCode?: string; dictTypeList: Array } = { 65 | systemProjectCode: 'oms', 66 | dictTypeList: [], 67 | } 68 | ) { 69 | return this.postReq({ url: 'Basic.GetDict', data }); 70 | } 71 | 72 | /** 73 | * 查询所有任务Job带有分页列表 74 | */ 75 | getTaskList(data: TaskList) { 76 | return this.postReq({ url: 'Basic.TaskList', data }); 77 | } 78 | 79 | /** 80 | * 查询所有任务Job详情带有分页列表 81 | */ 82 | getTaskDetailList(data: TaskDetailList) { 83 | return this.postReq({ url: 'Basic.TaskDetailList', data }); 84 | } 85 | } 86 | 87 | // 单列模式返回对象 88 | let instance; 89 | export default (() => { 90 | if (instance) return instance; 91 | instance = new Basic(); 92 | return instance; 93 | })(); 94 | -------------------------------------------------------------------------------- /src/api/types/basic.ts: -------------------------------------------------------------------------------- 1 | import { CommonPageList } from './index'; 2 | 3 | export interface AuthLogin { 4 | account: string; 5 | password: string; 6 | captchaCode: string; 7 | captchaCodeToken: string; 8 | } 9 | 10 | export interface ModifyPass { 11 | password: string; 12 | newPassword: string; 13 | } 14 | 15 | export interface TaskList extends CommonPageList { 16 | queryParam?: { 17 | taskName?: string; 18 | taskStatus?: string; 19 | createTimeStart?: string; 20 | createTimeEnd?: string; 21 | }; 22 | } 23 | 24 | export interface TaskDetailList extends CommonPageList { 25 | queryParam?: { 26 | taskCode?: string; 27 | resultEnum?: string; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /src/api/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './basic'; 2 | 3 | export interface CustomResponse { 4 | readonly status: boolean; 5 | readonly message: string; 6 | data: any; 7 | origin?: any; 8 | } 9 | 10 | export interface CommonPageList { 11 | pageNum?: number; 12 | pageSize?: number; 13 | pageRemark?: string; 14 | sortBy?: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/scss/_base.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | // 基础配置 4 | @import './_config.scss'; 5 | 6 | // 公共类 7 | @import './utils/_class.scss'; 8 | 9 | // mixin 10 | @import './utils/_mixin.scss'; 11 | @import './utils/_animation.scss'; 12 | @import './utils/_background.scss'; 13 | @import './utils/_border.scss'; 14 | @import './utils/_box.scss'; 15 | @import './utils/_css3.scss'; 16 | @import './utils/_font.scss'; 17 | @import './utils/_function.scss'; 18 | @import './utils/_group.scss'; 19 | @import './utils/_position.scss'; 20 | @import './utils/_size.scss'; 21 | @import './utils/_text.scss'; 22 | -------------------------------------------------------------------------------- /src/assets/scss/_common.scss: -------------------------------------------------------------------------------- 1 | @import '_base.scss'; 2 | 3 | .flex { 4 | display: flex; 5 | } 6 | 7 | .flex-column { 8 | flex-direction: column; 9 | } 10 | 11 | .flex-center { 12 | justify-content: center; 13 | } 14 | 15 | .flex-1 { 16 | flex: 1; 17 | } 18 | 19 | .align-center { 20 | align-items: center; 21 | } 22 | 23 | .f-9 { 24 | font-size: 9px !important; 25 | } 26 | 27 | .f-12 { 28 | font-size: 12px !important; 29 | } 30 | 31 | .f-14 { 32 | font-size: 14px !important; 33 | } 34 | 35 | .f-16 { 36 | font-size: 16px !important; 37 | } 38 | 39 | .f-18 { 40 | font-size: 18px !important; 41 | } 42 | 43 | .f-20 { 44 | font-size: 20px !important; 45 | } 46 | 47 | .f-24 { 48 | font-size: 24px !important; 49 | } 50 | 51 | .f-30 { 52 | font-size: 30px !important; 53 | } 54 | 55 | .text-black { 56 | color: $textcolor1 !important; 57 | } 58 | 59 | .text-dark { 60 | color: $textcolor2 !important; 61 | } 62 | 63 | .text-dark-gray { 64 | color: $textcolor3 !important; 65 | } 66 | 67 | .text-gray { 68 | color: $textcolor4 !important; 69 | } 70 | 71 | .text-warning { 72 | color: #f7b500 !important; 73 | } 74 | 75 | .text-primary { 76 | color: $mainblue !important; 77 | } 78 | 79 | .text-primary-light { 80 | color: #8786bf !important; 81 | } 82 | 83 | .text-pink { 84 | color: $mainpink !important; 85 | } 86 | 87 | .text-danger { 88 | color: $subcolor2 !important; 89 | } 90 | 91 | .text-success { 92 | color: rgba(22, 185, 22, 0.603); 93 | } 94 | 95 | .h-1 { 96 | line-height: 1; 97 | } 98 | 99 | .m-t-0 { 100 | margin-top: 0; 101 | } 102 | 103 | .m-l-5 { 104 | margin-left: 5px; 105 | } 106 | 107 | .m-r-5 { 108 | margin-right: 5px; 109 | } 110 | 111 | .m-b-5 { 112 | margin-bottom: 5px; 113 | } 114 | 115 | .m-t-5 { 116 | margin-top: 5px; 117 | } 118 | 119 | .m-t-15 { 120 | margin-top: 15px !important; 121 | } 122 | 123 | .m-r-15 { 124 | margin-right: 15px !important; 125 | } 126 | 127 | .m-t-15 { 128 | margin-top: 15px !important; 129 | } 130 | 131 | .m-b-15 { 132 | margin-bottom: 15px !important; 133 | } 134 | 135 | .m-t-20 { 136 | margin-top: 20px !important; 137 | } 138 | 139 | .m-t-30 { 140 | margin-top: 30px !important; 141 | } 142 | 143 | .m-b-0 { 144 | margin-bottom: 0 !important; 145 | } 146 | 147 | .m-b-10 { 148 | margin-bottom: 10px !important; 149 | } 150 | 151 | .m-t-10 { 152 | margin-top: 10px !important; 153 | } 154 | 155 | .m-l-10 { 156 | margin-left: 10px !important; 157 | } 158 | 159 | .m-r-10 { 160 | margin-right: 10px !important; 161 | } 162 | 163 | .m-b-20 { 164 | margin-bottom: 20px; 165 | } 166 | 167 | .m-l-25 { 168 | margin-left: 25px !important; 169 | } 170 | 171 | .m-l-20 { 172 | margin-left: 20px !important; 173 | } 174 | 175 | .m-t-25 { 176 | margin-top: 25px !important; 177 | } 178 | 179 | .m-b-25 { 180 | margin-bottom: 25px !important; 181 | } 182 | 183 | .m-r-25 { 184 | margin-right: 25px !important; 185 | } 186 | 187 | .m-b-30 { 188 | margin-bottom: 30px !important; 189 | } 190 | 191 | .m-l-50 { 192 | margin-left: 50px; 193 | } 194 | 195 | .m-t-50 { 196 | margin-top: 50px; 197 | } 198 | 199 | .m-b-30 { 200 | margin-bottom: 30px; 201 | } 202 | 203 | .m-t-4 { 204 | margin-top: 4px; 205 | } 206 | 207 | .m-t-5 { 208 | margin-top: 5px !important; 209 | } 210 | 211 | .m-t-8 { 212 | margin-top: 8px; 213 | } 214 | 215 | .m-r-8 { 216 | margin-right: 8px; 217 | } 218 | 219 | .m-r-10 { 220 | margin-right: 10px; 221 | } 222 | 223 | .m-l-10 { 224 | margin-left: 10px; 225 | } 226 | 227 | .p-t-10 { 228 | padding-top: 10px; 229 | } 230 | 231 | .p-t-20 { 232 | padding-top: 20px; 233 | } 234 | 235 | .p-b-20 { 236 | padding-bottom: 20px; 237 | } 238 | 239 | .f-w-500 { 240 | font-weight: 500; 241 | } 242 | 243 | .f-w-b { 244 | font-weight: bold !important; 245 | } 246 | 247 | .f-w-normal { 248 | font-weight: normal !important; 249 | } 250 | 251 | .number { 252 | font-family: HelveticaNeue-Medium, HelveticaNeue; 253 | } 254 | 255 | .float-right { 256 | float: right; 257 | } 258 | 259 | .float-left { 260 | float: left; 261 | } 262 | 263 | .overflow-hidden { 264 | overflow: hidden; 265 | } 266 | 267 | .w-full { 268 | width: 100% !important; 269 | } 270 | 271 | .h-full { 272 | height: 100%; 273 | } 274 | 275 | .text-left { 276 | text-align: left; 277 | } 278 | 279 | .text-center { 280 | text-align: center; 281 | } 282 | 283 | .text-right { 284 | text-align: right; 285 | } 286 | 287 | .inline-block { 288 | display: inline-block !important; 289 | } 290 | 291 | .block { 292 | display: block; 293 | } 294 | 295 | .b-b { 296 | border-bottom: 1px solid rgba(232, 233, 234, 1); 297 | } 298 | 299 | .ellipsis { 300 | @include tofl(100%); 301 | } 302 | 303 | .cursor-pointer { 304 | cursor: pointer; 305 | } 306 | 307 | .relative { 308 | position: relative; 309 | } 310 | 311 | .absolute { 312 | position: absolute; 313 | } 314 | -------------------------------------------------------------------------------- /src/assets/scss/_config.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | $vendors: webkit moz ms o; // css3前缀; 4 | $prefixWebkit: true !default; // 谷歌前缀: webkit前缀 5 | $prefixMozilla: true !default; // 火狐前缀: moz前缀 6 | $prefixMicrosoft: true !default; // IE前缀: ms前缀 7 | $prefixOpera: true !default; // opera前缀: o前缀 8 | 9 | // 单位; 10 | $global-unit: px; 11 | 12 | // colors 13 | $backcolor: #f6f7fb; 14 | // 主要配色 15 | $mainblue: #5755b3; 16 | $mainbluelight: #8786bf; 17 | $mainpink: #f1b7d0; 18 | $maincyan: #6fd0ee; 19 | // 配色延伸 20 | $subcolor1: #1c1980; 21 | $subcolor2: #b95881; 22 | $subcolor3: #0aa0cc; 23 | $subcolor4: #bebdea; 24 | $subcolor5: #f6d7e4; 25 | $subcolor6: #bff0ff; 26 | // 黑色递进 27 | $textcolor1: #222530; 28 | $textcolor2: #6f7178; 29 | $textcolor3: #909297; 30 | $textcolor4: #c8c8cb; 31 | // 边框色 32 | $bordercolor: #c8c8cb; 33 | // fonts 34 | 35 | // weight 36 | -------------------------------------------------------------------------------- /src/assets/scss/_reset.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | /* 样式重置 */ 4 | html, 5 | body, 6 | div, 7 | span, 8 | applet, 9 | object, 10 | iframe, 11 | h1, 12 | h2, 13 | h3, 14 | h4, 15 | h5, 16 | h6, 17 | p, 18 | blockquote, 19 | pre, 20 | a, 21 | abbr, 22 | acronym, 23 | address, 24 | big, 25 | cite, 26 | code, 27 | del, 28 | dfn, 29 | em, 30 | font, 31 | img, 32 | ins, 33 | kbd, 34 | q, 35 | s, 36 | samp, 37 | small, 38 | strike, 39 | strong, 40 | sub, 41 | sup, 42 | tt, 43 | var, 44 | dd, 45 | dl, 46 | dt, 47 | li, 48 | ol, 49 | ul, 50 | i, 51 | fieldset, 52 | form, 53 | label, 54 | legend { 55 | margin: 0; 56 | padding: 0; 57 | border: 0 none; 58 | outline: 0; 59 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 60 | } 61 | 62 | img { 63 | display: block; 64 | border: 0 none; 65 | } 66 | 67 | p { 68 | margin-bottom: 0; 69 | } 70 | 71 | body, 72 | a, 73 | input, 74 | textarea, 75 | button { 76 | font-family: 'Microsoft YaHei', '微软雅黑', 'PingFang SC', 'Helvetica Neue', 77 | Helvetica, 'Hiragino Sans GB', Arial, sans-serif; 78 | font-size: 14px; 79 | font-weight: normal; 80 | outline: none; 81 | border: 0; 82 | padding: 0; 83 | color: #666; 84 | text-decoration: none; 85 | } 86 | 87 | input, 88 | input::placeholder, 89 | textarea, 90 | textarea::placeholder { 91 | font-family: 'Microsoft YaHei', '微软雅黑', 'PingFang SC', 'Helvetica Neue', 92 | Helvetica, 'Hiragino Sans GB', Arial, sans-serif; 93 | } 94 | 95 | input::-webkit-outer-spin-button, 96 | input::-webkit-inner-spin-button { 97 | -webkit-appearance: none !important; 98 | } 99 | 100 | input[type='number'] { 101 | -moz-appearance: textfield; 102 | /* firefox */ 103 | } 104 | 105 | ul, 106 | li { 107 | list-style-type: none; 108 | padding: 0; 109 | margin: 0; 110 | } 111 | 112 | /* 清除点击出现虚拟框 */ 113 | a { 114 | color: #0aa0cc; 115 | outline: none; 116 | text-decoration: none; 117 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 118 | 119 | &:focus { 120 | outline: 0; 121 | } 122 | 123 | &:hover { 124 | text-decoration: none; 125 | } 126 | 127 | &:link, 128 | &:visited { 129 | color: #0aa0cc; 130 | text-decoration: none; 131 | } 132 | } 133 | 134 | input, 135 | textarea, 136 | select { 137 | outline: none; 138 | // 兼容firefox 139 | filter: none; 140 | 141 | // 兼容chrome/edge/safari 142 | &:-webkit-autofill { 143 | box-shadow: none; 144 | } 145 | } 146 | 147 | table { 148 | text-align: center; 149 | vertical-align: middle; 150 | } 151 | 152 | th { 153 | text-align: center; 154 | vertical-align: middle; 155 | } 156 | -------------------------------------------------------------------------------- /src/assets/scss/component/_custom.scss: -------------------------------------------------------------------------------- 1 | @import '../_base.scss'; 2 | 3 | // 动态placeholder样式 4 | .design-form { 5 | position: relative; 6 | width: fit-content; 7 | .input-label { 8 | display: none; 9 | position: absolute; 10 | left: 10px; 11 | height: 20px; 12 | padding: 0 5px; 13 | font-size: 12px; 14 | line-height: 20px; 15 | color: #a2a9b6; 16 | pointer-events: none; 17 | transform-origin: 0 0; 18 | transition: 0.3s transform; 19 | transform: translateY(50%); 20 | } 21 | .el-input__inner:not(:placeholder-shown) ~ .input-label, 22 | .el-input__inner:focus ~ .input-label { 23 | transform: scale(0.85) translate(0, -50%) !important; 24 | background-color: #fff; 25 | } 26 | .el-input__inner:placeholder-shown::placeholder { 27 | color: transparent !important; 28 | } 29 | @media screen and (-webkit-min-device-pixel-ratio: 0) { 30 | .input-label { 31 | display: initial; 32 | } 33 | } 34 | @-moz-document url-prefix() { 35 | .input-label { 36 | display: initial; 37 | } 38 | } 39 | } 40 | // 列表按钮操作区 41 | .operate-area { 42 | display: flex; 43 | align-items: center; 44 | justify-content: space-between; 45 | margin: 14px 0 14px -6px; 46 | .el-button { 47 | &.add { 48 | height: 30px; 49 | padding: 0 24px; 50 | border: none; 51 | border-radius: 15px; 52 | font-size: 12px; 53 | font-weight: 600; 54 | line-height: 30px; 55 | text-align: center; 56 | color: #fff; 57 | background-color: $mainblue; 58 | &:hover, 59 | &:focus { 60 | background-color: $mainblue; 61 | box-shadow: 0px 10px 10px -4px rgba(87, 85, 179, 0.15); 62 | } 63 | } 64 | &.normal { 65 | height: 30px; 66 | padding: 0 40px; 67 | border: none; 68 | border-radius: 15px; 69 | font-size: 12px; 70 | font-weight: 600; 71 | line-height: 30px; 72 | text-align: center; 73 | color: $mainblue; 74 | border: 1px solid #e9ecf5; 75 | background-color: #fff; 76 | 77 | &:hover, 78 | &:focus { 79 | background-color: #fff; 80 | box-shadow: 0px 10px 10px -4px rgba(147, 147, 176, 0.07); 81 | } 82 | } 83 | } 84 | .double { 85 | display: flex; 86 | align-items: center; 87 | & + .el-button { 88 | margin-left: 24px; 89 | } 90 | & + .double { 91 | margin-left: 24px; 92 | } 93 | .add, 94 | .normal { 95 | padding: 0 20px; 96 | &:first-of-type { 97 | position: relative; 98 | border-radius: 15px 0 0 15px; 99 | &::after { 100 | position: absolute; 101 | right: 0; 102 | top: 50%; 103 | transform: translateY(-50%); 104 | content: ''; 105 | width: 1px; 106 | height: 16px; 107 | background-color: #fff; 108 | opacity: 0.3; 109 | } 110 | } 111 | &:last-of-type { 112 | margin-left: 0; 113 | border-radius: 0 15px 15px 0; 114 | } 115 | } 116 | .normal { 117 | &:first-of-type { 118 | border-right: none; 119 | &::after { 120 | background-color: #e9ecf5; 121 | } 122 | } 123 | &:last-of-type { 124 | border-left: none; 125 | } 126 | } 127 | } 128 | .item { 129 | margin: 6px; 130 | } 131 | .btn-container { 132 | display: flex; 133 | align-items: center; 134 | flex-wrap: wrap; 135 | } 136 | .round-input { 137 | box-sizing: border-box; 138 | display: flex; 139 | align-items: center; 140 | width: 240px; 141 | height: 32px; 142 | padding: 0 5px; 143 | margin-left: 36px; 144 | border-radius: 16px; 145 | border: 1px solid #c8c8cb; 146 | background-color: #fff; 147 | 148 | &:hover { 149 | border-color: #f1b7d0; 150 | } 151 | 152 | .iconfont { 153 | display: flex; 154 | align-items: center; 155 | justify-content: center; 156 | flex-shrink: 0; 157 | width: 30px; 158 | height: 30px; 159 | font-size: 14px; 160 | color: #c8c8cb; 161 | } 162 | 163 | .search { 164 | flex: 1; 165 | font-size: 12px; 166 | color: #222530; 167 | 168 | &::placeholder { 169 | font-size: 12px; 170 | color: #909297; 171 | } 172 | } 173 | 174 | .button { 175 | display: flex; 176 | align-items: center; 177 | justify-content: center; 178 | flex-shrink: 0; 179 | width: 40px; 180 | height: 24px; 181 | border-radius: 16px; 182 | background-color: #5755b3; 183 | font-size: 12px; 184 | font-weight: 600; 185 | color: #fff; 186 | cursor: pointer; 187 | } 188 | } 189 | } 190 | // 表格按钮操作区 191 | .operate .cell { 192 | display: flex; 193 | align-items: center; 194 | .circle { 195 | display: flex; 196 | align-items: center; 197 | justify-content: center; 198 | flex-shrink: 0; 199 | width: 30px; 200 | height: 30px; 201 | min-height: initial; 202 | padding: 0; 203 | border-radius: 50%; 204 | background-color: #fff; 205 | border: 1px solid #e8e9ea; 206 | > span { 207 | display: flex; 208 | } 209 | & + .circle { 210 | margin-left: 20px; 211 | } 212 | &:hover { 213 | border-color: $mainpink; 214 | } 215 | &.is-disabled { 216 | opacity: 0.3; 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/assets/scss/component/_richtext.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | .rich-text { 4 | padding-bottom: 0.1rem; 5 | word-wrap: break-word; 6 | background-color: #fff; 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_animation.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 动画混合宏(mixin); 3 | 4 | /////////////////////////////////////////////////////////////////////////// 5 | // css3动画,只能写在调用页面,配合.css3( @style,@frames )使用; // 6 | // 例子 : // 7 | // @include keyframes(animation-name) { // 8 | // 0% { // 9 | // transform: translate3d(100%, 0, 0); // 10 | // } // 11 | // 100% { // 12 | // transform: translate3d(0%, 0, 0); // 13 | // } // 14 | // } // 15 | // .className{ @include css3(animation,animation-name 5s infinite); } // 16 | /////////////////////////////////////////////////////////////////////////// 17 | @mixin keyframes($animationName) { 18 | @-webkit-keyframes #{$animationName} { 19 | @content; 20 | } 21 | @-moz-keyframes #{$animationName} { 22 | @content; 23 | } 24 | @-ms-keyframes #{$animationName} { 25 | @content; 26 | } 27 | @-o-keyframes #{$animationName} { 28 | @content; 29 | } 30 | @keyframes #{$animationName} { 31 | @content; 32 | } 33 | } 34 | 35 | // animation动画; 36 | @mixin ani($animations) { 37 | @include css3(animation, $animations); 38 | } 39 | 40 | // animation动画名称; 41 | @mixin ani-name($names) { 42 | @include css3(animation-name, $names); 43 | } 44 | 45 | // animation动画成一个周期所需要的时间; 46 | @mixin ani-dur($times: 0.2s) { 47 | @include css3(animation-duration, $times); 48 | } 49 | 50 | // animation动画速度形式; 51 | @mixin ani-time($motions: linear) { 52 | // linear : 动画从头到尾的速度是相同的; 53 | // ease : 默认。动画以低速开始,然后加快,在结束前变慢; 54 | // ease-in : 动画以低速开始; 55 | // ease-out : 动画以低速结束; 56 | // ease-in-out : 动画以低速开始和结束; 57 | // cubic-bezier(n,n,n,n) : 在 cubic-bezier 函数中自己的值。可能的值是从 0 到 1 的数值; 58 | @include css3(animation-timing-function, $motions); 59 | } 60 | 61 | //animation动画播放次数; 62 | @mixin ani-itc($values: 1) { 63 | // (播放次数) | infinite(无限次播放); 64 | @include css3(animation-iteration-count, $values); 65 | } 66 | 67 | // animation动画轮流反向播放动画; 68 | @mixin ani-dir($directions: alternate) { 69 | // normal(动画应该正常播放) | alternate(动画应该轮流反向播放); 70 | @include css3(animation-direction, $directions); 71 | } 72 | 73 | // animation动画"播放"或"暂停"; 74 | @mixin ani-play($states: running) { 75 | // paused(暂停) | running(运行); 76 | @include css3(animation-play-state, $states); 77 | } 78 | 79 | // animation动画延迟时间设置; 80 | @mixin ani-del($times: 0.2s) { 81 | @include css3(animation-delay, $times); 82 | } 83 | 84 | // animation动画运动完成后的状态设置; 85 | @mixin ani-fill($modes: forwards) { 86 | // none(默认值) | forwards(动画结束时的状态) | backwards(动画开始时的状态) | both(动画结束或开始的状态); 87 | @include css3(animation-fill-mode, $modes); 88 | } 89 | 90 | // 三维闪动 bug 处理; 91 | @mixin trf-fix() { 92 | -webkit-backface-visibility: hidden; 93 | -webkit-transform-style: preserve-3d; 94 | } 95 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_background.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 背景的混合宏(mixin); 3 | 4 | // 设置背景; 5 | @mixin bg($color, $img: false, $repeat: no-repeat, $position: 0 0) { 6 | @if ($img) { 7 | background: url($img) $color $repeat addUnits($position); 8 | } @else { 9 | @include bgc($color); 10 | } 11 | } 12 | 13 | //设置图片背景; 14 | @mixin bgi($img: false, $repeat: no-repeat, $position: 0 0) { 15 | @if (not $position) { 16 | background: url($img) $repeat; 17 | } @else if(not $repeat) { 18 | background: url($img) addUnits($position); 19 | } @else if(not $position and not $repeat) { 20 | background: url($img); 21 | } @else { 22 | background: url($img) $repeat addUnits($position); 23 | } 24 | } 25 | 26 | // 设置图片背景 && background-size; 27 | @mixin bgiz($img: false, $repeat: no-repeat, $position: 0 0, $size: cover) { 28 | @include bgi($img, $repeat, $position); 29 | @include bgz($size); 30 | } 31 | 32 | //设置背景颜色; 33 | @mixin bgc($color) { 34 | background-color: $color; 35 | } 36 | 37 | // 设置background-size; 38 | @mixin bgz($value: cover) { 39 | background-size: addUnits($value); 40 | } 41 | 42 | // 禁用样式disabled 43 | @mixin disabled($bgColor: #e6e6e6, $textColor: #ababab) { 44 | background-color: $bgColor !important; 45 | color: $textColor !important; 46 | cursor: not-allowed !important; 47 | } 48 | 49 | // 透明度 @include myOpacity(0.3,93,197,16); 50 | @mixin myOpacity($opacity: 0.5, $r: 0, $g: 0, $b: 0) { 51 | $xx: $opacity * 100; 52 | filter: alpha(opacity=$xx); 53 | background-color: rgba($r, $g, $b, $opacity); 54 | } 55 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_border.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 边框的混合宏(mixin); 3 | 4 | // border; 5 | @mixin bd($args...) { 6 | border: addUnits($args...); 7 | } 8 | 9 | // border-top; 10 | @mixin bdt($args...) { 11 | border-top: addUnits($args...); 12 | } 13 | // border-right; 14 | @mixin bdr($args...) { 15 | border-right: addUnits($args...); 16 | } 17 | 18 | // border-bottom; 19 | @mixin bdb($args...) { 20 | border-bottom: addUnits($args...); 21 | } 22 | 23 | // border-left; 24 | @mixin bdl($args...) { 25 | border-left: addUnits($args...); 26 | } 27 | 28 | // border-top && border-bottom; 29 | @mixin bdtb($args1, $args2: $args1) { 30 | border-top: addUnits($args1); 31 | border-bottom: addUnits($args2); 32 | } 33 | 34 | // border-right && border-left; 35 | @mixin bdrl($args1, $args2: $args1) { 36 | border-right: addUnits($args1); 37 | border-left: addUnits($args2); 38 | } 39 | 40 | // border-color; 41 | @mixin bdc($args...) { 42 | border-color: $args; 43 | } 44 | 45 | // 边框图片; 46 | @mixin bdi($image) { 47 | @include css3(border-image, $image, false, true); 48 | } 49 | 50 | // 取消边框; 51 | @mixin no-bd { 52 | border: none; 53 | } 54 | 55 | // 取消上边框; 56 | @mixin no-bdt { 57 | border-top: none; 58 | } 59 | 60 | // 取消右边框; 61 | @mixin no-bdr { 62 | border-right: none; 63 | } 64 | 65 | // 取消下边框; 66 | @mixin no-bdb { 67 | border-bottom: none; 68 | } 69 | 70 | // 取消左边框; 71 | @mixin no-bdl { 72 | border-left: none; 73 | } 74 | 75 | // 取消上边框&&下边框; 76 | @mixin no-bdtb { 77 | border-top: none; 78 | border-bottom: none; 79 | } 80 | 81 | // 取消右边框&&左边框; 82 | @mixin no-bdrl { 83 | border-right: none; 84 | border-left: none; 85 | } 86 | 87 | // 描边; 88 | @mixin oln($args...) { 89 | outline: addUnits($args...); 90 | } 91 | 92 | // 描边的颜色; 93 | @mixin oln-c($args...) { 94 | outline-color: $args; 95 | } 96 | 97 | // 描边的圆角; 98 | @mixin oln-r($args...) { 99 | outline-radius: addUnits($args); 100 | } 101 | 102 | // 描边的样式; 103 | @mixin oln-s($args: solid) { 104 | outline-style: $args; 105 | } 106 | 107 | // 边框的宽度; 108 | @mixin oln-w($args...) { 109 | outline-width: addUnits($args); 110 | } 111 | 112 | // 描边的偏移; 113 | @mixin oln-o($args) { 114 | outline-offset: addUnits($args); 115 | } 116 | 117 | // 分割线 |; 118 | @mixin cut-line($mar: 0 10px, $color: #999, $fz: 14) { 119 | @include mar($mars); 120 | @include c($color); 121 | @include fz($fz); 122 | } 123 | 124 | // 分割线 / (面包屑导航); 125 | @mixin cut-line2($pad: 0 10px, $color: #ccc, $fz: 14) { 126 | &:before { 127 | @include pad($pad); 128 | @include c($color); 129 | @include fz($fz); 130 | content: '/\00a0'; 131 | } 132 | } 133 | 134 | // 细边框 135 | @mixin thin-line($borderColor, $borderRadius) { 136 | position: relative; 137 | &:after { 138 | content: ''; 139 | position: absolute; 140 | top: 0; 141 | left: 0; 142 | box-sizing: border-box; 143 | width: 200%; 144 | height: 200%; 145 | transform: scale(0.5); 146 | transform-origin: left top; 147 | border: 1px solid $borderColor; 148 | border-radius: $borderRadius; 149 | pointer-events: none; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_box.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 边距的混合宏; 3 | 4 | // 设置margin; 5 | @mixin mar($size...) { 6 | margin: addUnits($size...); 7 | } 8 | 9 | // 设置padding; 10 | @mixin pad($size...) { 11 | padding: addUnits($size...); 12 | } 13 | 14 | // 设置margin-top; 15 | @mixin mt($size) { 16 | margin-top: addUnits($size); 17 | } 18 | 19 | // 设置margin-right; 20 | @mixin mr($size) { 21 | margin-right: addUnits($size); 22 | } 23 | 24 | // 设置margin-bottom; 25 | @mixin mb($size) { 26 | margin-bottom: addUnits($size); 27 | } 28 | 29 | // 设置margin-left; 30 | @mixin ml($size) { 31 | margin-left: addUnits($size); 32 | } 33 | 34 | // 设置padding-top; 35 | @mixin pt($size) { 36 | padding-top: addUnits($size); 37 | } 38 | 39 | // 设置padding-right; 40 | @mixin pr($size) { 41 | padding-right: addUnits($size); 42 | } 43 | 44 | // 设置padding-bottom; 45 | @mixin pb($size) { 46 | padding-bottom: addUnits($size); 47 | } 48 | 49 | // 设置padding-left; 50 | @mixin pl($size) { 51 | padding-left: addUnits($size); 52 | } 53 | 54 | // 设置margin-top && margin-bottom; 55 | @mixin mtb($size1, $size2: $size1) { 56 | margin-top: addUnits($size1); 57 | margin-bottom: addUnits($size2); 58 | } 59 | 60 | // 设置margin-left && margin-right; 61 | @mixin mlr($size1, $size2: $size1) { 62 | margin-left: addUnits($size1); 63 | margin-right: addUnits($size2); 64 | } 65 | 66 | // 设置padding-top && padding-bottom; 67 | @mixin ptb($size1, $size2: $size1) { 68 | padding-top: addUnits($size1); 69 | padding-bottom: addUnits($size2); 70 | } 71 | 72 | // 设置padding-left && padding-right; 73 | @mixin plr($size1, $size2: $size1) { 74 | padding-left: addUnits($size1); 75 | padding-right: addUnits($size2); 76 | } 77 | 78 | // margin | padding 79 | @mixin distance($margin, $padding: 0) { 80 | margin: $margin; 81 | padding: $padding; 82 | } 83 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_class.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | /* 清除浮动 */ 4 | %clearfix { 5 | zoom: 1; 6 | &::after { 7 | display: block; 8 | height: 0; 9 | content: '.'; 10 | clear: both; 11 | overflow: hidden; 12 | visibility: hidden; 13 | } 14 | } 15 | 16 | /* 隐藏元素 @extend %visuallyhidden; */ 17 | %visuallyhidden { 18 | position: absolute; 19 | width: 1px; 20 | height: 1px; 21 | padding: 0; 22 | margin: -1px; 23 | overflow: hidden; 24 | clip: rect(0 0 0 0); 25 | clip: rect(0, 0, 0, 0); 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_css3.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | // css3的混合宏(mixin); 4 | ////////////////////////////////////////////////////// 5 | // css3前缀: // 6 | // $style : 样式名 ; // 7 | // $frames : 样式内容; // 8 | // $prefix : 是否添加样式前缀,默认开启; // 9 | // $suffix : 是否添加样式后缀,默认关闭; // 10 | ////////////////////////////////////////////////////// 11 | @mixin css3($style, $frames, $prefix: true, $suffix: false) { 12 | @if ($prefix) { 13 | @if ($suffix) { 14 | @each $goods in $vendors { 15 | @if ($prefixWebkit and $goods == 'webkit') { 16 | -#{$goods}-#{$style}: -#{$goods}-#{$frames}; 17 | } @else if($prefixMozilla and $goods == 'moz') { 18 | -#{$goods}-#{$style}: -#{$goods}-#{$frames}; 19 | } @else if($prefixMicrosoft and $goods == 'ms') { 20 | -#{$goods}-#{$style}: -#{$goods}-#{$frames}; 21 | } @else if($prefixOpera and $goods == 'o') { 22 | -#{$goods}-#{$style}: -#{$goods}-#{$frames}; 23 | } 24 | } 25 | #{$style}: #{$frames}; 26 | } @else { 27 | @each $goods in $vendors { 28 | @if ($prefixWebkit and $goods == 'webkit') { 29 | -#{$goods}-#{$style}: #{$frames}; 30 | } @else if($prefixMozilla and $goods == 'moz') { 31 | -#{$goods}-#{$style}: #{$frames}; 32 | } @else if($prefixMicrosoft and $goods == 'ms') { 33 | -#{$goods}-#{$style}: #{$frames}; 34 | } @else if($prefixOpera and $goods == 'o') { 35 | -#{$goods}-#{$style}: #{$frames}; 36 | } 37 | } 38 | #{$style}: #{$frames}; 39 | } 40 | } @else { 41 | @if ($suffix) { 42 | @each $goods in $vendors { 43 | @if ($prefixWebkit and $goods == 'webkit') { 44 | #{$style}: -#{$goods}-#{$frames}; 45 | } @else if($prefixMozilla and $goods == 'moz') { 46 | #{$style}: -#{$goods}-#{$frames}; 47 | } @else if($prefixMicrosoft and $goods == 'ms') { 48 | #{$style}: -#{$goods}-#{$frames}; 49 | } @else if($prefixOpera and $goods == 'o') { 50 | #{$style}: -#{$goods}-#{$frames}; 51 | } 52 | } 53 | #{$style}: #{$frames}; 54 | } @else { 55 | #{$style}: #{$frames}; 56 | } 57 | } 58 | } 59 | 60 | // 圆角 @include radius(24,100%); 61 | @mixin radius($num: 1234, $size: 5px) { 62 | @if $num==1234 { 63 | @include css3(border-radius, $size); 64 | } @else if $num==12 { 65 | @include css3(border-top-left-radius, $size); 66 | @include css3(border-top-right-radius, $size); 67 | } @else if $num==23 { 68 | @include css3(border-top-right-radius, $size); 69 | @include css3(border-bottom-right-radius, $size); 70 | } @else if $num==34 { 71 | @include css3(border-bottom-right-radius, $size); 72 | @include css3(border-bottom-left-radius, $size); 73 | } @else if $num==14 { 74 | @include css3(border-bottom-left-radius, $size); 75 | @include css3(border-top-left-radius, $size); 76 | } @else if $num==13 { 77 | @include css3(border-top-left-radius, $size); 78 | @include css3(border-bottom-right-radius, $size); 79 | } @else if $num==24 { 80 | @include css3(border-top-right-radius, $size); 81 | @include css3(border-bottom-left-radius, $size); 82 | } 83 | } 84 | 85 | // 盒子阴影 @include boxshadow(24,100%); 86 | @mixin boxshadow($shadow...) { 87 | @include css3(box-shadow, $shadow); 88 | } 89 | 90 | // 设置content; 91 | @mixin cnt($content...) { 92 | @if (length($content) >=1) { 93 | content: $content; 94 | } @else { 95 | content: ''; 96 | } 97 | } 98 | 99 | //是否允许用户选中文本; 100 | @mixin usr-s($arg: text) { 101 | @include css3(user-select, $arg); 102 | } 103 | 104 | // 禁用元素事件 105 | // 1. 阻止任何点击动作的执行 106 | // 2. 使链接显示为默认光标(cursor:default) 107 | // 3. 阻止触发hover和active状态 108 | // 4. 阻止JavaScript点击事件的触发 109 | @mixin pe($arg: none) { 110 | pointer-events: $arg; 111 | } 112 | 113 | // 美化占位符 placeholder 样式 114 | @mixin beauty-placeholder($fz: 12, $color: #999, $align: left) { 115 | &:-moz-placeholder { 116 | @include fz($fz); 117 | @include c($color); 118 | @include ta($align); 119 | } 120 | &:-ms-input-placeholder { 121 | @include fz($fz); 122 | @include c($color); 123 | @include ta($align); 124 | } 125 | &::-webkit-input-placeholder { 126 | @include fz($fz); 127 | @include c($color); 128 | @include ta($align); 129 | } 130 | } 131 | 132 | // 美化选中文本 133 | @mixin beauty-select($bg: #00f, $color: #fff, $obj: '') { 134 | #{$obj}::selection { 135 | background: $bg; 136 | color: $color; 137 | } 138 | #{$obj}::-moz-selection { 139 | background: $bg; 140 | color: $color; 141 | } 142 | #{$obj}::-webkit-selection { 143 | background: $bg; 144 | color: $color; 145 | } 146 | #{$obj}::-ms-selection { 147 | background: $bg; 148 | color: $color; 149 | } 150 | #{$obj}::-o-selection { 151 | background: $bg; 152 | color: $color; 153 | } 154 | } 155 | 156 | /////////////////////////////////////////////////////////////////////////// 157 | // css3 滚动条; // 158 | // 例子 : // 159 | // @include scrollbar(scrollbar-track){ // 160 | // #{$browser}box-shadow: inset 0 0 6px rgba(0,0,0,0.3); // 161 | // background-color: #f5f5f5; // 162 | // }; // 163 | // @include scrollbar(scrollbar){ // 164 | // width: 12px; // 165 | // }; // 166 | // @include scrollbar(scrollbar-thumb){ // 167 | // #{$browser}border-radius: 10px; // 168 | // #{$browser}box-shadow: inset 0 0 6px rgba(0,0,0,.3); // 169 | // background-color: #fbd0c9; // 170 | // }; // 171 | /////////////////////////////////////////////////////////////////////////// 172 | @mixin scrollbar($style, $obj: '') { 173 | #{$obj}::-webkit-#{$style} { 174 | $browser: '-webkit-' !global; 175 | @content; 176 | } 177 | #{$obj}::-moz-#{$style} { 178 | $browser: '-moz-' !global; 179 | @content; 180 | } 181 | #{$obj}::-ms-#{$style} { 182 | $browser: '-ms-' !global; 183 | @content; 184 | } 185 | #{$obj}::-o-#{$style} { 186 | $browser: '-o-' !global; 187 | @content; 188 | } 189 | #{$obj}::#{$style} { 190 | $browser: '' !global; 191 | @content; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_font.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 字体的混合宏(mixin); 3 | 4 | // 字体样式 5 | @mixin fontStyle($size, $weight: 300, $color: $textcolor, $align: center) { 6 | @include fz($size); 7 | @include fb($weight); 8 | @include lh($size); 9 | @include c($color); 10 | @include fz($size); 11 | @include ta($align); 12 | } 13 | 14 | // 设置所有字体属性; 15 | @mixin f($font...) { 16 | @if (length($font) >= 2) { 17 | font: $font; 18 | } @else { 19 | font-size: addUnits(nth($font, 1)); 20 | } 21 | } 22 | 23 | // 设置字号; 24 | @mixin fz($font: 12) { 25 | font-size: addUnits($font); 26 | } 27 | 28 | // 设置字体; 29 | @mixin fa($family: 'Microsoft Yahei') { 30 | font-family: $family, '宋体', '黑体'; 31 | } 32 | 33 | // 文字颜色; 34 | @mixin c($color: #000, $hovercolor: false) { 35 | color: $color; 36 | @if ($hovercolor) { 37 | &:hover { 38 | color: $hovercolor; 39 | } 40 | } 41 | } 42 | 43 | // 文字风格; 44 | @mixin fs($value: bold) { 45 | font-style: $value; 46 | } 47 | 48 | // 文字加粗; 49 | @mixin fb($value: bold) { 50 | font-weight: $value; 51 | } 52 | 53 | // 设置line-height; 54 | @mixin lh($line) { 55 | line-height: addUnits($line); 56 | } 57 | 58 | // 文字透明; 59 | @mixin transparent-text() { 60 | font: 0/0 serif; 61 | text-shadow: none; 62 | color: transparent; 63 | } 64 | 65 | // 字符间距; 66 | @mixin ls($args...) { 67 | letter-spacing: addUnits($args...); 68 | } 69 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_function.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | // 判断是否为数字; 4 | @function TestNum($num1, $num2) { 5 | @if (not unitless($num1) or not unitless($num2)) { 6 | @debug '其中数值有单位'; 7 | @return false; 8 | } 9 | @if (type-of($num1) == number and type-of($num1) == number) { 10 | @return true; 11 | } @else { 12 | @debug '其中有值不数字'; 13 | @return false; 14 | } 15 | } 16 | 17 | // 判断是否为数字返回true/false; 18 | @function returnNum($num) { 19 | @if (type-of($num) == number) { 20 | @return true; 21 | } @else { 22 | @return false; 23 | } 24 | } 25 | 26 | // 判断是否为字符串; 27 | @function returnStr($str, $value) { 28 | @if ($value) { 29 | @if ((type-of($num1) == number) and ($num1 == $value)) { 30 | @warn "字符串相等"; 31 | @return true; 32 | } @else { 33 | @warn "字符串不相等"; 34 | @return false; 35 | } 36 | } @else { 37 | @if (type-of($num1) == number) { 38 | @warn "非字符串"; 39 | @return true; 40 | } @else { 41 | @warn "字符串"; 42 | @return false; 43 | } 44 | } 45 | } 46 | 47 | //添加单位; 48 | @function addUnits($arg1, $arg2...) { 49 | $new-args: join($arg1, $arg2); 50 | $len: length($new-args); 51 | $arr: null; 52 | $new-arg: null; 53 | $argUnit: null; 54 | 55 | @for $i from 1 through $len { 56 | $new-arg: nth($new-args, $i); 57 | @if (type-of($new-arg) == number) { 58 | $argUnit: testUnit($new-arg); 59 | $arr: $arr $argUnit; 60 | } @else { 61 | $arr: $arr $new-arg; 62 | } 63 | } 64 | @return $arr; 65 | } 66 | 67 | //检验单位; 68 | @function testUnit($args) { 69 | $filter-units: px, rem, em, rpx, auto, vw, vh, vmin, vmax, fr, deg; 70 | @each $o in $filter-units { 71 | @if (str-index('#{$args}', $o)) { 72 | @return $args; 73 | } 74 | // 保持数字不变keep缩写k 75 | @else if(str-index('#{$args}', k)) { 76 | $new-args: '' + $args + ''; 77 | $new-len: (str-length($new-args) - 1); 78 | @return unquote(str-slice($new-args, 1, $new-len)); 79 | } 80 | } 81 | @if (unit($args) == '%' or $args == 0) { 82 | @return $args; 83 | } @else if($global-unit == rem) { 84 | @return px2rem($args); 85 | } @else if($global-unit == rpx) { 86 | @return px2rpx($args); 87 | } @else if(unitless($args)) { 88 | @return $args + $global-unit; 89 | } @else { 90 | @return $args; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_group.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 混合缩写的混合宏(mixin); 3 | 4 | // 适配pc端 5 | @mixin pc($width) { 6 | $xs: 100px/ (750px/640px); 7 | @media (min-width: ($width)) { 8 | html { 9 | width: 640px !important; 10 | font-size: $xs !important; 11 | background-color: $backcolor !important; 12 | margin: auto !important; 13 | } 14 | } 15 | } 16 | 17 | // 设置width & height & line-height; 18 | @mixin whl($width, $height, $line-height: $height) { 19 | @include w($width); 20 | @include h($height); 21 | @include lh($line-height); 22 | } 23 | 24 | // 设置width & height & font-size & line-height; 25 | @mixin whfl($width, $height, $line-height, $font) { 26 | @include w($width); 27 | @include h($height); 28 | @include lh($line-height); 29 | @include fz($font); 30 | } 31 | 32 | // 设置width & height & font-size & line-height & color; 33 | @mixin whflc($width, $height, $font, $line-height, $color) { 34 | @include w($width); 35 | @include h($height); 36 | @include fz($font); 37 | @include lh($line-height); 38 | @include c($color); 39 | } 40 | 41 | // 设置width & height & font-size & line-height & color & font-weight; 42 | @mixin whflcb($width, $height, $font, $line-height, $color, $bold: bold) { 43 | @include w($width); 44 | @include h($height); 45 | @include fz($font); 46 | @include lh($line-height); 47 | @include c($color); 48 | @include fb($bold); 49 | } 50 | 51 | // 设置height & line-height; 52 | @mixin hl($height, $line-height: $height) { 53 | @include h($height); 54 | @include lh($line-height); 55 | } 56 | 57 | // 设置height & font-size & line-height & color; 58 | @mixin hflc($height, $font, $line-height, $color) { 59 | @include h($height); 60 | @include fz($font); 61 | @include lh($line-height); 62 | @include c($color); 63 | } 64 | 65 | // 设置height & font-size & line-height & color & font-weight; 66 | @mixin hflcb($height, $font, $line-height, $color, $bold: bold) { 67 | @include h($height); 68 | @include fz($font); 69 | @include lh($line-height); 70 | @include c($color); 71 | @include fb($bold); 72 | } 73 | 74 | // 设置width & line-height; 75 | @mixin wl($width, $line-height: $height) { 76 | @include w($width); 77 | @include lh($line-height); 78 | } 79 | 80 | //设置font-szie & line-height; 81 | @mixin flh($font, $line) { 82 | @include fz($font); 83 | @include lh($line); 84 | } 85 | 86 | //设置font-size & color; 87 | @mixin fc($font, $color) { 88 | @include fz($font); 89 | @include c($color); 90 | } 91 | 92 | //设置font-size & line-height & color; 93 | @mixin flc($font, $line, $color) { 94 | @include fz($font); 95 | @include lh($line); 96 | @include c($color); 97 | } 98 | 99 | //设置font-size & color & font-weight; 100 | @mixin fcb($font, $color, $bold: bold) { 101 | @include fz($font); 102 | @include c($color); 103 | @include fb($bold); 104 | } 105 | 106 | //设置font-size & line-height & color & font-weight; 107 | @mixin flcb($font, $line, $color, $bold: bold) { 108 | @include fz($font); 109 | @include lh($line); 110 | @include c($color); 111 | @include fb($bold); 112 | } 113 | 114 | //设置family & font-size & line-height & color; 115 | @mixin faflc($family, $font, $line, $color) { 116 | @include fa($family); 117 | @include fz($font); 118 | @include lh($line); 119 | @include c($color); 120 | } 121 | 122 | //设置width & height; 123 | @mixin wh($width, $height: $width, $attr: true) { 124 | @if ($attr== 'w') { 125 | @include w($width); 126 | } @else if($attr== 'h') { 127 | @include h($height); 128 | } @else { 129 | @include w($width); 130 | @include h($height); 131 | } 132 | } 133 | 134 | //设置max-width & max-height; 135 | @mixin mawh($width: 100%, $height: $width) { 136 | @include maw($width); 137 | @include mah($height); 138 | } 139 | 140 | //设置min-width & min-height; 141 | @mixin miwh($width: 100%, $height: $width) { 142 | @include miw($width); 143 | @include mih($height); 144 | } 145 | 146 | //设置min-width & max-width; 147 | @mixin mmw($minWidth, $maxWidth) { 148 | @include miw($minWidth); 149 | @include maw($maxWidth); 150 | } 151 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_mixin.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | /* 单行省略 */ 4 | @mixin tofl($width) { 5 | max-width: $width; 6 | white-space: nowrap; 7 | overflow: hidden; 8 | text-overflow: ellipsis; 9 | -ms-text-overflow: ellipsis; 10 | } 11 | 12 | /* 多行省略 */ 13 | @mixin erow($row: 2) { 14 | display: -webkit-box; 15 | -webkit-box-orient: vertical; 16 | -webkit-line-clamp: $row; 17 | overflow: hidden; 18 | } 19 | 20 | /* 文字断行 */ 21 | @mixin breakword { 22 | word-wrap: break-word; 23 | word-break: break-all; 24 | } 25 | 26 | /* 禁用样式disabled */ 27 | @mixin disabled($bgColor: #e6e6e6, $textColor: #ababab) { 28 | background-color: $bgColor; 29 | color: $textColor; 30 | cursor: not-allowed; 31 | } 32 | 33 | /* 细边框 */ 34 | @mixin thin-line($bordercolor, $borderRadius) { 35 | position: relative; 36 | &:after { 37 | content: ''; 38 | position: absolute; 39 | top: 0; 40 | left: 0; 41 | box-sizing: border-box; 42 | width: 200%; 43 | height: 200%; 44 | transform: scale(0.5); 45 | transform-origin: left top; 46 | border: 1px solid $bordercolor; 47 | border-radius: $borderRadius; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_position.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 定位的混合宏(mixin); 3 | 4 | // @include position(absolute,(t:10px,l:0px,z:5)); 5 | @mixin position($position, $args: false) { 6 | position: $position; 7 | @include position_each($args); 8 | } 9 | 10 | // @include abs((t:10px,l:0px,z:5)); 11 | @mixin abs($args: false) { 12 | position: absolute; 13 | @include position_each($args); 14 | } 15 | 16 | // @include rel((t:10px,l:0px,z:5)); 17 | @mixin rel($args: false) { 18 | position: relative; 19 | @include position_each($args); 20 | } 21 | 22 | // @include fixed((t:10px,l:0px,z:5)); 23 | @mixin fixed($args: false) { 24 | position: fixed; 25 | @include position_each($args); 26 | } 27 | 28 | //遍历定位属性:top\right\bottom\left\z-index\width\height; 29 | @mixin position_each($args) { 30 | $parameter: t, r, b, l, z, w, h; 31 | $parameter2: ( 32 | t: top, 33 | r: right, 34 | b: bottom, 35 | l: left, 36 | z: z-index, 37 | w: width, 38 | h: height, 39 | ); 40 | @each $o, $s in $args { 41 | $i: index($parameter, $o); 42 | @if ($i and type-of($i) == number) { 43 | @if ($i == 5) { 44 | #{map-get($parameter2, $o)}: $s; 45 | } @else { 46 | #{map-get($parameter2, $o)}: addUnits($s); 47 | } 48 | } 49 | } 50 | } 51 | 52 | // 垂直,水平居中 53 | @mixin positionAb($x, $y) { 54 | position: absolute; 55 | left: 50%; 56 | top: 50%; 57 | width: $x; 58 | height: $y; 59 | margin-left: -$x/2; 60 | margin-top: -$y/2; 61 | z-index: 99; 62 | } 63 | 64 | //定位top值; 65 | @mixin t($top: 0) { 66 | top: addUnits($top); 67 | } 68 | 69 | //定位left值; 70 | @mixin l($left: 0) { 71 | left: addUnits($left); 72 | } 73 | 74 | //定位bottom值; 75 | @mixin b($bottom: 0) { 76 | bottom: addUnits($bottom); 77 | } 78 | 79 | //定位right值; 80 | @mixin r($right: 0) { 81 | right: addUnits($right); 82 | } 83 | 84 | // 定位top/left值; 85 | @mixin tl($top: 0, $left: 0, $zindex: false) { 86 | @include t($top); 87 | @include l($top); 88 | @if ($zindex) { 89 | @include z($zindex); 90 | } 91 | } 92 | 93 | // 定位right/left值; 94 | @mixin rl($right: 0, $left: 0, $zindex: false) { 95 | @include r($right); 96 | @include l($left); 97 | @if ($zindex) { 98 | @include z($zindex); 99 | } 100 | } 101 | 102 | // 定位bottom/left值; 103 | @mixin bl($bottom: 0, $left: 0, $zindex: false) { 104 | @include b($bottom); 105 | @include l($left); 106 | @if ($zindex) { 107 | @include z($zindex); 108 | } 109 | } 110 | 111 | // 定位bottom/right值; 112 | @mixin br($bottom: 0, $right: 0, $zindex: false) { 113 | @include b($bottom); 114 | @include r($right); 115 | @if ($zindex) { 116 | @include z($zindex); 117 | } 118 | } 119 | 120 | // 定位top/left/bottom/right值; 121 | @mixin trbl($top: 0, $left: 0, $bottom: 0, $right: 0, $zindex: false) { 122 | @include t($top); 123 | @include l($left); 124 | @include b($bottom); 125 | @include r($right); 126 | @if ($zindex) { 127 | @include z($zindex); 128 | } 129 | } 130 | 131 | // 设置层级数; 132 | @mixin z($num) { 133 | z-index: $num; 134 | } 135 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_size.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 宽高的混合宏(mixin); 3 | 4 | // 设置width; 5 | @mixin w($width) { 6 | width: addUnits($width); 7 | } 8 | 9 | // 设置height; 10 | @mixin h($height) { 11 | height: addUnits($height); 12 | } 13 | 14 | //设置max-height; 15 | @mixin mah($height) { 16 | max-height: addUnits($height); 17 | } 18 | 19 | //设置max-width; 20 | @mixin maw($width) { 21 | max-width: addUnits($width); 22 | } 23 | 24 | //设置min-height; 25 | @mixin mih($height) { 26 | min-height: addUnits($height); 27 | } 28 | 29 | //设置min-width; 30 | @mixin miw($width) { 31 | min-width: addUnits($width); 32 | } 33 | 34 | // 设置width/height; 35 | @mixin size($width, $height: $width, $attr: true) { 36 | @if ($attr== 'w') { 37 | @include w($width); 38 | } @else if($attr== 'h') { 39 | @include h($height); 40 | } @else { 41 | @include w($width); 42 | @include h($height); 43 | } 44 | } 45 | 46 | @mixin boxSize($width: auto, $height: auto, $color: transparent) { 47 | @include size($width, $height); 48 | @include bgc($color); 49 | } 50 | 51 | // 弹性盒 52 | @mixin flex($direction: row, $justify: center, $align: center) { 53 | @include css3(display, flex); 54 | @include css3(flex-direction, $direction); 55 | @include css3(justify-content, $justify); 56 | @include css3(align-items, $align); 57 | } 58 | -------------------------------------------------------------------------------- /src/assets/scss/utils/_text.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | // 文本的混合宏(mixin); 3 | 4 | // 单行省略 5 | @mixin tofl($width) { 6 | max-width: $width; 7 | white-space: nowrap; 8 | overflow: hidden; 9 | text-overflow: ellipsis; 10 | -ms-text-overflow: ellipsis; 11 | } 12 | 13 | // 多行省略 14 | @mixin erow($row: 2) { 15 | @include css3(display, box, $prefix: false, $suffix: true); 16 | @include css3(box-orient, vertical); 17 | @include css3(line-clamp, $row); 18 | overflow: hidden; 19 | } 20 | 21 | // 隐藏文字 22 | @mixin hide-text { 23 | display: block; 24 | overflow: hidden; 25 | white-space: nowrap; 26 | text-indent: -9000px; 27 | } 28 | 29 | // 显示图标; 30 | @mixin replace-text($image, $x: 50%, $y: 50%) { 31 | @include hide-text(); 32 | background-image: $image; 33 | background-repeat: no-repeat; 34 | background-position: addUnits($x) addUnits($y); 35 | } 36 | 37 | // 设置文字对齐方式; 38 | @mixin ta($direction: left, $arg: false) { 39 | text-align: $direction; 40 | @if ($arg) { 41 | text-justify: $arg; 42 | } 43 | } 44 | 45 | // 文字排列方式; 46 | @mixin wm($arg: normal) { 47 | writing-mode: $arg; 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/theme/_element-iconfont.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | @font-face { 4 | font-family: 'iconfont'; 5 | 6 | /* project id 1830148 */ 7 | src: url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.eot'); 8 | src: url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.eot?#iefix') 9 | format('embedded-opentype'), 10 | url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.woff2') format('woff2'), 11 | url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.woff') format('woff'), 12 | url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.ttf') format('truetype'), 13 | url('//at.alicdn.com/t/font_1830148_4m0w60x7asx.svg#iconfont') format('svg'); 14 | } 15 | 16 | // select下拉按钮 17 | .el-icon-close { 18 | color: #c8c8cb !important; 19 | font-family: 'iconfont' !important; 20 | font-size: 16px !important; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale; 23 | font-style: normal; 24 | 25 | &::before { 26 | // content: "\e6d9"; 27 | content: '\e71a'; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/theme/_element-theme.scss: -------------------------------------------------------------------------------- 1 | @import './element-iconfont'; 2 | @import './element-variables'; 3 | 4 | // 改变 icon 字体路径变量,必需 5 | $--font-path: 'element-plus/lib/theme-chalk/fonts'; 6 | 7 | // 引入element默认样式 8 | @import 'element-plus/packages/theme-chalk/src/index.scss'; 9 | -------------------------------------------------------------------------------- /src/assets/theme/_element-variables.scss: -------------------------------------------------------------------------------- 1 | /* 自定义主题 2 | * https://element.eleme.cn/#/zh-CN/theme/preview 3 | */ 4 | 5 | /*********************************** Color ***********************************/ 6 | // Brand Color 7 | $--color-primary: #f1b7d0; 8 | // Functional Color 9 | $--color-success: #67c23a; 10 | $--color-warning: #e6a23c; 11 | $--color-danger: #b95881; 12 | $--color-info: #909399; 13 | $--color-error: #b95881; 14 | 15 | /*********************************** Color ***********************************/ 16 | $--table-row-hover-background-color: #eee; 17 | 18 | /*********************************** Button ***********************************/ 19 | $--button-font-weight: 400; 20 | 21 | /*********************************** Checkbox ***********************************/ 22 | $--checkbox-font-color: #909297; 23 | 24 | /*********************************** Pagination ***********************************/ 25 | $--pagination-hover-color: #5755b3; 26 | 27 | /*********************************** Tree ***********************************/ 28 | $--tree-node-hover-background-color: #f5f7fa; 29 | $--tree-expand-icon-color: #c8c8c8; 30 | 31 | /*********************************** Table ***********************************/ 32 | $--table-row-hover-background-color: #f6f7fb; 33 | 34 | /*********************************** Radio ***********************************/ 35 | $--radio-checked-font-color: #5755b3; 36 | $--radio-checked-icon-color: #5755b3; 37 | $--radio-checked-input-border-color: #5755b3; 38 | -------------------------------------------------------------------------------- /src/components/basic/index.ts: -------------------------------------------------------------------------------- 1 | import ModifyPass from './modify-pass.vue'; 2 | import ServeError from './serve-error.vue'; 3 | 4 | export { ModifyPass, ServeError }; 5 | -------------------------------------------------------------------------------- /src/components/basic/modify-pass.vue: -------------------------------------------------------------------------------- 1 | 47 | 175 | 298 | -------------------------------------------------------------------------------- /src/components/basic/serve-error.vue: -------------------------------------------------------------------------------- 1 | 30 | 63 | 80 | -------------------------------------------------------------------------------- /src/components/custom/custom-chart.vue: -------------------------------------------------------------------------------- 1 | 4 | 53 | 59 | -------------------------------------------------------------------------------- /src/components/custom/custom-dialog.vue: -------------------------------------------------------------------------------- 1 | 43 | 115 | 178 | -------------------------------------------------------------------------------- /src/components/custom/custom-drawer.vue: -------------------------------------------------------------------------------- 1 | 27 | 100 | 141 | -------------------------------------------------------------------------------- /src/components/custom/custom-image.vue: -------------------------------------------------------------------------------- 1 | 39 | 121 | 134 | -------------------------------------------------------------------------------- /src/components/custom/custom-message.ts: -------------------------------------------------------------------------------- 1 | /** 重置message,防止重复点击重复弹出message弹框 */ 2 | import { ElMessage } from 'element-plus'; 3 | import { 4 | MessageHandle, 5 | MessageParams, 6 | } from 'element-plus/lib/components/message/src/message'; 7 | 8 | type MessageType = 'success' | 'warning' | 'info' | 'error'; 9 | 10 | let messageInstance: MessageHandle; 11 | const customMessage: any = (options: MessageParams) => { 12 | if (messageInstance) { 13 | messageInstance.close(); 14 | } 15 | messageInstance = ElMessage(options); 16 | }; 17 | const messageType: MessageType[] = ['error', 'success', 'info', 'warning']; 18 | messageType.forEach((item) => { 19 | customMessage[item] = (options: MessageParams) => { 20 | if (typeof options === 'string') { 21 | options = { 22 | message: options, 23 | }; 24 | } 25 | options.type = item; 26 | return customMessage(options); 27 | }; 28 | }); 29 | 30 | export default customMessage; 31 | -------------------------------------------------------------------------------- /src/components/custom/custom-pagination.vue: -------------------------------------------------------------------------------- 1 | 25 | 121 | 162 | -------------------------------------------------------------------------------- /src/components/custom/custom-query.vue: -------------------------------------------------------------------------------- 1 | 34 | 117 | 248 | -------------------------------------------------------------------------------- /src/components/custom/custom-scroll.vue: -------------------------------------------------------------------------------- 1 | 6 | 72 | -------------------------------------------------------------------------------- /src/components/custom/custom-toast/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 145 | 194 | -------------------------------------------------------------------------------- /src/components/custom/custom-toast/toast.ts: -------------------------------------------------------------------------------- 1 | import { createVNode, isVNode, render } from 'vue'; 2 | import ToastConstructor from './index.vue'; 3 | import type { 4 | ToastVM, 5 | ToastVMQueue, 6 | ToastOptions, 7 | ToastImplements, 8 | } from './types'; 9 | 10 | let instance: ToastVM; 11 | let instanceQueue: ToastVMQueue = []; 12 | let isMultiple = false; // 是否同时存在多个 13 | const zIndex = 2001; 14 | 15 | const mergeOptions = (options: ToastOptions) => ({ 16 | ...options, 17 | zIndex, 18 | }); 19 | 20 | const Toast: ToastImplements = (options: ToastOptions) => { 21 | if (window === undefined) return null; 22 | 23 | if (typeof options === 'string') { 24 | options = { 25 | message: options, 26 | }; 27 | } 28 | mergeOptions(options); 29 | 30 | // 筛选出未关闭的toast, 已关闭的从dom移除 31 | instanceQueue = instanceQueue.filter((item) => { 32 | const { show } = item.component?.ctx; 33 | return show; 34 | }); 35 | 36 | // 清空显示 37 | if (instanceQueue.length && !isMultiple) { 38 | instanceQueue.forEach((item) => { 39 | item.component?.ctx.close(); 40 | }); 41 | } 42 | 43 | // 创建新的toast 44 | instance = createVNode( 45 | ToastConstructor, 46 | options, 47 | isVNode(options.message) ? { default: () => options.message } : null 48 | ); 49 | 50 | const container = document.createElement('div'); 51 | render(instance, container); 52 | instance.close = () => { 53 | instance.component?.ctx.close(); 54 | }; 55 | 56 | instanceQueue.push(instance); 57 | return instance; 58 | }; 59 | 60 | Toast.multiple = (value: boolean) => { 61 | isMultiple = value; 62 | }; 63 | 64 | const defineMethod = (type: string) => (options: ToastOptions) => { 65 | if (typeof options === 'string' || isVNode(options)) { 66 | options = { 67 | message: options, 68 | }; 69 | } 70 | return Toast({ 71 | type, 72 | ...options, 73 | }); 74 | }; 75 | 76 | ['loading', 'success', 'fail'].forEach((type) => { 77 | Toast[type] = defineMethod(type); 78 | }); 79 | 80 | export default Toast; 81 | -------------------------------------------------------------------------------- /src/components/custom/custom-toast/types.ts: -------------------------------------------------------------------------------- 1 | import type { VNode } from 'vue'; 2 | 3 | export type ToastType = 'success' | 'fail' | 'loading'; 4 | 5 | export type ToastVM = any; 6 | 7 | export type ToastVMQueue = Array; 8 | 9 | export type ToastImplements = { 10 | multiple(value: boolean): void; 11 | [propName: string]: any; 12 | (options: any): any; 13 | }; 14 | 15 | export type ToastOptions = { 16 | message?: string | VNode; 17 | type?: 'loading' | 'success' | 'fail' | ''; 18 | position?: 'middle' | 'top' | 'bottom' | ''; 19 | icon?: string; 20 | zIndex?: number; 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/custom/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'vue'; 2 | import CustomImage from './custom-image.vue'; 3 | import CustomChart from './custom-chart.vue'; 4 | import CustomDialog from './custom-dialog.vue'; 5 | import CustomQuery from './custom-query.vue'; 6 | import CustomPagination from './custom-pagination.vue'; 7 | import CustomScroll from './custom-scroll.vue'; 8 | import CustomToast from './custom-toast/toast'; 9 | import CustomDrawer from './custom-drawer.vue'; 10 | 11 | const install = (app: App): void => { 12 | app.component('CustomImage', CustomImage); 13 | app.component('CustomChart', CustomChart); 14 | app.component('CustomDialog', CustomDialog); 15 | app.component('CustomQuery', CustomQuery); 16 | app.component('CustomPagination', CustomPagination); 17 | app.component('CustomScroll', CustomScroll); 18 | app.component('CustomDrawer', CustomDrawer); 19 | 20 | // CustomToast 21 | app.provide('$toast', CustomToast); 22 | }; 23 | 24 | export { 25 | install, 26 | CustomImage, 27 | CustomChart, 28 | CustomDialog, 29 | CustomQuery, 30 | CustomPagination, 31 | CustomScroll, 32 | CustomToast, 33 | }; 34 | -------------------------------------------------------------------------------- /src/config/global.ts: -------------------------------------------------------------------------------- 1 | import { i18n } from '@/plugin'; 2 | 3 | class FunctionType extends Function {} 4 | 5 | const global = { 6 | form: { 7 | // 正则表达式 8 | rule: { 9 | requied: /\S/, 10 | illegal: 11 | /[`~!@#$%^&*()+<>?:"{},/;'[\]]|[·!#¥(——):;“”‘、,|《。》?、【】[\]]/im, 12 | email: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]+$/i, 13 | password: /^[a-zA-Z0-9]{6,8}$/i, 14 | tel: /^1\d{10}$/, 15 | fax: /^(?:\d{3,4}-)?\d{7,8}(?:-\d{1,6})?$/, 16 | num: /^\d+$/i, 17 | code: /^[0-9a-zA-Z_]+$/, 18 | name: /[`~!@#$%^&*()+<>?:"{},/;'[\]]|[·!#¥(——):;“”‘、,|《。》?、【】[\]]/im, 19 | onlyspace: /^\s+$/, 20 | filter: /^[\w\u4e00-\u9fa5]+$/i, 21 | phone: /^\d{3}-\d{7,8}|\d{4}-\d{7,8}$/, 22 | zone: /^0[0-9]{2,3}$/i, 23 | picCode: /^[a-zA-Z0-9]{6,6}$/i, 24 | smsCode: /^[0-9]{6,6}$/i, 25 | zipCode: /^[0-9]{6}$/, 26 | areaTel: /^((\+?86)|(\+?96)|(\+?06))?1\d{10}$/, 27 | passLength: /^[a-zA-Z0-9]{6,15}$/, 28 | cnen: /^[\u4e00-\u9fa5a-zA-Z]+$/i, 29 | }, 30 | validateCode: ( 31 | rule: unknown, 32 | value: string, 33 | callback: FunctionType 34 | ): void => { 35 | const { code } = global.form.rule; 36 | if (value && !code.test(value)) { 37 | callback(new Error(`${i18n.global.t('baseGlobal.t4')}`)); 38 | } else { 39 | callback(); 40 | } 41 | }, 42 | validateRequiredCode: ( 43 | rule: unknown, 44 | value: string, 45 | callback: FunctionType 46 | ): void => { 47 | const { code } = global.form.rule; 48 | if (!value) { 49 | callback(new Error(`${i18n.global.t('baseGlobal.t5')}`)); 50 | } else if (!code.test(value)) { 51 | callback(new Error(`${i18n.global.t('baseGlobal.t4')}`)); 52 | } else { 53 | callback(); 54 | } 55 | }, 56 | validateRequiredName: ( 57 | rule: unknown, 58 | value: string, 59 | callback: FunctionType 60 | ): void => { 61 | const { name } = global.form.rule; 62 | const { onlyspace } = global.form.rule; 63 | if (!value) { 64 | callback(new Error(`${i18n.global.t('baseGlobal.t6')}`)); 65 | } else if (onlyspace.test(value)) { 66 | callback(new Error(`${i18n.global.t('baseGlobal.t7')}`)); 67 | } else if (name.test(value)) { 68 | callback(new Error(`${i18n.global.t('baseGlobal.t8')}`)); 69 | } else { 70 | callback(); 71 | } 72 | }, 73 | validateName: ( 74 | rule: unknown, 75 | value: string, 76 | callback: FunctionType 77 | ): void => { 78 | const { illegal } = global.form.rule; 79 | if (illegal.test(value)) { 80 | callback(new Error(`${i18n.global.t('baseGlobal.t8')}`)); 81 | } else { 82 | callback(); 83 | } 84 | }, 85 | validateFax: ( 86 | rule: unknown, 87 | value: string, 88 | callback: FunctionType 89 | ): void => { 90 | const { fax } = global.form.rule; 91 | if (value && !fax.test(value)) { 92 | callback(new Error(`${i18n.global.t('baseGlobal.t9')}`)); 93 | } else { 94 | callback(); 95 | } 96 | }, 97 | validateTel: ( 98 | rule: unknown, 99 | value: string, 100 | callback: FunctionType 101 | ): void => { 102 | const { tel } = global.form.rule; 103 | if (value && !tel.test(value)) { 104 | callback(new Error(`${i18n.global.t('baseGlobal.t10')}`)); 105 | } else { 106 | callback(); 107 | } 108 | }, 109 | validatePhone: ( 110 | rule: unknown, 111 | value: string, 112 | callback: FunctionType 113 | ): void => { 114 | const { phone } = global.form.rule; 115 | if (value && !phone.test(value)) { 116 | callback(new Error(`${i18n.global.t('baseGlobal.t11')}`)); 117 | } else { 118 | callback(); 119 | } 120 | }, 121 | validateEmail: ( 122 | rule: unknown, 123 | value: string, 124 | callback: FunctionType 125 | ): void => { 126 | const { email } = global.form.rule; 127 | if (value && !email.test(value)) { 128 | callback(new Error(`${i18n.global.t('baseGlobal.t12')}`)); 129 | } else { 130 | callback(); 131 | } 132 | }, 133 | validateZipCode: ( 134 | rule: unknown, 135 | value: string, 136 | callback: FunctionType 137 | ): void => { 138 | const { zipCode } = global.form.rule; 139 | if (value && !zipCode.test(value)) { 140 | callback(new Error(`${i18n.global.t('baseGlobal.t13')}`)); 141 | } else { 142 | callback(); 143 | } 144 | }, 145 | }, 146 | // 防抖白名单 147 | whitelist: [], 148 | }; 149 | 150 | export default global; 151 | -------------------------------------------------------------------------------- /src/config/microApps.ts: -------------------------------------------------------------------------------- 1 | const microApps: Array<{ 2 | name: 'common' | 'rights' | 'pim' | 'oms' | 'crm' | 'prom'; 3 | activeRule: string | Array; 4 | container: string; 5 | entry: string; 6 | [key: string]: unknown; 7 | }> = [ 8 | { 9 | name: 'common', 10 | activeRule: '/common', 11 | container: '#micro-app', 12 | entry: '/micro-app/web-micro-masterdata/', 13 | }, 14 | { 15 | name: 'rights', 16 | activeRule: '/rights', 17 | container: '#micro-app', 18 | entry: '/micro-app/web-micro-rights/', 19 | }, 20 | { 21 | name: 'pim', 22 | activeRule: '/pim', 23 | container: '#micro-app', 24 | entry: '/micro-app/web-micro-pim/', 25 | }, 26 | { 27 | name: 'oms', 28 | activeRule: ['/orderhub', '/order', '/inventory', '/report'], 29 | container: '#micro-app', 30 | entry: '/micro-app/web-micro-oms/', 31 | }, 32 | { 33 | name: 'crm', 34 | activeRule: '/crm', 35 | container: '#micro-app', 36 | entry: '/micro-app/web-micro-crm/', 37 | }, 38 | { 39 | name: 'prom', 40 | activeRule: '/promotion', 41 | container: '#micro-app', 42 | entry: '/micro-app/web-micro-prom/', 43 | }, 44 | ]; 45 | 46 | export default microApps; 47 | -------------------------------------------------------------------------------- /src/layout/Demo.vue: -------------------------------------------------------------------------------- 1 | 18 | 87 | -------------------------------------------------------------------------------- /src/layout/components/LayoutAside.vue: -------------------------------------------------------------------------------- 1 | 49 | 159 | 240 | -------------------------------------------------------------------------------- /src/layout/components/LayoutNav.vue: -------------------------------------------------------------------------------- 1 | 26 | 61 | 98 | -------------------------------------------------------------------------------- /src/layout/components/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 71 | 131 | 189 | -------------------------------------------------------------------------------- /src/layout/components/SidebarLink.vue: -------------------------------------------------------------------------------- 1 | 15 | 35 | -------------------------------------------------------------------------------- /src/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | import LayoutAside from './LayoutAside.vue'; 2 | import LayoutHeader from './LayoutHeader.vue'; 3 | import LayoutNav from './LayoutNav.vue'; 4 | import SidebarItem from './SidebarItem.vue'; 5 | import SidebarLink from './SidebarLink.vue'; 6 | 7 | export { LayoutAside, LayoutHeader, LayoutNav, SidebarItem, SidebarLink }; 8 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 195 | 227 | -------------------------------------------------------------------------------- /src/locale/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "base403": { 3 | "t107": "Back to homepage", 4 | "t108": "No permission, please contact the administrator" 5 | }, 6 | "base404": { 7 | "t107": "Back to homepage" 8 | }, 9 | "base503": { 10 | "t107": "Back to homepage", 11 | "t109": "Try refresh" 12 | }, 13 | "baseLogin": { 14 | "t4": "\\ Security Code", 15 | "t5": "Next automatic login", 16 | "t1": "Sign In", 17 | "t110": "Password at least 6 digits", 18 | "t111": "login successful", 19 | "t2": "\\ User Name", 20 | "t3": "\\ Password" 21 | }, 22 | "baseTaskList": { 23 | "t121": "shut down", 24 | "t146": "Task center", 25 | "t147": "mission name", 26 | "t148": "Task status", 27 | "t149": "Creation time", 28 | "t150": "End of creation", 29 | "t152": "View failed results", 30 | "t153": "download", 31 | "t154": "Subline number of the task", 32 | "t155": "Task unique business code", 33 | "t156": "Success or error message", 34 | "t157": "Remarks", 35 | "t158": "Execution report time", 36 | "t159": "Result enumeration", 37 | "t160": "Current progress", 38 | "t161": "Task abnormal data", 39 | "t162": "No abnormal data", 40 | "t151": "operating" 41 | }, 42 | "baseAbstract": { 43 | "t0": "Request failed", 44 | "t1": "Login timed out, please log in again" 45 | }, 46 | "baseCommonPagination": { 47 | "t122": "Total", 48 | "t123": "Records", 49 | "t124": "First", 50 | "t125": "page" 51 | }, 52 | "baseCustomDialog": { 53 | "t102": "determine", 54 | "t126": "cancel" 55 | }, 56 | "baseLayoutHeader": { 57 | "t101": "prompt", 58 | "t102": "determine", 59 | "t126": "cancel", 60 | "t138": "Please select membership system", 61 | "t139": "Please enter the keywords you want to search", 62 | "t140": "change Password", 63 | "t141": "sign out", 64 | "t142": "Chinese", 65 | "t143": "Are you sure to log out of the current account?", 66 | "t144": "Logout failed" 67 | }, 68 | "baseCustomModalbox": { 69 | "t126": "cancel", 70 | "t135": "title", 71 | "t136": "content", 72 | "t137": "confirm" 73 | }, 74 | "baseCustomPagination": { 75 | "t1": "Total", 76 | "t2": "Record", 77 | "t3": "page" 78 | }, 79 | "baseCustomQuery": { 80 | "t1": "Reset", 81 | "t2": "Inquire", 82 | "t3": "Unfold", 83 | "t4": "Put away" 84 | }, 85 | "baseCustomTagcache": { 86 | "t121": "shut down", 87 | "t127": "Refresh", 88 | "t128": "Close other" 89 | }, 90 | "baseCustomXlsx": { 91 | "t130": "Parsing", 92 | "t131": "Please upload a file in the correct format", 93 | "t132": "Please import the correct information", 94 | "t133": "Importing files", 95 | "t134": "Export file", 96 | "t135": "Please set getImportData first" 97 | }, 98 | "baseGlobal": { 99 | "t10": "please enter a valid phone number", 100 | "t11": "Please enter the correct phone", 101 | "t12": "please enter your vaild email", 102 | "t13": "Please enter the correct zip code", 103 | "t4": "Can only be a combination of numbers, letters or underscores", 104 | "t5": "Please enter the code", 105 | "t6": "Please enter a name", 106 | "t7": "Can't just enter a space", 107 | "t8": "Cannot contain special characters!", 108 | "t9": "Please enter the correct fax" 109 | }, 110 | "routerIndex": { 111 | "t100": "You do not have permission, please contact the administrator to assign permission before using", 112 | "t101": "prompt", 113 | "t102": "determine", 114 | "t103": "log in", 115 | "t104": "No permission", 116 | "t105": "service is not available", 117 | "t106": "Sub application" 118 | }, 119 | "baseIntercept": { 120 | "t2": "The operation is too frequent, please try again later", 121 | "t3": "Loading" 122 | }, 123 | "baseLayoutIndex": { 124 | "t100": "You do not have permission, please contact the administrator to assign permission before using", 125 | "t101": "prompt", 126 | "t102": "determine" 127 | }, 128 | "configMenu": { 129 | "t14": "Basic management", 130 | "t15": "Basic data", 131 | "t16": "Channel management", 132 | "t17": "Store management", 133 | "t18": "Warehouse management", 134 | "t19": "Express management", 135 | "t20": "Supplier type", 136 | "t21": "Supplier Management", 137 | "t22": "Purchase type", 138 | "t23": "Purchase return type", 139 | "t24": "authority management", 140 | "t25": "User Management", 141 | "t26": "user list", 142 | "t27": "Role list", 143 | "t28": "Data group list", 144 | "t29": "Organization list", 145 | "t30": "Menu list", 146 | "t31": "Log management", 147 | "t32": "Operation log", 148 | "t34": "Merchandise management", 149 | "t35": "Commodity release management", 150 | "t36": "Commodity off-shelf management", 151 | "t37": "Publish new products", 152 | "t38": "Commodity portfolio management", 153 | "t39": "Commodity recycling bin", 154 | "t40": "Commodity library", 155 | "t41": "Brand management", 156 | "t42": "Brand Management List", 157 | "t43": "Category management", 158 | "t44": "Virtual category management", 159 | "t45": "Sales category management", 160 | "t47": "Commodity label management", 161 | "t48": "Commodity marking", 162 | "t49": "Data Bus", 163 | "t50": "Store authorization management", 164 | "t51": "Authorized shop", 165 | "t52": "File management", 166 | "t53": "Address mapping management", 167 | "t54": "Order center", 168 | "t55": "Order file", 169 | "t56": "Store configuration", 170 | "t57": "Commodity mapping", 171 | "t58": "Order label", 172 | "t59": "Material file", 173 | "t60": "Promotional gifts", 174 | "t61": "business", 175 | "t62": "Order management", 176 | "t63": "Abnormal order", 177 | "t64": "Order Tracking", 178 | "t65": "Invoice inquiry", 179 | "t66": "After-sales management", 180 | "t68": "Refunds", 181 | "t69": "Stock Center", 182 | "t70": "Warehouse file", 183 | "t33": "Commodity Center", 184 | "t46": "Label management", 185 | "t67": "Refund order management", 186 | "t71": "Warehouse comparison table", 187 | "t72": "Upload inventory settings", 188 | "t73": "Procurement management", 189 | "t74": "purchase order", 190 | "t75": "Purchase receipt", 191 | "t76": "Purchase return outbound order", 192 | "t77": "Material inventory statistics", 193 | "t78": "Inventory management", 194 | "t79": "Inventory adjustment", 195 | "t80": "Warehouse processing order", 196 | "t81": "External warehouse docking", 197 | "t82": "Odd door", 198 | "t83": "Configuration", 199 | "t84": "Commodity synchronization", 200 | "t85": "Interface log query", 201 | "t86": "Member Centre", 202 | "t87": "Member 360", 203 | "t88": "Member loyalty", 204 | "t89": "Membership level rules", 205 | "t90": "Level setting", 206 | "t91": "Level up and down rules", 207 | "t92": "Member Points Rules", 208 | "t93": "Points basic settings", 209 | "t94": "Transaction reward points", 210 | "t95": "Interactive points task", 211 | "t96": "More", 212 | "t97": "Data management", 213 | "t98": "Metadata management", 214 | "t99": "Membership system management" 215 | }, 216 | "baseModifyPass": { 217 | "t112": "For account security, please change the initial password immediately", 218 | "t113": "Please enter the old password", 219 | "t114": "Please enter a new password", 220 | "t115": "Confirm the new password", 221 | "t116": "Characters, including numbers and letters", 222 | "t117": "Password does not conform to the format", 223 | "t118": "The password input is different, please re-enter", 224 | "t119": "Please modify the initial password", 225 | "t120": "Successful operation" 226 | }, 227 | "baseServeError": { 228 | "t121": "shut down" 229 | }, 230 | "baseTableDraggable": { 231 | "t145": "Defaults" 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/locale/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseAbstract": { 3 | "t0": "请求失败", 4 | "t1": "登录超时,请重新登录" 5 | }, 6 | "baseIntercept": { 7 | "t2": "操作太频繁,请稍后再试", 8 | "t3": "加载中" 9 | }, 10 | "baseGlobal": { 11 | "t4": "只能是数字、字母或下划线的组合", 12 | "t5": "请输入编码", 13 | "t6": "请输入名称", 14 | "t7": "不能只输入空格", 15 | "t8": "不能含有特殊字符!", 16 | "t9": "请输入正确的传真", 17 | "t10": "请输入正确的手机号", 18 | "t11": "请输入正确的电话", 19 | "t12": "请输入正确的邮箱", 20 | "t13": "请输入正确的邮编" 21 | }, 22 | "configMenu": { 23 | "t14": "基础管理", 24 | "t15": "基础数据", 25 | "t16": "渠道管理", 26 | "t17": "店铺管理", 27 | "t18": "仓库管理", 28 | "t19": "快递管理", 29 | "t20": "供应商类型", 30 | "t21": "供应商管理", 31 | "t22": "采购类型", 32 | "t23": "采购退货类型", 33 | "t24": "权限管理", 34 | "t25": "用户管理", 35 | "t26": "用户列表", 36 | "t27": "角色列表", 37 | "t28": "数据组列表", 38 | "t29": "组织架构列表", 39 | "t30": "菜单列表", 40 | "t31": "日志管理", 41 | "t32": "操作日志", 42 | "t33": "商品中心", 43 | "t34": "商品管理", 44 | "t35": "商品发布管理", 45 | "t36": "商品上下架管理", 46 | "t37": "发布新商品", 47 | "t38": "商品组合管理", 48 | "t39": "商品回收站", 49 | "t40": "商品库", 50 | "t41": "品牌管理", 51 | "t42": "品牌管理列表", 52 | "t43": "品类管理", 53 | "t44": "虚拟类目管理", 54 | "t45": "销售类目管理", 55 | "t46": "标签管理", 56 | "t47": "商品标签管理", 57 | "t48": "商品打标", 58 | "t49": "数据总线", 59 | "t50": "店铺授权管理", 60 | "t51": "授权店铺", 61 | "t52": "档案管理", 62 | "t53": "地址映射管理", 63 | "t54": "订单中心", 64 | "t55": "订单档案", 65 | "t56": "店铺配置", 66 | "t57": "商品映射", 67 | "t58": "订单标签", 68 | "t59": "物料档案", 69 | "t60": "促销赠品", 70 | "t61": "业务", 71 | "t62": "订单管理", 72 | "t63": "异常订单", 73 | "t64": "订单查询", 74 | "t65": "发货单查询", 75 | "t66": "售后管理", 76 | "t67": "退款单管理", 77 | "t68": "退货退款", 78 | "t69": "库存中心", 79 | "t70": "仓库档案", 80 | "t71": "仓库对照表", 81 | "t72": "上传库存设置", 82 | "t73": "采购管理", 83 | "t74": "采购订单", 84 | "t75": "采购入库单", 85 | "t76": "采购退货出库单", 86 | "t77": "物料库存统计", 87 | "t78": "库存管理", 88 | "t79": "库存调整单", 89 | "t80": "仓内加工单", 90 | "t81": "外部仓库对接", 91 | "t82": "奇门", 92 | "t83": "配置", 93 | "t84": "商品同步", 94 | "t85": "接口日志查询", 95 | "t86": "会员中心", 96 | "t87": "会员360", 97 | "t88": "会员忠诚度", 98 | "t89": "会员等级规则", 99 | "t90": "等级设置", 100 | "t91": "等级升降规则", 101 | "t92": "会员积分规则", 102 | "t93": "积分基础设置", 103 | "t94": "交易奖励积分", 104 | "t95": "互动积分任务", 105 | "t96": "更多", 106 | "t97": "数据管理", 107 | "t98": "元数据管理", 108 | "t99": "会员体系管理" 109 | }, 110 | "routerIndex": { 111 | "t100": "您没有权限,请联系管理员分配权限后再使用", 112 | "t101": "提示", 113 | "t102": "确定", 114 | "t103": "登录", 115 | "t104": "暂无权限", 116 | "t105": "服务不可用", 117 | "t106": "子应用" 118 | }, 119 | "base404": { 120 | "t107": "返回首页" 121 | }, 122 | "base403": { 123 | "t107": "返回首页", 124 | "t108": "暂无权限,请联系管理员" 125 | }, 126 | "base503": { 127 | "t107": "返回首页", 128 | "t109": "刷新试试" 129 | }, 130 | "baseLogin": { 131 | "t1": "Sign In", 132 | "t2": "\\ User Name", 133 | "t3": "\\ Password", 134 | "t4": "\\ Security Code", 135 | "t5": "Next automatic login", 136 | "t110": "密码至少6位", 137 | "t111": "登录成功" 138 | }, 139 | "baseModifyPass": { 140 | "t112": "为了账户安全请立即修改初始密码", 141 | "t113": "请输入旧密码", 142 | "t114": "请输入新密码", 143 | "t115": "确认新密码", 144 | "t116": "位字符,包含数字及字母", 145 | "t117": "密码不符合格式", 146 | "t118": "次密码输入不相同,请重新输入", 147 | "t119": "请修改初始密码", 148 | "t120": "操作成功" 149 | }, 150 | "baseServeError": { 151 | "t121": "关闭" 152 | }, 153 | "baseCommonPagination": { 154 | "t122": "共", 155 | "t123": "条记录", 156 | "t124": "第", 157 | "t125": "页" 158 | }, 159 | "baseCustomDialog": { 160 | "t126": "取消", 161 | "t102": "确定" 162 | }, 163 | "baseCustomModalbox": { 164 | "t126": "取消", 165 | "t135": "标题", 166 | "t136": "内容", 167 | "t137": "确认" 168 | }, 169 | "baseCustomPagination": { 170 | "t1": "共", 171 | "t2": "条记录 第", 172 | "t3": "页" 173 | }, 174 | "baseCustomQuery": { 175 | "t1": "重置", 176 | "t2": "查询", 177 | "t3": "展开", 178 | "t4": "收起" 179 | }, 180 | "baseCustomTagcache": { 181 | "t121": "关闭", 182 | "t127": "刷新", 183 | "t128": "关闭其它" 184 | }, 185 | "baseCustomXlsx": { 186 | "t130": "解析中", 187 | "t131": "请上传正确格式的文件", 188 | "t132": "请导入正确信息", 189 | "t133": "导入文件", 190 | "t134": "导出文件", 191 | "t135": "请先设置getImportData" 192 | }, 193 | "baseLayoutHeader": { 194 | "t101": "提示", 195 | "t102": "确定", 196 | "t126": "取消", 197 | "t139": "请输入您要搜索的关键词", 198 | "t140": "修改密码", 199 | "t141": "退出登录", 200 | "t142": "中文", 201 | "t143": "确定退出当前帐号吗", 202 | "t144": "登出失败" 203 | }, 204 | "baseLayoutIndex": { 205 | "t100": "您没有权限,请联系管理员分配权限后再使用", 206 | "t101": "提示", 207 | "t102": "确定" 208 | }, 209 | "baseTableDraggable": { 210 | "t145": "默认值" 211 | }, 212 | "baseTaskList": { 213 | "t121": "关闭", 214 | "t146": "任务中心", 215 | "t147": "任务名称", 216 | "t148": "任务状态", 217 | "t149": "创建时间", 218 | "t150": "创建结束", 219 | "t151": "操作", 220 | "t152": "查看失败结果", 221 | "t153": "下载", 222 | "t154": "任务的子行号", 223 | "t155": "任务唯一业务编码", 224 | "t156": "成功或错误消息", 225 | "t157": "备注", 226 | "t158": "执行汇报时间", 227 | "t159": "结果枚举", 228 | "t160": "当前进度", 229 | "t161": "任务异常数据", 230 | "t162": "无异常数据" 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | import router from './router'; 4 | import store from './store'; 5 | import { FilterType } from './utils/filters'; 6 | import * as CustomPlugin from './plugin'; 7 | import * as CustomComponents from './components/custom'; 8 | // 解决滚动背景warning 9 | import 'default-passive-events'; 10 | // 引入nprogress样式 11 | import 'nprogress/nprogress.css'; 12 | // 路由拦截注册 13 | import './router/intercept'; 14 | 15 | // 扩展window类型 16 | declare global { 17 | interface Window { 18 | env: { 19 | VITE_BASEURL: string; 20 | VITE_WEBURL: string; 21 | VITE_MICROAPPS: string[]; 22 | }; 23 | } 24 | } 25 | 26 | // 对vue进行类型补充说明 27 | declare module '@vue/runtime-core' { 28 | interface ComponentCustomProperties { 29 | $filters: FilterType; 30 | } 31 | } 32 | 33 | const app = createApp(App); 34 | 35 | // 注册全局组件 36 | app.use(CustomComponents); 37 | // 注册全局 插件/过滤器/指令 38 | app.use(CustomPlugin); 39 | 40 | app.use(router).use(store).mount('#container'); 41 | -------------------------------------------------------------------------------- /src/plugin/dayjs.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | export default dayjs; 4 | -------------------------------------------------------------------------------- /src/plugin/echarts.ts: -------------------------------------------------------------------------------- 1 | // 引入 echarts 核心模块 2 | import * as echarts from 'echarts/core'; 3 | // 引入 Canvas 渲染器 4 | import { CanvasRenderer } from 'echarts/renderers'; 5 | // 引入图表 6 | import { 7 | BarChart, 8 | PieChart, 9 | LineChart, 10 | // 系列类型的定义后缀都为 SeriesOption 11 | BarSeriesOption, 12 | PieSeriesOption, 13 | LineSeriesOption, 14 | } from 'echarts/charts'; 15 | // 引入组件 16 | import { 17 | GridComponent, 18 | TitleComponent, 19 | LegendComponent, 20 | TooltipComponent, 21 | DataZoomComponent, 22 | GraphicComponent, 23 | PolarComponent, 24 | // 组件类型的定义后缀都为 ComponentOption 25 | GridComponentOption, 26 | TitleComponentOption, 27 | LegendComponentOption, 28 | TooltipComponentOption, 29 | DataZoomComponentOption, 30 | GraphicComponentOption, 31 | PolarComponentOption, 32 | } from 'echarts/components'; 33 | 34 | // 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 35 | export type ECOption = echarts.ComposeOption< 36 | | BarSeriesOption 37 | | PieSeriesOption 38 | | LineSeriesOption 39 | | TitleComponentOption 40 | | GridComponentOption 41 | | LegendComponentOption 42 | | TooltipComponentOption 43 | | DataZoomComponentOption 44 | | GraphicComponentOption 45 | | PolarComponentOption 46 | >; 47 | 48 | // 注册必须的组件 49 | echarts.use([ 50 | BarChart, 51 | PieChart, 52 | LineChart, 53 | GridComponent, 54 | TitleComponent, 55 | LegendComponent, 56 | TooltipComponent, 57 | DataZoomComponent, 58 | GraphicComponent, 59 | PolarComponent, 60 | CanvasRenderer, 61 | ]); 62 | 63 | export default echarts; 64 | -------------------------------------------------------------------------------- /src/plugin/element.ts: -------------------------------------------------------------------------------- 1 | // 引入element 2 | import { 3 | ElAlert, 4 | ElAside, 5 | ElAutocomplete, 6 | ElAvatar, 7 | ElBacktop, 8 | ElBadge, 9 | ElBreadcrumb, 10 | ElBreadcrumbItem, 11 | ElButton, 12 | ElButtonGroup, 13 | ElCalendar, 14 | ElCard, 15 | ElCarousel, 16 | ElCarouselItem, 17 | ElCascader, 18 | ElCascaderPanel, 19 | ElCheckbox, 20 | ElCheckboxButton, 21 | ElCheckboxGroup, 22 | ElCol, 23 | ElCollapse, 24 | ElCollapseItem, 25 | ElCollapseTransition, 26 | ElColorPicker, 27 | ElContainer, 28 | ElDatePicker, 29 | ElDialog, 30 | ElDivider, 31 | ElDrawer, 32 | ElDropdown, 33 | ElDropdownItem, 34 | ElDropdownMenu, 35 | ElFooter, 36 | ElForm, 37 | ElFormItem, 38 | ElHeader, 39 | ElIcon, 40 | ElImage, 41 | ElInput, 42 | ElInputNumber, 43 | ElLink, 44 | ElMain, 45 | ElMenu, 46 | ElMenuItem, 47 | ElMenuItemGroup, 48 | ElOption, 49 | ElOptionGroup, 50 | ElPageHeader, 51 | ElPagination, 52 | ElPopconfirm, 53 | ElPopover, 54 | ElPopper, 55 | ElProgress, 56 | ElRadio, 57 | ElRadioButton, 58 | ElRadioGroup, 59 | ElRate, 60 | ElRow, 61 | ElScrollbar, 62 | ElSelect, 63 | ElSlider, 64 | ElStep, 65 | ElSteps, 66 | // ElSubmenu, 67 | ElSwitch, 68 | ElTabPane, 69 | ElTable, 70 | ElTableColumn, 71 | ElTabs, 72 | ElTag, 73 | ElTimePicker, 74 | ElTimeSelect, 75 | ElTimeline, 76 | ElTimelineItem, 77 | ElTooltip, 78 | ElTransfer, 79 | ElTree, 80 | ElUpload, 81 | ElInfiniteScroll, 82 | ElLoading, 83 | ElMessage, 84 | ElMessageBox, 85 | ElNotification, 86 | ElConfigProvider, 87 | } from 'element-plus'; 88 | 89 | const components = [ 90 | ElAlert, 91 | ElAside, 92 | ElAutocomplete, 93 | ElAvatar, 94 | ElBacktop, 95 | ElBadge, 96 | ElBreadcrumb, 97 | ElBreadcrumbItem, 98 | ElButton, 99 | ElButtonGroup, 100 | ElCalendar, 101 | ElCard, 102 | ElCarousel, 103 | ElCarouselItem, 104 | ElCascader, 105 | ElCascaderPanel, 106 | ElCheckbox, 107 | ElCheckboxButton, 108 | ElCheckboxGroup, 109 | ElCol, 110 | ElCollapse, 111 | ElCollapseItem, 112 | ElCollapseTransition, 113 | ElColorPicker, 114 | ElContainer, 115 | ElDatePicker, 116 | ElDialog, 117 | ElDivider, 118 | ElDrawer, 119 | ElDropdown, 120 | ElDropdownItem, 121 | ElDropdownMenu, 122 | ElFooter, 123 | ElForm, 124 | ElFormItem, 125 | ElHeader, 126 | ElIcon, 127 | ElImage, 128 | ElInput, 129 | ElInputNumber, 130 | ElLink, 131 | ElMain, 132 | ElMenu, 133 | ElMenuItem, 134 | ElMenuItemGroup, 135 | ElOption, 136 | ElOptionGroup, 137 | ElPageHeader, 138 | ElPagination, 139 | ElPopconfirm, 140 | ElPopover, 141 | ElPopper, 142 | ElProgress, 143 | ElRadio, 144 | ElRadioButton, 145 | ElRadioGroup, 146 | ElRate, 147 | ElRow, 148 | ElScrollbar, 149 | ElSelect, 150 | ElSlider, 151 | ElStep, 152 | ElSteps, 153 | // ElSubmenu, 154 | ElSwitch, 155 | ElTabPane, 156 | ElTable, 157 | ElTableColumn, 158 | ElTabs, 159 | ElTag, 160 | ElTimePicker, 161 | ElTimeSelect, 162 | ElTimeline, 163 | ElTimelineItem, 164 | ElTooltip, 165 | ElTransfer, 166 | ElTree, 167 | ElUpload, 168 | ElConfigProvider, 169 | ]; 170 | 171 | const plugins = { 172 | ElInfiniteScroll, 173 | ElLoading, 174 | ElMessage, 175 | ElMessageBox, 176 | ElNotification, 177 | }; 178 | 179 | export default { 180 | components, 181 | plugins, 182 | }; 183 | -------------------------------------------------------------------------------- /src/plugin/i18n.ts: -------------------------------------------------------------------------------- 1 | import { createI18n } from 'vue-i18n'; 2 | import enElement from 'element-plus/lib/locale/lang/en'; 3 | import zhElement from 'element-plus/lib/locale/lang/zh-cn'; 4 | import storage from '@/utils/storage'; 5 | // 语言包 6 | import enLocale from '@/locale/en.json'; 7 | import zhLocale from '@/locale/zh-CN.json'; 8 | 9 | if (!storage('localstorage').get('i18n')) { 10 | storage('localstorage').set('i18n', 'zh-CN'); 11 | } 12 | const messages = { 13 | en: { 14 | name: 'en', 15 | el: enElement.el, 16 | ...enLocale, 17 | }, 18 | 'zh-CN': { 19 | name: 'zh-CN', 20 | el: zhElement.el, 21 | ...zhLocale, 22 | }, 23 | }; 24 | const i18n = createI18n({ 25 | locale: storage('localstorage').get('i18n'), 26 | fallbackLocale: enElement.name, 27 | messages, 28 | }); 29 | 30 | export default i18n; 31 | -------------------------------------------------------------------------------- /src/plugin/index.ts: -------------------------------------------------------------------------------- 1 | import { App, Directive } from 'vue'; 2 | import filters, { FilterKey } from '@/utils/filters'; 3 | import * as directives from '@/utils/directives/index'; 4 | import storage from '@/utils/storage'; 5 | import customMessage from '@/components/custom/custom-message'; 6 | // 三方插件 7 | import element from './element'; 8 | import i18n from './i18n'; 9 | import dayjs from './dayjs'; 10 | import lodash from './lodash'; 11 | import echarts from './echarts'; 12 | 13 | // 探测是否支持webp 14 | const canvas = document.createElement('canvas'); 15 | if (canvas.getContext && canvas.getContext('2d')) { 16 | try { 17 | const isWebp = canvas 18 | .toDataURL('image/webp') 19 | .includes('data:image/webp') 20 | .toString(); 21 | storage('localstorage').set('isWebp', isWebp); 22 | } catch (e) { 23 | console.error(e); 24 | } 25 | } 26 | 27 | const install = (app: App): void => { 28 | // 挂载过滤器 29 | app.config.globalProperties.$filters = {}; 30 | Object.keys(filters).forEach((key) => { 31 | app.config.globalProperties.$filters[key] = 32 | filters[key as keyof typeof FilterKey]; 33 | }); 34 | 35 | // 挂载指令 36 | Object.keys(directives).forEach((key) => { 37 | app.directive(key, (directives as { [key: string]: Directive })[key]); 38 | }); 39 | 40 | // 注册element 41 | element.components.forEach((component) => { 42 | if (component.name) app.component(component.name, component); 43 | }); 44 | Object.values(element.plugins).forEach((plugin) => { 45 | app.use(plugin); 46 | }); 47 | app.provide('$message', customMessage); 48 | 49 | // 注册i18n 50 | app.use(i18n); 51 | }; 52 | 53 | export { install, element, i18n, dayjs, lodash, echarts }; 54 | -------------------------------------------------------------------------------- /src/plugin/lodash.ts: -------------------------------------------------------------------------------- 1 | import lodash from 'lodash'; 2 | 3 | export default lodash; 4 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router'; 2 | import Layout from '@/layout/index.vue'; 3 | import { i18n } from '@/plugin'; 4 | 5 | const routes = [ 6 | { 7 | path: '/login', 8 | name: 'Login', 9 | component: () => import('@/views/Login.vue'), 10 | meta: { 11 | title: i18n.global.t('routerIndex.t103'), 12 | }, 13 | }, 14 | { 15 | path: '/:catchAll(.*)', 16 | name: 'Layout', 17 | component: Layout, 18 | meta: { 19 | title: 'Layout', 20 | }, 21 | }, 22 | // { 23 | // path: '/:catchAll(.*)', 24 | // name: 'NotFount', 25 | // component: () => import('@/views/404.vue'), 26 | // meta: { 27 | // title: '页面不存在' 28 | // } 29 | // } 30 | ]; 31 | const router = createRouter({ 32 | history: createWebHistory(import.meta.env.BASE_URL), 33 | routes, 34 | scrollBehavior(to, from, savedPosition) { 35 | if (savedPosition) { 36 | return savedPosition; 37 | } 38 | return { top: 0 }; 39 | }, 40 | }); 41 | 42 | export default router; 43 | -------------------------------------------------------------------------------- /src/router/intercept.ts: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress'; 2 | import router from './index'; 3 | import storage from '@/utils/storage'; 4 | 5 | NProgress.configure({ showSpinner: false }); 6 | // 白名单 7 | const whiteList = ['/login']; 8 | 9 | router.beforeEach(async (to, from, next) => { 10 | // 开始进度条 11 | NProgress.start(); 12 | 13 | const token = storage().get('token') || storage('localstorage').get('token'); 14 | 15 | // 判断是否登录 16 | if (token) { 17 | if (whiteList.includes(to.path)) { 18 | next({ name: 'Layout' }); 19 | NProgress.done(); 20 | } else { 21 | next(); 22 | } 23 | } else if (whiteList.includes(to.path)) { 24 | next(); 25 | } else { 26 | next({ name: 'Login' }); 27 | NProgress.done(); 28 | } 29 | }); 30 | 31 | router.afterEach(() => { 32 | // 完成进度条 33 | NProgress.done(); 34 | }); 35 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | interface ImportMetaEnv { 2 | VITE_VERSION: string; 3 | VITE_BASEURL: string; 4 | VITE_VILEURL: string; 5 | VITE_WEBURL: string; 6 | } 7 | 8 | declare module '*.vue' { 9 | import { ComponentOptions } from 'vue'; 10 | 11 | const componentOptions: ComponentOptions; 12 | export default componentOptions; 13 | } 14 | 15 | declare module 'echarts/lib/echarts'; 16 | 17 | declare module 'js-md5'; 18 | 19 | declare module 'lodash'; 20 | 21 | declare module 'nprogress'; 22 | 23 | declare module 'path-browserify'; 24 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore, Store } from 'vuex'; 2 | import vuexPersistedstate from 'vuex-persistedstate'; 3 | import basic from './modules/basic'; 4 | import { BasicState } from './types'; 5 | 6 | export type State = { 7 | basic: BasicState; 8 | }; 9 | 10 | const store: Store = createStore({ 11 | plugins: [vuexPersistedstate()], 12 | modules: { 13 | basic, 14 | }, 15 | }); 16 | 17 | export default store; 18 | -------------------------------------------------------------------------------- /src/store/types/index.ts: -------------------------------------------------------------------------------- 1 | import { _RouteLocationBase, RouteMeta } from 'vue-router'; 2 | 3 | export interface PopupType { 4 | status: boolean; 5 | type?: string; 6 | data?: unknown; 7 | } 8 | 9 | export interface CurMenuType { 10 | tab?: boolean; 11 | affix?: boolean; 12 | noCache?: boolean; 13 | iconCode: string; 14 | name: string; 15 | title: string; 16 | routeName: string; 17 | routePath: string; 18 | children: Array; 19 | permissions?: Array; 20 | } 21 | 22 | export interface UserInfoType { 23 | id?: number; 24 | account?: string; 25 | accountName?: string; 26 | } 27 | 28 | export interface MetaType extends RouteMeta { 29 | title?: string; 30 | cacheName?: string; 31 | noCache?: number; 32 | } 33 | 34 | export interface TagView extends Partial<_RouteLocationBase> { 35 | title?: string; 36 | name?: string; 37 | meta: MetaType; 38 | } 39 | 40 | export interface BasicState { 41 | errorPage: Partial; 42 | modifyPass: Partial; 43 | userInfo: Partial; 44 | menuList: CurMenuType[]; 45 | permissionsMap: Map; 46 | language: string; 47 | menuCollapse: boolean; 48 | taskDotStatus: boolean; 49 | visitedViews: TagView[]; 50 | cachedViews: string[]; 51 | noCachedViews: string[]; 52 | } 53 | -------------------------------------------------------------------------------- /src/utils/directives/designform.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | 3 | export const designform: Directive = { 4 | beforeMount(el) { 5 | const input = el.querySelector('.el-input__inner'); 6 | const label = document.createElement('label'); 7 | el.classList.add('design-form'); 8 | label.innerHTML = input?.getAttribute('placeholder') as string; 9 | label.classList.add('input-label'); 10 | const box = el.querySelector('.el-input') || el; 11 | box.insertBefore(label, (input as HTMLElement).nextSibling); 12 | }, 13 | mounted(el) { 14 | const input = el.querySelector('.el-input__inner'); 15 | const label = el.querySelector('.input-label'); 16 | const { paddingLeft } = getComputedStyle(input as HTMLElement, null); 17 | (label as HTMLElement).style.left = `${parseInt(paddingLeft, 10)}px`; 18 | }, 19 | updated(el, bind) { 20 | const label = el.querySelector('.input-label'); 21 | if (bind.arg) (label as HTMLElement).innerHTML = bind.arg; 22 | if (!el.classList.contains('design-form')) { 23 | setTimeout(() => { 24 | el.classList.add('design-form'); 25 | }, 0); 26 | } 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /src/utils/directives/index.ts: -------------------------------------------------------------------------------- 1 | export * from './permission'; 2 | export * from './loadmore'; 3 | export * from './designform'; 4 | export * from './onlyNumber'; 5 | -------------------------------------------------------------------------------- /src/utils/directives/inputLimit.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | 3 | const trigger = (el: HTMLInputElement, type: string) => { 4 | const e = document.createEvent('HTMLEvents'); 5 | e.initEvent(type, true, true); 6 | el.dispatchEvent(e); 7 | }; 8 | 9 | /** 10 | * 11 | */ 12 | 13 | // 禁止输入表情 14 | const reg = 15 | /[^\u0020-\u007E\u00A0-\u00BE\u2E80-\uA4CF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF\u0080-\u009F\u2000-\u201f\u2026\u2022\u20ac\r\n]/g; 16 | export const inputLimit: Directive = { 17 | beforeMount(el, binding) { 18 | const { len } = binding.value; 19 | const type = binding.value.type || 'input'; 20 | const input = el.getElementsByTagName(type)[0]; 21 | const trim = binding.value.trim || false; // select 可搜索模式去空格 22 | input.onkeyup = function () { 23 | let temp = 0; 24 | const filterValue = trim 25 | ? input.value.replace(/^\s+|\s+$/g, '').replace(reg, '') 26 | : input.value.replace(reg, ''); // 去首尾空格去表情 27 | input.value = filterValue; 28 | // 大写字母,汉字算两个字符,其他都算一个字符 29 | for (let i = 0; i < input.value.length; i++) { 30 | if (/[A-Z\u4e00-\u9fa5]/.test(input.value[i])) { 31 | temp += 2; 32 | } else { 33 | temp++; 34 | } 35 | if (temp > len) { 36 | input.value = input.value.substr(0, i); 37 | } 38 | } 39 | trigger(input, 'input'); 40 | }; 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /src/utils/directives/loadmore.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | 3 | export const loadmore: Directive = { 4 | mounted(el, binding) { 5 | // 获取element-ui定义好的scroll盒子 6 | const SELECTWRAP_DOM = el.querySelector( 7 | '.el-select-dropdown .el-select-dropdown__wrap' 8 | ); 9 | (SELECTWRAP_DOM as Element).addEventListener( 10 | 'scroll', 11 | function (this: HTMLElement) { 12 | const CONDITION = 13 | this.scrollHeight - this.scrollTop <= this.clientHeight; 14 | if (CONDITION) { 15 | binding.value(); 16 | } 17 | } 18 | ); 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/utils/directives/onlyNumber.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | 3 | export const onlyNumber: Directive = { 4 | beforeMount(el, vDir) { 5 | // vDir.value 有指令的参数 6 | let content; 7 | // 按键按下=>只允许输入 数字/小数点 8 | el.addEventListener('keypress', (evt: KeyboardEvent) => { 9 | const inputKey = String.fromCharCode( 10 | typeof evt.charCode === 'number' ? evt.charCode : evt.keyCode 11 | ); 12 | const re = /\d|\.|-/; 13 | content = (evt.target as HTMLInputElement).value; 14 | // 定义方法,阻止输入 15 | function preventInput() { 16 | if (evt.preventDefault) { 17 | evt.preventDefault(); 18 | } else { 19 | evt.returnValue = false; 20 | } 21 | } 22 | if (!re.test(inputKey) && !evt.ctrlKey) { 23 | preventInput(); 24 | } else if (content.indexOf('.') > 0 && inputKey === '.') { 25 | // 已有小数点,再次输入小数点 26 | preventInput(); 27 | } 28 | }); 29 | // 按键弹起=>并限制最大最小 30 | el.addEventListener('keyup', (evt: KeyboardEvent) => { 31 | if ((evt.target as HTMLInputElement)?.value) { 32 | content = parseFloat((evt.target as HTMLInputElement).value); 33 | if (!content) { 34 | content = 0.0; 35 | } 36 | // 限制最大最小值 37 | let argMax = 0; 38 | let argMin = 0; 39 | if (vDir.value) { 40 | argMax = parseFloat(vDir.value.max); 41 | argMin = parseFloat(vDir.value.min); 42 | } 43 | if (!Number.isNaN(argMax) && content > argMax) { 44 | (evt.target as HTMLInputElement).value = `${argMax}`; 45 | content = argMax; 46 | } 47 | if (!Number.isNaN(argMin) && content < argMin) { 48 | (evt.target as HTMLInputElement).value = `${argMin}`; 49 | content = argMin; 50 | } 51 | } 52 | }); 53 | // 失去焦点=>保留指定位小数 54 | el.addEventListener('focusout', (evt: KeyboardEvent) => { 55 | // 此处会在 el-input 的 @change 后执行 56 | // 限制最大最小值 57 | if ((evt.target as HTMLInputElement).value) { 58 | content = parseFloat((evt.target as HTMLInputElement).value); 59 | if (!content) { 60 | content = 0.0; 61 | } 62 | let argPrecision = 0; // 默认保留至整数 63 | if (vDir.value.precision) { 64 | argPrecision = parseFloat(vDir.value.precision); 65 | } 66 | let argMax = 0; 67 | let argMin = 0; 68 | if (vDir.value) { 69 | argMax = parseFloat(vDir.value.max); 70 | argMin = parseFloat(vDir.value.min); 71 | } 72 | if (!Number.isNaN(argMax) && content > argMax) { 73 | (evt.target as HTMLInputElement).value = `${argMax}`; 74 | content = argMax; 75 | } 76 | if (!Number.isNaN(argMin) && content < argMin) { 77 | (evt.target as HTMLInputElement).value = `${argMin}`; 78 | content = argMin; 79 | } 80 | 81 | // 保留几位小数 82 | (evt.target as HTMLInputElement).value = content.toFixed(argPrecision); 83 | } 84 | }); 85 | }, 86 | }; 87 | -------------------------------------------------------------------------------- /src/utils/directives/permission.ts: -------------------------------------------------------------------------------- 1 | import { Directive } from 'vue'; 2 | import store from '@/store'; 3 | 4 | export const permission: Directive = { 5 | updated(el, binding, vnode: any) { 6 | const { value } = binding; 7 | const { permissionsMap } = store.state.basic; 8 | const routePath = vnode.context?.$route.path as string; 9 | if (value) { 10 | const hasPermission = permissionsMap.get(routePath)?.includes(value); 11 | if (!hasPermission) { 12 | el.style.display = 'none'; 13 | el.style.visibility = 'hidden'; 14 | } else { 15 | // el.style.display = originDisplay; 16 | // el.style.visibility = originVisibility; 17 | } 18 | } 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/utils/filters.ts: -------------------------------------------------------------------------------- 1 | // 引入tools下的过滤函数 2 | import { 3 | multiply, 4 | divide, 5 | priceFilter, 6 | textTohtml, 7 | handleImage, 8 | isAllow, 9 | decimalFormat, 10 | dateFormat, 11 | } from './tools'; 12 | 13 | export enum FilterKey { 14 | multiply = 'multiply', 15 | divide = 'divide', 16 | priceFilter = 'priceFilter', 17 | textTohtml = 'textTohtml', 18 | handleImage = 'handleImage', 19 | dateFormat = 'dateFormat', 20 | decimalFormat = 'decimalFormat', 21 | isAllow = 'isAllow', 22 | } 23 | 24 | class FunctionType extends Function {} 25 | 26 | export type FilterType = { 27 | [key in FilterKey]: FunctionType; 28 | }; 29 | 30 | const filters: FilterType = { 31 | multiply, 32 | divide, 33 | priceFilter, 34 | textTohtml, 35 | handleImage, 36 | isAllow, 37 | decimalFormat, 38 | dateFormat, 39 | }; 40 | 41 | export default filters; 42 | -------------------------------------------------------------------------------- /src/utils/pager.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 使用rxjs解决应用间通信问题 3 | * @date 2020-8-5 4 | */ 5 | import { Subject } from 'rxjs'; 6 | import { setPageTitle } from '@/utils/tools'; 7 | 8 | const pager = new Subject(); 9 | 10 | // 全局监听子应用消息 11 | pager.subscribe((v: any) => { 12 | switch (v.name) { 13 | // 设置页面标题 14 | case 'routeChange': 15 | document.title = setPageTitle(v.to.meta.title); 16 | break; 17 | default: 18 | break; 19 | } 20 | }); 21 | 22 | export default pager; 23 | -------------------------------------------------------------------------------- /src/utils/storage/cookie.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 存储封装对外提供统一的方法及接口使用 3 | * Cookie 存储到客户端 4 | */ 5 | import { StorageType } from './index'; 6 | 7 | class CookieAPI implements StorageType { 8 | set(key: string, value: string, expire = 3600, path = '/'): void { 9 | const date = new Date(); 10 | date.setSeconds(date.getSeconds() + expire); 11 | document.cookie = `${key}=${escape( 12 | value 13 | )}; expires=${date.toUTCString()}; Path=${path}`; 14 | } 15 | 16 | get(key: string): string { 17 | if (document.cookie.length > 0) { 18 | let cStart = document.cookie.indexOf(`${key}=`); 19 | if (cStart !== -1) { 20 | cStart = cStart + key.length + 1; 21 | let cEnd = document.cookie.indexOf(';', cStart); 22 | if (cEnd === -1) cEnd = document.cookie.length; 23 | return unescape(document.cookie.substring(cStart, cEnd)); 24 | } 25 | } 26 | return ''; 27 | } 28 | 29 | remove(key: string): void { 30 | this.set(key, '', -1); 31 | } 32 | } 33 | 34 | export default CookieAPI; 35 | -------------------------------------------------------------------------------- /src/utils/storage/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 统一封装对外的接口 3 | */ 4 | 5 | import SessionStorageAPI from './sessionstorage'; 6 | import LocalStorageAPI from './localstorage'; 7 | import CookieAPI from './cookie'; 8 | 9 | export interface StorageType { 10 | set(key: string, value: string): void; 11 | get(key: string): string; 12 | remove(key: string): void; 13 | setExpire?(key: string, value: string, expire: number): void; 14 | getExpire?(key: string): string; 15 | } 16 | 17 | export default (type?: string): StorageType => { 18 | let UseStore; 19 | switch (type) { 20 | case 'session': 21 | UseStore = LocalStorageAPI; 22 | break; 23 | case 'cookie': 24 | UseStore = CookieAPI; 25 | break; 26 | case 'localstorage': 27 | UseStore = LocalStorageAPI; 28 | break; 29 | default: 30 | UseStore = SessionStorageAPI; 31 | break; 32 | } 33 | return new UseStore(); 34 | }; 35 | -------------------------------------------------------------------------------- /src/utils/storage/localstorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 存储封装对外提供统一的方法及接口使用 3 | * Localstorage 存储到客户端 4 | */ 5 | import { StorageType } from './index'; 6 | 7 | class LocalStorageAPI implements StorageType { 8 | set(key: string, value: string): void { 9 | try { 10 | localStorage.setItem(key, value); 11 | } catch (e) { 12 | if ((e as SyntaxError).name === 'QuotaExceededError') { 13 | throw new Error('Out of Memory Limit Localstorage'); 14 | } else { 15 | throw new Error((e as SyntaxError).name); 16 | } 17 | } 18 | } 19 | 20 | get(key: string): string { 21 | return localStorage.getItem(key) ?? ''; 22 | } 23 | 24 | remove(key: string): void { 25 | localStorage.removeItem(key); 26 | } 27 | 28 | setExpire(key: string, value: string, expire: number): void { 29 | const currTime = new Date().getTime(); 30 | return this.set( 31 | key, 32 | JSON.stringify({ val: value, time: currTime + expire }) 33 | ); 34 | } 35 | 36 | getExpire(key: string): string { 37 | const val: string = this.get(key); 38 | const dataObj = JSON.parse(val); 39 | if (new Date().getTime() - dataObj.time > 0) { 40 | return dataObj.val; 41 | } 42 | return ''; 43 | } 44 | } 45 | 46 | export default LocalStorageAPI; 47 | -------------------------------------------------------------------------------- /src/utils/storage/sessionstorage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 存储封装对外提供统一的方法及接口使用 3 | * sessionStorage 存储到客户端 4 | */ 5 | import { StorageType } from './index'; 6 | 7 | class SessionStorageAPI implements StorageType { 8 | set(key: string, value: string): void { 9 | return sessionStorage.setItem(key, value); 10 | } 11 | 12 | get(key: string): string { 13 | return sessionStorage.getItem(key) ?? ''; 14 | } 15 | 16 | remove(key: string): void { 17 | return sessionStorage.removeItem(key); 18 | } 19 | } 20 | 21 | export default SessionStorageAPI; 22 | -------------------------------------------------------------------------------- /src/views/404.vue: -------------------------------------------------------------------------------- 1 | 21 | 28 | 98 | -------------------------------------------------------------------------------- /src/views/components/index.ts: -------------------------------------------------------------------------------- 1 | import TaskList from './TaskList.vue'; 2 | 3 | export { TaskList }; 4 | -------------------------------------------------------------------------------- /static/img/403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/static/img/403.png -------------------------------------------------------------------------------- /static/img/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/static/img/404.png -------------------------------------------------------------------------------- /static/img/503.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/static/img/503.png -------------------------------------------------------------------------------- /static/img/login_bg1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/static/img/login_bg1.png -------------------------------------------------------------------------------- /static/img/login_bg2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunweijieMJ/vite-vue3-temp/18ba0d5f8d6fc2829611e98da6caf46fa787b8b0/static/img/login_bg2.png -------------------------------------------------------------------------------- /static/svg/default_user.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "allowSyntheticDefaultImports": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": ["esnext", "dom"], 13 | "types": ["vite/client"], 14 | "baseUrl": "./", 15 | "paths": { 16 | "@/*": ["src/*"] 17 | }, 18 | "plugins": [ 19 | { 20 | "name": "@vuedx/typescript-plugin-vue" 21 | } 22 | ] 23 | }, 24 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import vueJsx from '@vitejs/plugin-vue-jsx'; 4 | import viteCompression from 'vite-plugin-compression'; 5 | import styleImport from 'vite-plugin-style-import'; 6 | import vueI18n from '@intlify/vite-plugin-vue-i18n'; 7 | import path from 'path'; 8 | 9 | const port = 9000; 10 | const timeStamp = Date.now(); 11 | 12 | // https://vitejs.dev/config/ 13 | export default ({ mode }: { mode: string }): unknown => { 14 | process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }; 15 | return defineConfig({ 16 | plugins: [ 17 | vue(), 18 | vueJsx(), 19 | viteCompression({ 20 | verbose: true, 21 | disable: false, 22 | threshold: 1024 * 10, 23 | algorithm: 'gzip', 24 | ext: '.gz', 25 | }), 26 | styleImport({ 27 | libs: [ 28 | { 29 | libraryName: 'element-plus', 30 | esModule: true, 31 | ensureStyleFile: true, 32 | resolveStyle: (name) => { 33 | name = name.slice(3); 34 | return `element-plus/packages/theme-chalk/src/${name}.scss`; 35 | }, 36 | resolveComponent: (name) => { 37 | return `element-plus/lib/${name}`; 38 | }, 39 | }, 40 | ], 41 | }), 42 | vueI18n({ 43 | compositionOnly: false, 44 | include: path.resolve(__dirname, './src/locale/**'), 45 | }), 46 | ], 47 | resolve: { 48 | alias: { 49 | '@': path.resolve(__dirname, 'src'), 50 | 'vue-i18n': 'vue-i18n/dist/vue-i18n.esm-browser.prod', 51 | }, 52 | }, 53 | css: { 54 | preprocessorOptions: { 55 | scss: { 56 | additionalData: '@use "@/assets/scss/_base.scss" as *;', 57 | }, 58 | }, 59 | }, 60 | server: { 61 | host: 'localhost', 62 | port, 63 | https: false, 64 | proxy: { 65 | '^/(masterdataDomain|taskDomain|userDomain|pim/admin|oms/admin)': { 66 | target: 'http://dev.backendapi.aid.connext.net.cn/', // 开发 67 | // target: 'http://test.backendapi.aid.connext.net.cn/' // 测试 68 | }, 69 | }, 70 | }, 71 | build: { 72 | assetsDir: 'static/assets', 73 | rollupOptions: { 74 | output: { 75 | entryFileNames: `static/js/[name].${process.env.VITE_VERSION}.t${timeStamp}.js`, 76 | chunkFileNames: `static/js/[name].${process.env.VITE_VERSION}.t${timeStamp}.js`, 77 | assetFileNames: `static/js/[name].${process.env.VITE_VERSION}.t${timeStamp}.[ext]`, 78 | manualChunks(id) { 79 | const chunkMap = new Map(); 80 | chunkMap.set(/[\\/]src[\\/]layout[\\/]/.test(id), 'basicLayout'); 81 | chunkMap.set( 82 | /[\\/]src[\\/]components[\\/]/.test(id), 83 | 'basicComponent' 84 | ); 85 | chunkMap.set(/[\\/]node_modules[\\/]/.test(id), 'vendors'); 86 | chunkMap.set( 87 | /[\\/]node_modules[\\/]echarts[\\/]/.test(id), 88 | 'echarts' 89 | ); 90 | chunkMap.set( 91 | /[\\/]node_modules[\\/]lodash[\\/]/.test(id), 92 | 'lodash' 93 | ); 94 | chunkMap.set(/[\\/]node_modules[\\/]dayjs[\\/]/.test(id), 'dayjs'); 95 | chunkMap.set( 96 | /[\\/]node_modules[\\/]xlsx[\\/]xlsx.js/.test(id), 97 | 'xlsxIndex' 98 | ); 99 | chunkMap.set( 100 | /[\\/]node_modules[\\/]xlsx[\\/](?!(xlsx.js))/.test(id), 101 | 'xlsx' 102 | ); 103 | chunkMap.set( 104 | /[\\/]node_modules[\\/]element-plus[\\/]/.test(id), 105 | 'element' 106 | ); 107 | if (chunkMap.get(true)) return chunkMap.get(true); 108 | return null; 109 | }, 110 | }, 111 | }, 112 | }, 113 | }); 114 | }; 115 | --------------------------------------------------------------------------------