Compare commits

..

10 Commits

Author SHA1 Message Date
xiaoxian521
46a48a5650 release: update 2.8.5 2022-01-21 16:46:48 +08:00
xiaoxian521
8fa3448af2 chore: update typescript 2022-01-21 16:33:45 +08:00
xiaoxian521
f236829b0f refactor: use @iconify-icons/ep 2022-01-21 16:10:10 +08:00
xiaoxian521
47dea87275 fix: update lintstagedrc 2022-01-18 17:06:15 +08:00
xiaoxian521
5e9198c2e0 fix: vscode setting 2022-01-18 17:05:42 +08:00
xiaoxian521
c9c8f20cc5 docs: update 2022-01-09 18:44:23 +08:00
xiaoxian521
a19c97f152 fix: add vite-plugin-live-reload 2022-01-09 12:11:50 +08:00
xiaoxian521
5e1e9d3c5f feat: 添加线上环境删console插件vite-plugin-remove-console 2022-01-07 17:57:55 +08:00
xiaoxian521
96153f8a14 feat: 添加WindiCSS支持 2022-01-07 17:20:25 +08:00
xiaoxian521
14adf692ab perf: theme 2022-01-07 14:45:21 +08:00
50 changed files with 1484 additions and 1189 deletions

View File

@@ -1,8 +1,6 @@
module.exports = { module.exports = {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"], "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [ "{!(package)*.json,.!(browserslist)*rc}": ["prettier --write--parser json"],
"prettier --write--parser json"
],
"package.json": ["prettier --write"], "package.json": ["prettier --write"],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"], "*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"], "*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],

View File

@@ -1,11 +1,16 @@
{ {
"recommendations": [ "recommendations": [
"johnsoncodehk.vscode-typescript-vue-plugin",
"voorjaar.windicss-intellisense",
"vscode-icons-team.vscode-icons",
"davidanson.vscode-markdownlint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"johnsoncodehk.volar", "johnsoncodehk.volar",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"mikestead.dotenv", "mikestead.dotenv",
"eamodio.gitlens",
"antfu.iconify" "antfu.iconify"
] ]
} }

17
.vscode/settings.json vendored
View File

@@ -1,15 +1,4 @@
{ {
/** 便
* ESLint
* Prettier - Code formatter
* stylelint
* vscode-icons
* i18n Ally
* Iconify IntelliSense
* TypeScript Vue Plugin (Volar)
* Vue Language Features (Volar)
*/
"terminal.integrated.rendererType": "dom",
"editor.formatOnType": true, "editor.formatOnType": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"javascript.updateImportsOnFileMove.enabled": "always", "javascript.updateImportsOnFileMove.enabled": "always",
@@ -27,26 +16,20 @@
"editor.suggestSelection": "first", "editor.suggestSelection": "first",
"editor.acceptSuggestionOnCommitCharacter": false, "editor.acceptSuggestionOnCommitCharacter": false,
"css.lint.propertyIgnoredDueToDisplay": "ignore", "css.lint.propertyIgnoredDueToDisplay": "ignore",
// Prevent inline styles from being automatically formatted to all lowercase
"editor.quickSuggestions": { "editor.quickSuggestions": {
"other": true, "other": true,
"comments": true, "comments": true,
"strings": true "strings": true
}, },
// Automatically fix some syntax errors of ts
"tslint.autoFixOnSave": true,
"files.associations": { "files.associations": {
// Specifies the location of snippets in the suggestion widget
"editor.snippetSuggestions": "top" "editor.snippetSuggestions": "top"
}, },
"[css]": { "[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"cSpell.userWords": ["sourcemap", "vite"],
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"volar.tsPlugin": true,
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/plugins/i18n"], "i18n-ally.localesPaths": ["src/plugins/i18n"],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",

View File

@@ -1,3 +1,14 @@
# 2.8.5 (2022-1-21)
### 🎫 Feat
- Added `WindiCSS` support
- Add online environment remove console plugin `vite-plugin-remove-console`
### ✔️ refactor
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`
# 2.8.0(2022-1-4) # 2.8.0(2022-1-4)
### 🎫 Feat ### 🎫 Feat

View File

@@ -1,3 +1,14 @@
# 2.8.5 (2022-1-21)
### 🎫 Feat
- Added `WindiCSS` support
- Add online environment remove console plugin `vite-plugin-remove-console`
### ✔️ refactor
- Replace `@element-plus/icons-vue` with `@iconify-icons/ep`
# 2.8.0(2022-1-4) # 2.8.0(2022-1-4)
### 🎫 Feat ### 🎫 Feat

View File

@@ -1,3 +1,14 @@
# 2.8.5(2022-1-21)
### 🎫 Feat
- 添加 `WindiCSS` 支持
- 添加线上环境删 console 插件`vite-plugin-remove-console`
### ✔️ refactor
- 使用`@iconify-icons/ep`替换`@element-plus/icons-vue`
# 2.8.0(2022-1-4) # 2.8.0(2022-1-4)
### 🎫 Feat ### 🎫 Feat

View File

@@ -1,6 +1,8 @@
<h1>vue-pure-admin</h1> <h1>vue-pure-admin</h1>
[![license](https://img.shields.io/github/license/xiaoxian521/vue-pure-admin.svg)](LICENSE) ![GitHub license](https://img.shields.io/github/license/xiaoxian521/vue-pure-admin?style=flat)
![GitHub stars](https://img.shields.io/github/stars/xiaoxian521/vue-pure-admin?color=fa6470&style=flat)
![GitHub forks](https://img.shields.io/github/forks/xiaoxian521/vue-pure-admin?style=flat)
**English** | [中文](./README.md) **English** | [中文](./README.md)
@@ -10,16 +12,16 @@ vue-pure-admin is a free and open source middle and back-end template. Using the
## Supporting Video ## Supporting Video
Tutorial: <https://www.bilibili.com/video/BV1534y1S7HV/> - [Click Watch Tutorial](https://www.bilibili.com/video/BV1534y1S7HV)
UI Design: <https://www.bilibili.com/video/BV17g411T7rq/> - [Click Watch UI Design](https://www.bilibili.com/video/BV17g411T7rq)
## Docs ## Docs
<https://pure-admin-doc.vercel.app/> - [Click Watch Docs](https://pure-admin-doc.vercel.app)
## Thin ## Thin
Github Address: <https://github.com/xiaoxian521/pure-admin-thin> - [Click Watch Thin](https://github.com/xiaoxian521/pure-admin-thin)
## Preview ## Preview

View File

@@ -1,6 +1,8 @@
<h1>vue-pure-admin</h1> <h1>vue-pure-admin</h1>
[![license](https://img.shields.io/github/license/xiaoxian521/vue-pure-admin.svg)](LICENSE) ![GitHub license](https://img.shields.io/github/license/xiaoxian521/vue-pure-admin?style=flat)
![GitHub stars](https://img.shields.io/github/stars/xiaoxian521/vue-pure-admin?color=fa6470&style=flat)
![GitHub forks](https://img.shields.io/github/forks/xiaoxian521/vue-pure-admin?style=flat)
**中文** | [English](./README.en-US.md) **中文** | [English](./README.en-US.md)
@@ -10,16 +12,16 @@ vue-pure-admin 是一个免费开源的中后台模版。使用了最新的`vue3
## 配套视频 ## 配套视频
教程:<https://www.bilibili.com/video/BV1534y1S7HV/> - [点我查看教程](https://www.bilibili.com/video/BV1534y1S7HV)
UI 设计:<https://www.bilibili.com/video/BV17g411T7rq/> - [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
## 配套文档 ## 配套文档
<https://pure-admin-doc.vercel.app/> - [点我查看文档](https://pure-admin-doc.vercel.app)
## 精简版 ## 精简版
仓库地址:<https://github.com/xiaoxian521/pure-admin-thin> - [点我查看精简版](https://github.com/xiaoxian521/pure-admin-thin)
## 预览 ## 预览

View File

@@ -1,6 +0,0 @@
const productPlugins = [];
process.env.NODE_ENV === "production" &&
productPlugins.push("transform-remove-console");
module.exports = {
plugins: [...productPlugins]
};

View File

@@ -27,94 +27,78 @@
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
background: #000;
overflow: hidden; overflow: hidden;
font-family: "Reggae One", cursive; font-family: "Reggae One", cursive;
} }
p { .loader,
font-size: 8vw; .loader:before,
overflow: hidden; .loader:after {
-webkit-text-stroke: 3px #7272a5; border-radius: 50%;
width: 2.5em;
height: 2.5em;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation: loadAnimation 1.8s infinite ease-in-out;
animation: loadAnimation 1.8s infinite ease-in-out;
} }
span { .loader {
display: block; color: #406eeb;
font-size: 20px; font-size: 10px;
overflow: hidden; margin: 80px auto;
color: green; position: relative;
text-align: center; text-indent: -9999em;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
} }
p::before { .loader:before,
.loader:after {
content: ""; content: "";
width: 100%;
height: 100%;
position: absolute; position: absolute;
left: 0;
top: 0; top: 0;
background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
mix-blend-mode: multiply;
} }
p::after { .loader:before {
content: ""; left: -3.5em;
background: radial-gradient(circle, #fff, #000 50%); -webkit-animation-delay: -0.32s;
background-size: 25% 25%; animation-delay: -0.32s;
position: absolute;
top: -100%;
left: -100%;
right: 0;
bottom: 0;
mix-blend-mode: color-dodge;
animation: mix 2s linear infinite;
} }
@keyframes mix { .loader:after {
to { left: 3.5em;
transform: translate(50%, 50%); }
@-webkit-keyframes loadAnimation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
}
}
@keyframes loadAnimation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
} }
} }
</style> </style>
<div class="g-container"> <div class="loader">Loading...</div>
<p>Pure-Admin</p>
<span class="_develop"></span>
</div> </div>
</div>
<script>
// 此代码仅用于开发环境的友好提示项目打包前请去掉这段js代码 This code is only used as a friendly reminder of the development environment, please remove this js code before packaging the project
window.onload = function () {
(function () {
const ua = navigator.userAgent.toLowerCase();
const re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
const m = ua.match(re);
const Sys = {
browser: m[1].replace(/version/, "'safari"),
version: m[2]
};
const browser = Array.of("chrome", "firefox").includes(Sys.browser);
const version = parseFloat(Sys.version);
const el = document.querySelector("._develop");
if (el) {
if (browser && version >= 90) {
let success =
document.createTextNode("当前浏览器版本很适合开发!!! 😃");
el.appendChild(success);
} else {
let warn = document.createTextNode(
"当前浏览器版本不适合开发,建议使用最新版本的谷歌或者火狐浏览器!!!😯"
);
el.appendChild(warn);
el.style.color = "red";
}
}
return Sys;
})();
};
</script>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

View File

@@ -7,7 +7,7 @@ const systemRouter = {
name: "system", name: "system",
redirect: "/system/user/index", redirect: "/system/user/index",
meta: { meta: {
icon: "Setting", icon: "setting",
title: "menus.hssysManagement", title: "menus.hssysManagement",
i18n: true, i18n: true,
showLink: true, showLink: true,
@@ -42,7 +42,7 @@ const permissionRouter = {
redirect: "/permission/page/index", redirect: "/permission/page/index",
meta: { meta: {
title: "menus.permission", title: "menus.permission",
icon: "Lollipop", icon: "lollipop",
i18n: true, i18n: true,
showLink: true, showLink: true,
rank: 3 rank: 3

View File

@@ -1,6 +1,6 @@
{ {
"name": "vue-pure-admin", "name": "vue-pure-admin",
"version": "2.8.0", "version": "2.8.5",
"private": true, "private": true,
"engines": { "engines": {
"node": ">= 16", "node": ">= 16",
@@ -18,7 +18,7 @@
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js", "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged", "lint:pretty": "pretty-quick --staged",
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint && pnpm lint:pretty", "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install", "prepare": "husky install",
"preinstall": "npx only-allow pnpm" "preinstall": "npx only-allow pnpm"
}, },
@@ -30,16 +30,16 @@
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@ctrl/tinycolor": "^3.4.0", "@ctrl/tinycolor": "^3.4.0",
"@element-plus/icons-vue": "^0.2.4",
"@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/vue-fontawesome": "^3.0.0-5", "@fortawesome/vue-fontawesome": "^3.0.0-5",
"@logicflow/core": "0.7.1", "@logicflow/core": "0.7.1",
"@logicflow/extension": "0.7.1", "@logicflow/extension": "0.7.1",
"@vueuse/core": "^6.7.1", "@vueuse/core": "^7.5.3",
"@vueuse/motion": "^2.0.0-beta.4", "@vueuse/motion": "^2.0.0-beta.9",
"@vueuse/shared": "^7.5.3",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^0.21.1", "axios": "^0.25.0",
"cropperjs": "^1.5.11", "cropperjs": "^1.5.11",
"css-color-function": "^1.3.3", "css-color-function": "^1.3.3",
"dayjs": "^1.10.7", "dayjs": "^1.10.7",
@@ -54,18 +54,18 @@
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.0.0-rc.14", "pinia": "^2.0.9",
"qs": "^6.10.1", "qs": "^6.10.1",
"remixicon": "^2.5.0", "remixicon": "^2.5.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11", "responsive-storage": "^1.0.11",
"rgb-hex": "^4.0.0", "rgb-hex": "^4.0.0",
"v-contextmenu": "3.0.0", "v-contextmenu": "3.0.0",
"vue": "^3.2.24", "vue": "^3.2.27",
"vue-i18n": "^9.2.0-beta.3", "vue-i18n": "^9.2.0-beta.26",
"vue-json-pretty": "^2.0.2", "vue-json-pretty": "^2.0.2",
"vue-router": "^4.0.12", "vue-router": "^4.0.12",
"vue-types": "^4.1.0", "vue-types": "^4.1.1",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"vxe-table": "^4.1.18", "vxe-table": "^4.1.18",
"wangeditor": "^4.7.9", "wangeditor": "^4.7.9",
@@ -75,6 +75,8 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@iconify-icons/ep": "^1.1.3",
"@iconify/vue": "^3.1.2",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
"@types/mockjs": "1.0.3", "@types/mockjs": "1.0.3",
@@ -84,14 +86,12 @@
"@typescript-eslint/eslint-plugin": "4.31.0", "@typescript-eslint/eslint-plugin": "4.31.0",
"@typescript-eslint/parser": "4.31.0", "@typescript-eslint/parser": "4.31.0",
"@vitejs/plugin-legacy": "^1.6.4", "@vitejs/plugin-legacy": "^1.6.4",
"@vitejs/plugin-vue": "^1.10.2", "@vitejs/plugin-vue": "^2.0.1",
"@vitejs/plugin-vue-jsx": "^1.3.1", "@vitejs/plugin-vue-jsx": "^1.3.3",
"@vue/compiler-sfc": "^3.2.24",
"@vue/eslint-config-prettier": "6.0.0", "@vue/eslint-config-prettier": "6.0.0",
"@vue/eslint-config-typescript": "7.0.0", "@vue/eslint-config-typescript": "7.0.0",
"@zougt/vite-plugin-theme-preprocessor": "^1.4.0", "@zougt/vite-plugin-theme-preprocessor": "^1.4.4",
"autoprefixer": "10.2.4", "autoprefixer": "10.2.4",
"babel-plugin-transform-remove-console": "6.9.4",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "7.30.0", "eslint": "7.30.0",
"eslint-plugin-prettier": "3.4.0", "eslint-plugin-prettier": "3.4.0",
@@ -109,13 +109,17 @@
"stylelint-config-prettier": "8.0.2", "stylelint-config-prettier": "8.0.2",
"stylelint-config-standard": "22.0.0", "stylelint-config-standard": "22.0.0",
"stylelint-order": "4.1.0", "stylelint-order": "4.1.0",
"typescript": "4.4.2", "typescript": "^4.5.5",
"unplugin-element-plus": "^0.1.3", "unplugin-element-plus": "^0.2.0",
"vite": "2.6.14", "vite": "^2.7.13",
"vite-plugin-live-reload": "^2.1.0",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-style-import": "^1.2.1", "vite-plugin-remove-console": "^0.0.6",
"vite-svg-loader": "^2.2.0", "vite-plugin-style-import": "^1.4.1",
"vue-eslint-parser": "7.10.0" "vite-plugin-windicss": "^1.6.1",
"vite-svg-loader": "2.2.0",
"vue-eslint-parser": "7.10.0",
"windicss": "^3.4.3"
}, },
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git", "repository": "git@github.com:xiaoxian521/vue-pure-admin.git",
"author": "xiaoxian521", "author": "xiaoxian521",

1855
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{ {
"Version": "2.8.0", "Version": "2.8.5",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,

View File

@@ -5,10 +5,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from "vue";
import { ElConfigProvider } from "element-plus"; import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn"; import zhCn from "element-plus/lib/locale/lang/zh-cn";
import en from "element-plus/lib/locale/lang/en"; import en from "element-plus/lib/locale/lang/en";
export default { export default defineComponent({
name: "app", name: "app",
components: { components: {
[ElConfigProvider.name]: ElConfigProvider [ElConfigProvider.name]: ElConfigProvider
@@ -18,5 +19,5 @@ export default {
return this.$storage.locale?.locale === "zh" ? zhCn : en; return this.$storage.locale?.locale === "zh" ? zhCn : en;
} }
} }
}; });
</script> </script>

View File

@@ -13,21 +13,27 @@ const lists = ref([
<el-descriptions class="margin-top" direction="vertical" :column="3" border> <el-descriptions class="margin-top" direction="vertical" :column="3" border>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><user /></el-icon> <el-icon>
<IconifyIconOffline icon="user" />
</el-icon>
用户名 用户名
</template> </template>
xiaoxian xiaoxian
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><iphone /></el-icon> <el-icon>
<IconifyIconOffline icon="iphone" />
</el-icon>
手机号 手机号
</template> </template>
123456789 123456789
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><location /></el-icon> <el-icon>
<IconifyIconOffline icon="location" />
</el-icon>
居住地 居住地
</template> </template>
上海 上海
@@ -36,7 +42,9 @@ const lists = ref([
<el-descriptions class="margin-top" direction="vertical" :column="2" border> <el-descriptions class="margin-top" direction="vertical" :column="2" border>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><tickets /></el-icon> <el-icon>
<IconifyIconOffline icon="tickets" />
</el-icon>
标签 标签
</template> </template>
<el-tag <el-tag
@@ -51,7 +59,9 @@ const lists = ref([
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><office-building /></el-icon> <el-icon>
<IconifyIconOffline icon="office-building" />
</el-icon>
联系地址 联系地址
</template> </template>
上海市徐汇区 上海市徐汇区
@@ -60,7 +70,9 @@ const lists = ref([
<el-descriptions class="margin-top" direction="vertical" :column="1" border> <el-descriptions class="margin-top" direction="vertical" :column="1" border>
<el-descriptions-item> <el-descriptions-item>
<template #label> <template #label>
<el-icon><notebook /></el-icon> <el-icon>
<IconifyIconOffline icon="notebook" />
</el-icon>
留言 留言
</template> </template>
好好学习天天向上 好好学习天天向上

View File

@@ -1,7 +1,8 @@
import { App, defineComponent } from "vue"; import { App, defineComponent } from "vue";
import icon from "./src/Icon.vue"; import icon from "./src/Icon.vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { iconComponents } from "/@/plugins/element-plus"; import iconifyIconOffline from "./src/iconifyIconOffline";
import iconifyIconOnline from "./src/iconifyIconOnline";
/** /**
* find icon component * find icon component
@@ -67,21 +68,20 @@ export function findIcon(icon: String, type = "EL", property?: string) {
}); });
} else if (type === "RI") { } else if (type === "RI") {
return defineComponent({ return defineComponent({
name: "RIIcon", name: "RiIcon",
data() { data() {
return { icon: `ri-${icon}` }; return { icon: `ri-${icon}` };
}, },
template: `<i :class="icon" />` template: `<i :class="icon" />`
}); });
} else if (type === "EL") { } else if (type === "EL") {
const components = iconComponents.filter( return defineComponent({
component => component.name === icon name: "ElIcon",
); data() {
if (components.length > 0) { return { icon };
return components[0]; },
} else { template: `<IconifyIconOffline :icon="icon" />`
return null; });
}
} else if (type === "SVG") { } else if (type === "SVG") {
return icon; return icon;
} }
@@ -93,6 +93,11 @@ export const Icon = Object.assign(icon, {
} }
}); });
export const IconifyIconOffline = iconifyIconOffline;
export const IconifyIconOnline = iconifyIconOnline;
export default { export default {
Icon Icon,
IconifyIconOffline,
IconifyIconOnline
}; };

View File

@@ -0,0 +1,70 @@
import { h, defineComponent } from "vue";
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
import Check from "@iconify-icons/ep/check";
import Menu from "@iconify-icons/ep/menu";
import HomeFilled from "@iconify-icons/ep/home-filled";
import SetUp from "@iconify-icons/ep/set-up";
import Edit from "@iconify-icons/ep/edit";
import Setting from "@iconify-icons/ep/setting";
import Lollipop from "@iconify-icons/ep/lollipop";
import Link from "@iconify-icons/ep/link";
import Position from "@iconify-icons/ep/position";
import Histogram from "@iconify-icons/ep/histogram";
import RefreshRight from "@iconify-icons/ep/refresh-right";
import ArrowDown from "@iconify-icons/ep/arrow-down";
import Close from "@iconify-icons/ep/close";
import CloseBold from "@iconify-icons/ep/close-bold";
import Bell from "@iconify-icons/ep/bell";
import Guide from "@iconify-icons/ep/guide";
import User from "@iconify-icons/ep/user";
import Iphone from "@iconify-icons/ep/iphone";
import Location from "@iconify-icons/ep/location";
import Tickets from "@iconify-icons/ep/tickets";
import OfficeBuilding from "@iconify-icons/ep/office-building";
import Notebook from "@iconify-icons/ep/notebook";
addIcon("check", Check);
addIcon("menu", Menu);
addIcon("home-filled", HomeFilled);
addIcon("set-up", SetUp);
addIcon("edit", Edit);
addIcon("setting", Setting);
addIcon("lollipop", Lollipop);
addIcon("link", Link);
addIcon("position", Position);
addIcon("histogram", Histogram);
addIcon("refresh-right", RefreshRight);
addIcon("arrow-down", ArrowDown);
addIcon("close", Close);
addIcon("close-bold", CloseBold);
addIcon("bell", Bell);
addIcon("guide", Guide);
addIcon("user", User);
addIcon("iphone", Iphone);
addIcon("location", Location);
addIcon("tickets", Tickets);
addIcon("office-building", OfficeBuilding);
addIcon("notebook", Notebook);
// Iconify Icon在Vue里离线使用用于内网环境
// https://docs.iconify.design/icon-components/vue/offline.html
export default defineComponent({
name: "IconifyIcon",
components: { IconifyIcon },
props: {
icon: {
type: String,
default: ""
}
},
render() {
return h(
IconifyIcon,
{
icon: `${this.icon}`
},
{
default: () => []
}
);
}
});

View File

@@ -0,0 +1,30 @@
import { h, defineComponent } from "vue";
import { Icon as IconifyIcon } from "@iconify/vue";
// Iconify Icon在Vue里在线使用用于外网环境
// https://docs.iconify.design/icon-components/vue/offline.html
export default defineComponent({
name: "IconifyIcon",
components: { IconifyIcon },
props: {
icon: {
type: String,
default: ""
},
type: {
type: String,
default: "ep:"
}
},
render() {
return h(
IconifyIcon,
{
icon: `${this.type}${this.icon}`
},
{
default: () => []
}
);
}
});

View File

@@ -8,7 +8,7 @@ import {
getCurrentInstance getCurrentInstance
} from "vue"; } from "vue";
import { RouterView } from "vue-router"; import { RouterView } from "vue-router";
import backTop from "/@/assets/svg/back_top.svg"; import backTop from "/@/assets/svg/back_top.svg?component";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
const props = defineProps({ const props = defineProps({

View File

@@ -13,7 +13,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
import { unref, watch, getCurrentInstance } from "vue"; import { unref, watch, getCurrentInstance } from "vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import screenfull from "../components/screenfull/index.vue"; import screenfull from "../components/screenfull/index.vue";
import globalization from "/@/assets/svg/globalization.svg"; import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import globalization from "/@/assets/svg/globalization.svg?component";
const instance = const instance =
getCurrentInstance().appContext.config.globalProperties.$storage; getCurrentInstance().appContext.config.globalProperties.$storage;
@@ -26,7 +27,7 @@ const { locale } = useI18n();
const getDropdownItemStyle = computed(() => { const getDropdownItemStyle = computed(() => {
return t => { return t => {
return { return {
background: locale.value === t ? "#1b2a47" : "", background: locale.value === t ? useEpThemeStoreHook().epThemeColor : "",
color: locale.value === t ? "#f4f4f5" : "#000" color: locale.value === t ? "#f4f4f5" : "#000"
}; };
}; };
@@ -94,15 +95,17 @@ function translationEn() {
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('zh')" :style="getDropdownItemStyle('zh')"
@click="translationCh" @click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'" ><IconifyIconOffline
><check /></el-icon class="check-zh"
>简体中文</el-dropdown-item v-show="locale === 'zh'"
icon="check"
/>简体中文</el-dropdown-item
> >
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('en')" :style="getDropdownItemStyle('en')"
@click="translationEn" @click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'" ><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>English</el-dropdown-item >English</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
@@ -128,7 +131,7 @@ function translationEn() {
:title="$t('buttons.hssystemSet')" :title="$t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
<Setting /> <IconifyIconOffline icon="setting" />
</el-icon> </el-icon>
</div> </div>
</div> </div>
@@ -149,10 +152,6 @@ function translationEn() {
cursor: pointer; cursor: pointer;
transition: background 0.3s; transition: background 0.3s;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
} }
.vertical-header-right { .vertical-header-right {

View File

@@ -16,7 +16,9 @@ notices.value.forEach(notice => {
<el-dropdown trigger="click" placement="bottom-end"> <el-dropdown trigger="click" placement="bottom-end">
<span class="dropdown-badge"> <span class="dropdown-badge">
<el-badge :value="noticesNum" :max="99"> <el-badge :value="noticesNum" :max="99">
<el-icon class="header-notice-icon"><bell /></el-icon> <el-icon class="header-notice-icon"
><IconifyIconOffline icon="bell"
/></el-icon>
</el-badge> </el-badge>
</span> </span>
<template #dropdown> <template #dropdown>

View File

@@ -23,7 +23,7 @@ emitter.on("openPanel", () => {
<div class="project-configuration"> <div class="project-configuration">
<h3>项目配置</h3> <h3>项目配置</h3>
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show"> <el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
<Close /> <IconifyIconOffline icon="close" />
</el-icon> </el-icon>
</div> </div>
<div style="border-bottom: 1px solid #dcdfe6"></div> <div style="border-bottom: 1px solid #dcdfe6"></div>

View File

@@ -16,9 +16,7 @@ import { useRouter } from "vue-router";
import panel from "../panel/index.vue"; import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import dayIcon from "/@/assets/svg/day.svg";
import { debounce } from "/@/utils/debounce"; import { debounce } from "/@/utils/debounce";
import darkIcon from "/@/assets/svg/dark.svg";
import { themeColorsType } from "../../types"; import { themeColorsType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { shadeBgColor } from "../../theme/element-plus"; import { shadeBgColor } from "../../theme/element-plus";
@@ -28,6 +26,9 @@ import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { createNewStyle, writeNewStyle } from "../../theme/element-plus"; import { createNewStyle, writeNewStyle } from "../../theme/element-plus";
import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"; import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
import dayIcon from "/@/assets/svg/day.svg?component";
import darkIcon from "/@/assets/svg/dark.svg?component";
const router = useRouter(); const router = useRouter();
const { isSelect } = useCssModule(); const { isSelect } = useCssModule();
const body = document.documentElement as HTMLElement; const body = document.documentElement as HTMLElement;
@@ -155,7 +156,7 @@ function onReset() {
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true,
showLink: true showLink: true
} }
@@ -351,7 +352,7 @@ nextTick(() => {
:size="17" :size="17"
:color="getThemeColor(item.themeColor)" :color="getThemeColor(item.themeColor)"
> >
<Check /> <IconifyIconOffline icon="check" />
</el-icon> </el-icon>
</li> </li>
</ul> </ul>
@@ -444,7 +445,7 @@ nextTick(() => {
<style scoped module> <style scoped module>
.isSelect { .isSelect {
border: 2px solid #0960bd; border: 2px solid var(--el-color-primary);
} }
</style> </style>

View File

@@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
export interface Props { export interface Props {
isActive: boolean; isActive: boolean;
} }
@@ -7,6 +9,8 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false isActive: false
}); });
const fillColor = ref<string>("");
const emit = defineEmits<{ const emit = defineEmits<{
(e: "toggleClick"): void; (e: "toggleClick"): void;
}>(); }>();
@@ -21,8 +25,11 @@ const toggleClick = () => {
:class="classes.container" :class="classes.container"
:title="props.isActive ? '点击折叠' : '点击展开'" :title="props.isActive ? '点击折叠' : '点击展开'"
@click="toggleClick" @click="toggleClick"
@mouseenter="fillColor = useEpThemeStoreHook().epThemeColor"
@mouseleave="fillColor = ''"
> >
<svg <svg
:fill="fillColor"
:class="['hamburger', props.isActive ? 'is-active' : '']" :class="['hamburger', props.isActive ? 'is-active' : '']"
viewBox="0 0 1024 1024" viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -19,8 +19,8 @@ import { useRoute, useRouter } from "vue-router";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import Icon from "/@/components/ReIcon/src/Icon.vue"; import Icon from "/@/components/ReIcon/src/Icon.vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import globalization from "/@/assets/svg/globalization.svg";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import globalization from "/@/assets/svg/globalization.svg?component";
const instance = const instance =
getCurrentInstance().appContext.config.globalProperties.$storage; getCurrentInstance().appContext.config.globalProperties.$storage;
@@ -161,14 +161,14 @@ onMounted(() => {
:style="getDropdownItemStyle('zh')" :style="getDropdownItemStyle('zh')"
@click="translationCh" @click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'" ><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>简体中文</el-dropdown-item >简体中文</el-dropdown-item
> >
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('en')" :style="getDropdownItemStyle('en')"
@click="translationEn" @click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'" ><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>English</el-dropdown-item >English</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
@@ -194,7 +194,7 @@ onMounted(() => {
:title="$t('buttons.hssystemSet')" :title="$t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
<Setting /> <IconifyIconOffline icon="setting" />
</el-icon> </el-icon>
</div> </div>
</div> </div>

View File

@@ -3,6 +3,7 @@ import {
ref, ref,
watch, watch,
unref, unref,
reactive,
nextTick, nextTick,
computed, computed,
ComputedRef, ComputedRef,
@@ -11,15 +12,15 @@ import {
getCurrentInstance getCurrentInstance
} from "vue"; } from "vue";
import close from "/@/assets/svg/close.svg"; import close from "/@/assets/svg/close.svg?component";
import refresh from "/@/assets/svg/refresh.svg"; import refresh from "/@/assets/svg/refresh.svg?component";
import closeAll from "/@/assets/svg/close_all.svg"; import closeAll from "/@/assets/svg/close_all.svg?component";
import closeLeft from "/@/assets/svg/close_left.svg"; import closeLeft from "/@/assets/svg/close_left.svg?component";
import closeOther from "/@/assets/svg/close_other.svg"; import closeOther from "/@/assets/svg/close_other.svg?component";
import closeRight from "/@/assets/svg/close_right.svg"; import closeRight from "/@/assets/svg/close_right.svg?component";
import { $t as t } from "/@/plugins/i18n";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { $t as t } from "/@/plugins/i18n";
import { isEqual, isEmpty } from "lodash-es"; import { isEqual, isEmpty } from "lodash-es";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
@@ -30,7 +31,6 @@ import { handleAliveRoute, delAliveRoutes } from "/@/router/utils";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import { toggleClass, removeClass, hasClass } from "/@/utils/operate"; import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core"; import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core";
const route = useRoute(); const route = useRoute();
@@ -128,7 +128,7 @@ const moveToView = (index: number): void => {
if (!instance.refs["dynamic" + index]) { if (!instance.refs["dynamic" + index]) {
return; return;
} }
const tabItemEl = instance.refs["dynamic" + index]; const tabItemEl = instance.refs["dynamic" + index][0];
const tabItemElOffsetLeft = (tabItemEl as HTMLElement).offsetLeft; const tabItemElOffsetLeft = (tabItemEl as HTMLElement).offsetLeft;
const tabItemOffsetWidth = (tabItemEl as HTMLElement).offsetWidth; const tabItemOffsetWidth = (tabItemEl as HTMLElement).offsetWidth;
// 标签页导航栏可视长度(不包含溢出部分) // 标签页导航栏可视长度(不包含溢出部分)
@@ -186,7 +186,7 @@ const handleScroll = (offset: number): void => {
} }
}; };
const tagsViews = ref<Array<tagsViewsType>>([ const tagsViews = reactive<Array<tagsViewsType>>([
{ {
icon: refresh, icon: refresh,
text: t("buttons.hsreload"), text: t("buttons.hsreload"),
@@ -435,13 +435,13 @@ function closeMenu() {
function showMenus(value: boolean) { function showMenus(value: boolean) {
Array.of(1, 2, 3, 4, 5).forEach(v => { Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews.value[v].show = value; tagsViews[v].show = value;
}); });
} }
function disabledMenus(value: boolean) { function disabledMenus(value: boolean) {
Array.of(1, 2, 3, 4, 5).forEach(v => { Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews.value[v].disabled = value; tagsViews[v].disabled = value;
}); });
} }
@@ -463,7 +463,7 @@ function showMenuModel(
showMenus(true); showMenus(true);
if (refresh) { if (refresh) {
tagsViews.value[0].show = true; tagsViews[0].show = true;
} }
/** /**
@@ -473,25 +473,25 @@ function showMenuModel(
if (currentIndex === 1 && routeLength !== 2) { if (currentIndex === 1 && routeLength !== 2) {
// 左侧的菜单是首页,右侧存在别的菜单 // 左侧的菜单是首页,右侧存在别的菜单
tagsViews.value[2].show = false; tagsViews[2].show = false;
Array.of(1, 3, 4, 5).forEach(v => { Array.of(1, 3, 4, 5).forEach(v => {
tagsViews.value[v].disabled = false; tagsViews[v].disabled = false;
}); });
tagsViews.value[2].disabled = true; tagsViews[2].disabled = true;
} else if (currentIndex === 1 && routeLength === 2) { } else if (currentIndex === 1 && routeLength === 2) {
disabledMenus(false); disabledMenus(false);
// 左侧的菜单是首页,右侧不存在别的菜单 // 左侧的菜单是首页,右侧不存在别的菜单
Array.of(2, 3, 4).forEach(v => { Array.of(2, 3, 4).forEach(v => {
tagsViews.value[v].show = false; tagsViews[v].show = false;
tagsViews.value[v].disabled = true; tagsViews[v].disabled = true;
}); });
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) { } else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
// 当前路由是所有路由中的最后一个 // 当前路由是所有路由中的最后一个
tagsViews.value[3].show = false; tagsViews[3].show = false;
Array.of(1, 2, 4, 5).forEach(v => { Array.of(1, 2, 4, 5).forEach(v => {
tagsViews.value[v].disabled = false; tagsViews[v].disabled = false;
}); });
tagsViews.value[3].disabled = true; tagsViews[3].disabled = true;
} else if (currentIndex === 0 || currentPath === "/redirect/welcome") { } else if (currentIndex === 0 || currentPath === "/redirect/welcome") {
// 当前路由为首页 // 当前路由为首页
disabledMenus(true); disabledMenus(true);
@@ -505,10 +505,10 @@ function openMenu(tag, e) {
if (tag.path === "/welcome") { if (tag.path === "/welcome") {
// 右键菜单为首页,只显示刷新 // 右键菜单为首页,只显示刷新
showMenus(false); showMenus(false);
tagsViews.value[0].show = true; tagsViews[0].show = true;
} else if (route.path !== tag.path) { } else if (route.path !== tag.path) {
// 右键菜单不匹配当前路由,隐藏刷新 // 右键菜单不匹配当前路由,隐藏刷新
tagsViews.value[0].show = false; tagsViews[0].show = false;
showMenuModel(tag.path, tag.query); showMenuModel(tag.path, tag.query);
} else if ( } else if (
// eslint-disable-next-line no-dupe-else-if // eslint-disable-next-line no-dupe-else-if
@@ -517,7 +517,7 @@ function openMenu(tag, e) {
) { ) {
showMenus(true); showMenus(true);
// 只有两个标签时不显示关闭其他标签页 // 只有两个标签时不显示关闭其他标签页
tagsViews.value[4].show = false; tagsViews[4].show = false;
} else if (route.path === tag.path) { } else if (route.path === tag.path) {
// 右键当前激活的菜单 // 右键当前激活的菜单
showMenuModel(tag.path, tag.query, true); showMenuModel(tag.path, tag.query, true);
@@ -552,30 +552,32 @@ function tagOnClick(item) {
} }
// 鼠标移入 // 鼠标移入
function onMouseenter(item, index) { function onMouseenter(index) {
if (index) activeIndex.value = index; if (index) activeIndex.value = index;
if (unref(showModel) === "smart") { if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index], "schedule-active")) return; if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
toggleClass(true, "schedule-in", instance.refs["schedule" + index]); return;
toggleClass(false, "schedule-out", instance.refs["schedule" + index]); toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
} else { } else {
if (hasClass(instance.refs["dynamic" + index], "card-active")) return; if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(true, "card-in", instance.refs["dynamic" + index]); toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(false, "card-out", instance.refs["dynamic" + index]); toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
} }
} }
// 鼠标移出 // 鼠标移出
function onMouseleave(item, index) { function onMouseleave(index) {
activeIndex.value = -1; activeIndex.value = -1;
if (unref(showModel) === "smart") { if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index], "schedule-active")) return; if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
toggleClass(false, "schedule-in", instance.refs["schedule" + index]); return;
toggleClass(true, "schedule-out", instance.refs["schedule" + index]); toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
} else { } else {
if (hasClass(instance.refs["dynamic" + index], "card-active")) return; if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(false, "card-in", instance.refs["dynamic" + index]); toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(true, "card-out", instance.refs["dynamic" + index]); toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
} }
} }
@@ -644,8 +646,8 @@ const getContextMenuStyle = computed((): CSSProperties => {
: '' : ''
]" ]"
@contextmenu.prevent="openMenu(item, $event)" @contextmenu.prevent="openMenu(item, $event)"
@mouseenter.prevent="onMouseenter(item, index)" @mouseenter.prevent="onMouseenter(index)"
@mouseleave.prevent="onMouseleave(item, index)" @mouseleave.prevent="onMouseleave(index)"
@click="tagOnClick(item)" @click="tagOnClick(item)"
> >
<router-link :to="item.path" <router-link :to="item.path"
@@ -659,7 +661,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
class="el-icon-close" class="el-icon-close"
@click.stop="deleteMenu(item)" @click.stop="deleteMenu(item)"
> >
<CloseBold /> <IconifyIconOffline icon="close-bold" />
</el-icon> </el-icon>
<div <div
:ref="'schedule' + index" :ref="'schedule' + index"
@@ -698,13 +700,13 @@ const getContextMenuStyle = computed((): CSSProperties => {
class="el-icon-refresh-right rotate" class="el-icon-refresh-right rotate"
@click="onFresh" @click="onFresh"
> >
<RefreshRight /> <IconifyIconOffline icon="refresh-right" />
</el-icon> </el-icon>
</li> </li>
<li> <li>
<el-dropdown trigger="click" placement="bottom-end"> <el-dropdown trigger="click" placement="bottom-end">
<el-icon> <el-icon>
<ArrowDown /> <IconifyIconOffline icon="arrow-down" />
</el-icon> </el-icon>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>

View File

@@ -11,14 +11,15 @@ import { setType } from "./types";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { routerArrays } from "./types"; import { routerArrays } from "./types";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import backTop from "/@/assets/svg/back_top.svg";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import fullScreen from "/@/assets/svg/full_screen.svg";
import exitScreen from "/@/assets/svg/exit_screen.svg";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import { useMultiTagsStore } from "/@/store/modules/multiTags"; import { useMultiTagsStore } from "/@/store/modules/multiTags";
import { useSettingStoreHook } from "/@/store/modules/settings"; import { useSettingStoreHook } from "/@/store/modules/settings";
import backTop from "/@/assets/svg/back_top.svg?component";
import fullScreen from "/@/assets/svg/full_screen.svg?component";
import exitScreen from "/@/assets/svg/exit_screen.svg?component";
import navbar from "./components/navbar.vue"; import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue"; import tag from "./components/tag/index.vue";
import appMain from "./components/appMain.vue"; import appMain from "./components/appMain.vue";

View File

@@ -1,3 +1,4 @@
import { Component } from "vue";
export const routerArrays: Array<RouteConfigs> = [ export const routerArrays: Array<RouteConfigs> = [
{ {
path: "/welcome", path: "/welcome",
@@ -5,7 +6,7 @@ export const routerArrays: Array<RouteConfigs> = [
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
i18n: true, i18n: true,
icon: "HomeFilled", icon: "home-filled",
showLink: true showLink: true
} }
} }
@@ -32,7 +33,7 @@ export type multiTagsType = {
}; };
export type tagsViewsType = { export type tagsViewsType = {
icon: string; icon: Component;
text: string; text: string;
divided: boolean; divided: boolean;
disabled: boolean; disabled: boolean;

View File

@@ -11,6 +11,7 @@ import { useElementPlus } from "../src/plugins/element-plus";
import { injectResponsiveStorage } from "/@/utils/storage/responsive"; import { injectResponsiveStorage } from "/@/utils/storage/responsive";
import "animate.css"; import "animate.css";
import "virtual:windi.css";
// 导入公共样式 // 导入公共样式
import "./style/index.scss"; import "./style/index.scss";
// 导入字体图标 // 导入字体图标
@@ -26,6 +27,11 @@ Object.keys(directives).forEach(key => {
app.directive(key, (directives as { [key: string]: Directive })[key]); app.directive(key, (directives as { [key: string]: Directive })[key]);
}); });
// 全局注册`@iconify/vue`图标库
import { IconifyIconOffline, IconifyIconOnline } from "./components/ReIcon";
app.component("IconifyIconOffline", IconifyIconOffline);
app.component("IconifyIconOnline", IconifyIconOnline);
getServerConfig(app).then(async config => { getServerConfig(app).then(async config => {
injectResponsiveStorage(app, config); injectResponsiveStorage(app, config);
setupStore(app); setupStore(app);

View File

@@ -99,58 +99,6 @@ const components = [
ElTreeV2 ElTreeV2
]; ];
// https://element-plus.org/zh-CN/component/icon.html
import {
Check,
Menu,
HomeFilled,
SetUp,
Edit,
Setting,
Lollipop,
Link,
Position,
Histogram,
RefreshRight,
ArrowDown,
Close,
CloseBold,
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
} from "@element-plus/icons-vue";
// Icon
export const iconComponents = [
Check,
Menu,
HomeFilled,
SetUp,
Edit,
Setting,
Lollipop,
Link,
Position,
Histogram,
RefreshRight,
ArrowDown,
Close,
CloseBold,
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
];
export function useElementPlus(app: App) { export function useElementPlus(app: App) {
// 注册组件 // 注册组件
components.forEach((component: Component) => { components.forEach((component: Component) => {
@@ -158,10 +106,7 @@ export function useElementPlus(app: App) {
}); });
// 注册指令 // 注册指令
plugins.forEach(plugin => { plugins.forEach(plugin => {
// @ts-ignore
app.use(plugin); app.use(plugin);
}); });
// 注册图标
iconComponents.forEach((component: Component) => {
app.component(component.name, component);
});
} }

View File

@@ -7,7 +7,7 @@ const componentsRouter = {
component: Layout, component: Layout,
redirect: "/components/video", redirect: "/components/video",
meta: { meta: {
icon: "Menu", icon: "menu",
title: $t("menus.hscomponents"), title: $t("menus.hscomponents"),
i18n: true, i18n: true,
showLink: true, showLink: true,

View File

@@ -7,7 +7,7 @@ const editorRouter = {
component: Layout, component: Layout,
redirect: "/editor/index", redirect: "/editor/index",
meta: { meta: {
icon: "Edit", icon: "edit",
title: $t("menus.hseditor"), title: $t("menus.hseditor"),
i18n: true, i18n: true,
showLink: true, showLink: true,

View File

@@ -7,7 +7,7 @@ const errorRouter = {
component: Layout, component: Layout,
redirect: "/error/401", redirect: "/error/401",
meta: { meta: {
icon: "Position", icon: "position",
title: $t("menus.hserror"), title: $t("menus.hserror"),
showLink: true, showLink: true,
i18n: true, i18n: true,

View File

@@ -6,7 +6,7 @@ const externalLink = {
name: "external", name: "external",
component: Layout, component: Layout,
meta: { meta: {
icon: "Link", icon: "link",
title: $t("menus.externalLink"), title: $t("menus.externalLink"),
showLink: true, showLink: true,
i18n: true, i18n: true,

View File

@@ -7,7 +7,7 @@ const flowChartRouter = {
component: Layout, component: Layout,
redirect: "/flowChart/index", redirect: "/flowChart/index",
meta: { meta: {
icon: "SetUp", icon: "set-up",
title: $t("menus.hsflowChart"), title: $t("menus.hsflowChart"),
showLink: true, showLink: true,
i18n: true, i18n: true,

View File

@@ -7,7 +7,7 @@ const guideRouter = {
component: Layout, component: Layout,
redirect: "/guide/index", redirect: "/guide/index",
meta: { meta: {
icon: "Guide", icon: "guide",
title: $t("menus.hsguide"), title: $t("menus.hsguide"),
i18n: true, i18n: true,
showLink: true, showLink: true,

View File

@@ -7,7 +7,7 @@ const homeRouter = {
component: Layout, component: Layout,
redirect: "/welcome", redirect: "/welcome",
meta: { meta: {
icon: "HomeFilled", icon: "home-filled",
title: $t("menus.hshome"), title: $t("menus.hshome"),
showLink: true, showLink: true,
i18n: true, i18n: true,

View File

@@ -8,7 +8,7 @@ const nestedRouter = {
name: "Nested", name: "Nested",
meta: { meta: {
title: $t("menus.hsmenus"), title: $t("menus.hsmenus"),
icon: "Histogram", icon: "histogram",
showLink: true, showLink: true,
i18n: true, i18n: true,
rank: 5 rank: 5

View File

@@ -18,7 +18,7 @@ const remainingRouter = [
name: "redirect", name: "redirect",
component: Layout, component: Layout,
meta: { meta: {
icon: "HomeFilled", icon: "home-filled",
title: $t("menus.hshome"), title: $t("menus.hshome"),
i18n: true, i18n: true,
showLink: false, showLink: false,

View File

@@ -19,16 +19,16 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
import { getAsyncRoutes } from "/@/api/routes"; import { getAsyncRoutes } from "/@/api/routes";
// 按照路由中meta下的rank等级升序来排序路由 // 按照路由中meta下的rank等级升序来排序路由
const ascending = (arr: any[]) => { function ascending(arr: any[]) {
return arr.sort( return arr.sort(
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => { (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
return a?.meta?.rank - b?.meta?.rank; return a?.meta?.rank - b?.meta?.rank;
} }
); );
}; }
// 过滤meta中showLink为false的路由 // 过滤meta中showLink为false的路由
const filterTree = (data: RouteComponent[]) => { function filterTree(data: RouteComponent[]) {
const newTree = data.filter( const newTree = data.filter(
(v: { meta: { showLink: boolean } }) => v.meta.showLink (v: { meta: { showLink: boolean } }) => v.meta.showLink
); );
@@ -36,20 +36,20 @@ const filterTree = (data: RouteComponent[]) => {
(v: { children }) => v.children && (v.children = filterTree(v.children)) (v: { children }) => v.children && (v.children = filterTree(v.children))
); );
return newTree; return newTree;
}; }
// 批量删除缓存路由(keepalive) // 批量删除缓存路由(keepalive)
const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => { function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) {
delAliveRouteList.forEach(route => { delAliveRouteList.forEach(route => {
usePermissionStoreHook().cacheOperate({ usePermissionStoreHook().cacheOperate({
mode: "delete", mode: "delete",
name: route?.name name: route?.name
}); });
}); });
}; }
// 通过path获取父级路径 // 通过path获取父级路径
const getParentPaths = (path: string, routes: RouteRecordRaw[]) => { function getParentPaths(path: string, routes: RouteRecordRaw[]) {
// 深度遍历查找 // 深度遍历查找
function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) { function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
for (let i = 0; i < routes.length; i++) { for (let i = 0; i < routes.length; i++) {
@@ -70,10 +70,10 @@ const getParentPaths = (path: string, routes: RouteRecordRaw[]) => {
} }
return dfs(routes, path, []); return dfs(routes, path, []);
}; }
// 查找对应path的路由信息 // 查找对应path的路由信息
const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => { function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
let res = routes.find((item: { path: string }) => item.path == path); let res = routes.find((item: { path: string }) => item.path == path);
if (res) { if (res) {
return res; return res;
@@ -91,20 +91,20 @@ const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => {
} }
return null; return null;
} }
}; }
// 重置路由 // 重置路由
const resetRouter = (): void => { function resetRouter(): void {
router.getRoutes().forEach(route => { router.getRoutes().forEach(route => {
const { name } = route; const { name } = route;
if (name) { if (name) {
router.hasRoute(name) && router.removeRoute(name); router.hasRoute(name) && router.removeRoute(name);
} }
}); });
}; }
// 初始化路由 // 初始化路由
const initRouter = (name: string) => { function initRouter(name: string) {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes({ name }).then(({ info }) => { getAsyncRoutes({ name }).then(({ info }) => {
if (info.length === 0) { if (info.length === 0) {
@@ -137,15 +137,15 @@ const initRouter = (name: string) => {
}); });
}); });
}); });
}; }
/** /**
* 将多级嵌套路由处理成一维数组 * 将多级嵌套路由处理成一维数组
* @param routesList 传入路由 * @param routesList 传入路由
* @returns 返回处理后的一维路由 * @returns 返回处理后的一维路由
*/ */
const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => { function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
if (routesList.length <= 0) return routesList; if (routesList.length === 0) return routesList;
for (let i = 0; i < routesList.length; i++) { for (let i = 0; i < routesList.length; i++) {
if (routesList[i].children) { if (routesList[i].children) {
routesList = routesList routesList = routesList
@@ -154,7 +154,7 @@ const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
} }
} }
return routesList; return routesList;
}; }
/** /**
* 一维数组处理成多级嵌套数组三级及以上的路由全部拍成二级keep-alive 只支持到二级缓存) * 一维数组处理成多级嵌套数组三级及以上的路由全部拍成二级keep-alive 只支持到二级缓存)
@@ -162,8 +162,8 @@ const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
* @param routesList 处理后的一维路由菜单数组 * @param routesList 处理后的一维路由菜单数组
* @returns 返回将一维数组重新处理成规定路由的格式 * @returns 返回将一维数组重新处理成规定路由的格式
*/ */
const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => { function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
if (routesList.length <= 0) return routesList; if (routesList.length === 0) return routesList;
const newRoutesList: RouteRecordRaw[] = []; const newRoutesList: RouteRecordRaw[] = [];
routesList.forEach((v: RouteRecordRaw) => { routesList.forEach((v: RouteRecordRaw) => {
if (v.path === "/") { if (v.path === "/") {
@@ -180,10 +180,10 @@ const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => {
} }
}); });
return newRoutesList; return newRoutesList;
}; }
// 处理缓存路由(添加、删除、刷新) // 处理缓存路由(添加、删除、刷新)
const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => { function handleAliveRoute(matched: RouteRecordNormalized[], mode?: string) {
switch (mode) { switch (mode) {
case "add": case "add":
matched.forEach(v => { matched.forEach(v => {
@@ -207,10 +207,10 @@ const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
}); });
}, 100); }, 100);
} }
}; }
// 过滤后端传来的动态路由 重新生成规范路由 // 过滤后端传来的动态路由 重新生成规范路由
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => { function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
if (!arrRoutes || !arrRoutes.length) return; if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes); const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => { arrRoutes.forEach((v: RouteRecordRaw) => {
@@ -225,10 +225,10 @@ const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
} }
}); });
return arrRoutes; return arrRoutes;
}; }
// 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html // 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html
const getHistoryMode = (): RouterHistory => { function getHistoryMode(): RouterHistory {
const routerHistory = loadEnv().VITE_ROUTER_HISTORY; const routerHistory = loadEnv().VITE_ROUTER_HISTORY;
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
const historyMode = routerHistory.split(","); const historyMode = routerHistory.split(",");
@@ -249,10 +249,10 @@ const getHistoryMode = (): RouterHistory => {
return createWebHistory(rightMode); return createWebHistory(rightMode);
} }
} }
}; }
// 是否有权限 // 是否有权限
const hasPermissions = (value: Array<string>): boolean => { function hasPermissions(value: Array<string>): boolean {
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const roles = usePermissionStoreHook().buttonAuth; const roles = usePermissionStoreHook().buttonAuth;
const permissionRoles = value; const permissionRoles = value;
@@ -268,7 +268,7 @@ const hasPermissions = (value: Array<string>): boolean => {
} else { } else {
return false; return false;
} }
}; }
export { export {
ascending, ascending,

View File

@@ -16,7 +16,7 @@ export const useMultiTagsStore = defineStore({
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true,
showLink: true showLink: true
} }

View File

@@ -59,7 +59,7 @@ export const useUserStore = defineStore({
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true,
showLink: true showLink: true
} }

View File

@@ -47,7 +47,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
i18n: true, i18n: true,
icon: "HomeFilled", icon: "home-filled",
showLink: true showLink: true
} }
} }

View File

@@ -5,14 +5,14 @@ import { initRouter } from "/@/router/utils";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import { addClass, removeClass } from "/@/utils/operate"; import { addClass, removeClass } from "/@/utils/operate";
import bg from "/@/assets/login/bg.png"; import bg from "/@/assets/login/bg.png";
import avatar from "/@/assets/login/avatar.svg"; import avatar from "/@/assets/login/avatar.svg?component";
import illustration0 from "/@/assets/login/illustration0.svg"; import illustration0 from "/@/assets/login/illustration0.svg?component";
import illustration1 from "/@/assets/login/illustration1.svg"; import illustration1 from "/@/assets/login/illustration1.svg?component";
import illustration2 from "/@/assets/login/illustration2.svg"; import illustration2 from "/@/assets/login/illustration2.svg?component";
import illustration3 from "/@/assets/login/illustration3.svg"; import illustration3 from "/@/assets/login/illustration3.svg?component";
import illustration4 from "/@/assets/login/illustration4.svg"; import illustration4 from "/@/assets/login/illustration4.svg?component";
import illustration5 from "/@/assets/login/illustration5.svg"; import illustration5 from "/@/assets/login/illustration5.svg?component";
import illustration6 from "/@/assets/login/illustration6.svg"; import illustration6 from "/@/assets/login/illustration6.svg?component";
const router = useRouter(); const router = useRouter();

17
types/global.d.ts vendored
View File

@@ -6,16 +6,15 @@ import type {
PropType as VuePropType PropType as VuePropType
} from "vue"; } from "vue";
// GlobalComponents for Volar
declare module "vue" {
export interface GlobalComponents {
IconifyIconOffline: typeof import("../src/components/ReIcon")["IconifyIconOffline"];
IconifyIconOnline: typeof import("../src/components/ReIcon")["IconifyIconOnline"];
}
}
declare global { declare global {
const __APP_INFO__: {
pkg: {
name: string;
version: string;
dependencies: Recordable<string>;
devDependencies: Recordable<string>;
};
lastBuildTime: string;
};
interface Window { interface Window {
// Global vue app instance // Global vue app instance
__APP__: App<Element>; __APP__: App<Element>;

View File

@@ -3,10 +3,13 @@ import vue from "@vitejs/plugin-vue";
import svgLoader from "vite-svg-loader"; import svgLoader from "vite-svg-loader";
import legacy from "@vitejs/plugin-legacy"; import legacy from "@vitejs/plugin-legacy";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
import WindiCSS from "vite-plugin-windicss";
import { warpperEnv, regExps } from "./build"; import { warpperEnv, regExps } from "./build";
import liveReload from "vite-plugin-live-reload";
import { viteMockServe } from "vite-plugin-mock"; import { viteMockServe } from "vite-plugin-mock";
import styleImport from "vite-plugin-style-import"; import styleImport from "vite-plugin-style-import";
import ElementPlus from "unplugin-element-plus/vite"; import ElementPlus from "unplugin-element-plus/vite";
import removeConsole from "vite-plugin-remove-console";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite"; import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor"; import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
@@ -82,6 +85,11 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
vue(), vue(),
// jsx、tsx语法支持 // jsx、tsx语法支持
vueJsx(), vueJsx(),
WindiCSS(),
// 线上环境删除console
removeConsole(),
// 修改layout文件夹下的文件时自动重载浏览器 解决 https://github.com/xiaoxian521/vue-pure-admin/issues/170
liveReload(["src/layout/**/*"]),
// 自定义主题 // 自定义主题
themePreprocessorPlugin({ themePreprocessorPlugin({
scss: { scss: {
@@ -175,6 +183,10 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
], ],
optimizeDeps: { optimizeDeps: {
include: [ include: [
"pinia",
"vue-i18n",
"lodash-es",
"@vueuse/core",
"element-plus/lib/locale/lang/zh-cn", "element-plus/lib/locale/lang/zh-cn",
"element-plus/lib/locale/lang/en", "element-plus/lib/locale/lang/en",
"vxe-table/lib/locale/lang/zh-CN", "vxe-table/lib/locale/lang/zh-CN",

50
windi.config.ts Normal file
View File

@@ -0,0 +1,50 @@
// https://cn.windicss.org/ 中文文档
import { defineConfig } from "windicss/helpers";
import colors from "windicss/colors";
import typography from "windicss/plugin/typography";
export default defineConfig({
darkMode: "class",
attributify: true,
plugins: [typography()],
theme: {
extend: {
zIndex: {
"-1": "-1"
},
screens: {
sm: "576px",
md: "768px",
lg: "992px",
xl: "1200px",
"2xl": "1600px"
},
typography: {
DEFAULT: {
css: {
maxWidth: "65ch",
color: "inherit",
a: {
color: "inherit",
opacity: 0.75,
fontWeight: "500",
textDecoration: "underline",
"&:hover": {
opacity: 1,
color: colors.teal[600]
}
},
b: { color: "inherit" },
strong: { color: "inherit" },
em: { color: "inherit" },
h1: { color: "inherit" },
h2: { color: "inherit" },
h3: { color: "inherit" },
h4: { color: "inherit" },
code: { color: "inherit" }
}
}
}
}
}
});