mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-06 20:23:48 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cefe0c6d78 | ||
|
|
1187a29ca4 | ||
|
|
4cdfd7210e | ||
|
|
3d84d73e20 | ||
|
|
abbbd74e91 | ||
|
|
4c3512a13c | ||
|
|
7387cea6ce | ||
|
|
2e4610aec3 | ||
|
|
b7b949c160 | ||
|
|
7ee5bcb935 | ||
|
|
c239f2fa0f | ||
|
|
dcdba0d310 | ||
|
|
800e729c71 | ||
|
|
77f62626de | ||
|
|
d1fc70175e | ||
|
|
55f7e0b9d2 | ||
|
|
7960d9c6a2 | ||
|
|
6f88513a5f | ||
|
|
b9e48842ab | ||
|
|
135dc85fa8 | ||
|
|
87ac51726f | ||
|
|
7882cd8f22 | ||
|
|
5644268411 | ||
|
|
989453a644 | ||
|
|
18285821d9 | ||
|
|
9c3085367b | ||
|
|
52f5a76ca4 | ||
|
|
12c268d18d | ||
|
|
0c43078d32 | ||
|
|
c534577feb | ||
|
|
7474a19af2 | ||
|
|
ae1f81762f | ||
|
|
ffb3c85b83 | ||
|
|
ca1ea48dc6 | ||
|
|
5c6494931b | ||
|
|
e24431c38a | ||
|
|
f85a911b8e | ||
|
|
776fe7a944 | ||
|
|
ce099b3bea | ||
|
|
eff431a09d | ||
|
|
501a8955c3 | ||
|
|
f0a523f505 | ||
|
|
b2e33ca05f | ||
|
|
a401b1f5bf | ||
|
|
07a13b004e | ||
|
|
defd87869e | ||
|
|
051e6a99e1 | ||
|
|
5f3855cc82 | ||
|
|
6489e8b94a | ||
|
|
98ca00326a | ||
|
|
fc033c8693 | ||
|
|
57b18c2710 | ||
|
|
d667088c89 | ||
|
|
41b3cd7a8e | ||
|
|
8e9a1e5683 | ||
|
|
05939a99ee | ||
|
|
84e1581dcf | ||
|
|
4a2538cfe3 | ||
|
|
c091c685e8 | ||
|
|
613972f4a1 | ||
|
|
d860fe74c3 | ||
|
|
ffa8943267 | ||
|
|
babd6c9c2a | ||
|
|
7250872ac0 | ||
|
|
7f42710a55 | ||
|
|
fe6d81c08d | ||
|
|
1842f10172 | ||
|
|
5bbde8d102 | ||
|
|
8771b33834 | ||
|
|
842519768e | ||
|
|
25458835e9 | ||
|
|
abc0ac8e4c | ||
|
|
0e222b986d | ||
|
|
925ee00287 | ||
|
|
d9291175b9 | ||
|
|
c2e9b16917 | ||
|
|
c0881951d3 | ||
|
|
f0c6ddd221 | ||
|
|
07b9e31b22 | ||
|
|
a0708253af | ||
|
|
a1e1f2e4db | ||
|
|
b93c86aa44 | ||
|
|
49e78d5c20 | ||
|
|
33ea08fa4b | ||
|
|
0e89630b54 | ||
|
|
d6d67d357d | ||
|
|
848e107388 | ||
|
|
cc54fc07e4 | ||
|
|
5a75e566e7 |
+1
-1
@@ -23,7 +23,7 @@ module.exports = {
|
||||
{ value: 'fix', name: 'fix: 🐛 修复缺陷 | A bug fix', emoji: ':bug:' },
|
||||
{ value: 'docs', name: 'docs: 📝 文档更新 | Documentation only changes', emoji: ':memo:' },
|
||||
{ value: 'style', name: 'style: 💄 代码格式 | Changes that do not affect the meaning of the code', emoji: ':lipstick:' },
|
||||
{ value: 'refactor', name: 'refactor: ♻️ 代码重构 | A code change that neither fixes a bug nor adds a feature', emoji: ':recycle:' },
|
||||
{ value: 'refactor', name: 'refactor: ♻️ 代码重构 | A code change that neither fixes a bug nor adds a feature', emoji: ':recycle:' },
|
||||
{ value: 'perf', name: 'perf: ⚡️ 性能提升 | A code change that improves performance', emoji: ':zap:' },
|
||||
{ value: 'test', name: 'test: ✅ 测试相关 | Adding missing tests or correcting existing tests', emoji: ':white_check_mark:' },
|
||||
{ value: 'build', name: 'build: 📦️ 构建相关 | Changes that affect the build system or external dependencies', emoji: ':package:' },
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
NODE_ENV = production
|
||||
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 页面标题
|
||||
# 接口请求地址,会设置到 axios 的 baseURL 参数上
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
NODE_ENV = production
|
||||
|
||||
# 页面标题
|
||||
VITE_APP_TITLE = 页面标题(test)
|
||||
# 接口请求地址,会设置到 axios 的 baseURL 参数上
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
dist/
|
||||
node_modules/
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"extends": "@antfu"
|
||||
"extends": "@antfu",
|
||||
"rules": {
|
||||
"curly": ["error", "all"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,69 @@ jobs:
|
||||
|
||||
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
upload-archive:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get Release
|
||||
id: last_release
|
||||
uses: joutvhu/get-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
latest: true
|
||||
|
||||
- name: Create Archive
|
||||
uses: thedoctor0/zip-release@main
|
||||
with:
|
||||
type: 'zip'
|
||||
filename: fantastic-admin.${{ steps.last_release.outputs.tag_name }}.zip
|
||||
exclusions: '/.git/* /.github/*'
|
||||
|
||||
- name: Upload Archive To Release
|
||||
uses: xresloader/upload-to-github-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
release_id: ${{ steps.last_release.outputs.id }}
|
||||
draft: false
|
||||
file: fantastic-admin.${{ steps.last_release.outputs.tag_name }}.zip
|
||||
|
||||
upload-archive-example:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: example
|
||||
|
||||
- name: Get Release
|
||||
id: last_release
|
||||
uses: joutvhu/get-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
latest: true
|
||||
|
||||
- name: Create Archive
|
||||
uses: thedoctor0/zip-release@main
|
||||
with:
|
||||
type: 'zip'
|
||||
filename: fantastic-admin.example.${{ steps.last_release.outputs.tag_name }}.zip
|
||||
exclusions: '/.git/* /.github/*'
|
||||
|
||||
- name: Upload Archive To Release
|
||||
uses: xresloader/upload-to-github-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
release_id: ${{ steps.last_release.outputs.id }}
|
||||
draft: false
|
||||
file: fantastic-admin.example.${{ steps.last_release.outputs.tag_name }}.zip
|
||||
|
||||
@@ -3,5 +3,8 @@ node_modules
|
||||
dist*
|
||||
dist-ssr
|
||||
*.local
|
||||
.eslintcache
|
||||
.stylelintcache
|
||||
src/assets/sprites/*.*
|
||||
!src/assets/sprites/.gitkeep
|
||||
public/icons
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm lint-staged
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"src/**/*.{js,jsx,ts,tsx}": "eslint --fix",
|
||||
"src/**/*.{css,scss}": "stylelint --fix --allow-empty-input",
|
||||
"src/**/*.vue": ["eslint --fix", "stylelint --fix --allow-empty-input"]
|
||||
"src/**/*.{js,jsx,ts,tsx}": "eslint --cache --fix",
|
||||
"src/**/*.{css,scss}": "stylelint --cache --fix --allow-empty-input",
|
||||
"src/**/*.vue": ["eslint --cache --fix", "stylelint --cache --fix --allow-empty-input"]
|
||||
}
|
||||
|
||||
+4
-7
@@ -1,14 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-standard-scss",
|
||||
"stylelint-config-standard-vue/scss"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.scss"],
|
||||
"customSyntax": "postcss-scss"
|
||||
}
|
||||
],
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
@@ -18,6 +13,8 @@
|
||||
"property-no-unknown": null,
|
||||
"font-family-no-missing-generic-family-keyword": null,
|
||||
"selector-class-pattern": null,
|
||||
"max-line-length": null
|
||||
"max-line-length": null,
|
||||
"scss/double-slash-comment-empty-line-before": null,
|
||||
"scss/no-global-function-names": null
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-1
@@ -4,6 +4,7 @@
|
||||
"mikestead.dotenv",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"vue.volar"
|
||||
"Vue.volar",
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img src="https://hooray.gitee.io/fantastic-admin/logo.png" width="200" height="200" />
|
||||
<img src="https://hooray.gitee.io/fantastic-admin/logo.png" width="200" height="200" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Fantastic-admin</h1>
|
||||
@@ -7,18 +7,23 @@
|
||||
<p align="center">一款<b>开箱即用</b>的 Vue 中后台管理系统框架</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/" target="_blank">官网</a>
|
||||
( <a href="https://hooray.github.io/fantastic-admin" target="_blank">备用</a> )
|
||||
<span> | </span>
|
||||
演示
|
||||
( <a href="https://hooray.gitee.io/fantastic-admin/vue3/basic" target="_blank">基础版</a>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/vue3/pro" target="_blank">专业版</a> )
|
||||
<span> | </span>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/support.html" target="_blank">技术支持</a>
|
||||
<span> | </span>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/buy.html" target="_blank">购买</a>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin" target="_blank">官网</a>
|
||||
( <a href="https://hooray.github.io/fantastic-admin" target="_blank">备用</a> )
|
||||
<span> | </span>
|
||||
演示
|
||||
( <a href="https://hooray.gitee.io/fantastic-admin-example" target="_blank">基础版</a>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin-pro-example" target="_blank">专业版</a> )
|
||||
<span> | </span>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/support.html" target="_blank">技术支持</a>
|
||||
<span> | </span>
|
||||
<a href="https://hooray.gitee.io/fantastic-admin/buy.html" target="_blank">购买</a>
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<a href="###"><img src="https://img.shields.io/github/license/hooray/fantastic-admin?label=%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&style=flat-square" /></a>
|
||||
<a href="https://github.com/hooray/fantastic-admin/releases" target="_blank"><img src="https://img.shields.io/github/v/release/hooray/fantastic-admin?label=%E5%BD%93%E5%89%8D%E7%89%88%E6%9C%AC&style=flat-square" /></a>
|
||||
</p>
|
||||
|
||||
## 特点
|
||||
|
||||
- 丰富的布局与主题,覆盖市面上各种中后台应用场景,兼容PC、平板和移动端
|
||||
@@ -43,16 +48,16 @@
|
||||
> 预览截图为 Vue3 专业版
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview1.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview2.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview3.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview4.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview5.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview6.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview1.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview2.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview3.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview4.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview5.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/fantastic-admin/preview6.png" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 支持
|
||||
@@ -66,37 +71,37 @@
|
||||
## 生态
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
<a href="https://hooray.gitee.io/fantastic-startkit/" target="_blank">Fantastic-startkit</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
一款开箱即用的 Vue 项目启动套件
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
<a href="https://hooray.gitee.io/fantastic-startkit/" target="_blank">Fantastic-startkit</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
一款开箱即用的 Vue 项目启动套件
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
<a href="https://hooray.gitee.io/one-step-admin/" target="_blank">One-step-admin</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
一款干啥都快人一步的 Vue 中后台管理系统框架
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview1.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview2.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview3.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview4.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview5.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview6.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
<a href="https://hooray.gitee.io/one-step-admin/" target="_blank">One-step-admin</a>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
一款干啥都快人一步的 Vue 中后台管理系统框架
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview1.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview2.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview3.png" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview4.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview5.png" /></td>
|
||||
<td><img src="https://hooray.gitee.io/one-step-admin/preview6.png" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
+45
-40
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"version": "2.6.1",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:test": "vue-tsc --noEmit && vite build --mode test",
|
||||
@@ -8,77 +8,82 @@
|
||||
"serve": "http-server ./dist -o",
|
||||
"svgo": "svgo -f src/assets/icons",
|
||||
"new": "plop",
|
||||
"lint:eslint": "eslint src/**/*.{ts,tsx,vue} --fix",
|
||||
"lint:stylelint": "stylelint src/**/*.{css,scss,vue} --fix",
|
||||
"prepare": "husky install",
|
||||
"postinstall": "esno scripts/prepare.js",
|
||||
"lint": "npm-run-all -s lint:tsc lint:eslint lint:stylelint",
|
||||
"lint:tsc": "vue-tsc --noEmit",
|
||||
"lint:eslint": "eslint src/**/*.{ts,tsx,vue} --cache --fix",
|
||||
"lint:stylelint": "stylelint src/**/*.{css,scss,vue} --cache --fix",
|
||||
"postinstall": "simple-git-hooks && esno scripts/prepare.js",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"commit": "git cz",
|
||||
"release": "bumpp"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tinymce/tinymce-vue": "^5.0.0",
|
||||
"@vueuse/core": "^9.2.0",
|
||||
"@vueuse/integrations": "^9.2.0",
|
||||
"axios": "^0.27.2",
|
||||
"dayjs": "^1.11.5",
|
||||
"element-plus": "^2.2.17",
|
||||
"hotkeys-js": "^3.10.0",
|
||||
"@vueuse/core": "^9.10.0",
|
||||
"@vueuse/integrations": "^9.10.0",
|
||||
"axios": "^1.2.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"element-plus": "^2.2.28",
|
||||
"hotkeys-js": "^3.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"pinia": "^2.0.22",
|
||||
"pinia": "^2.0.28",
|
||||
"qs": "^6.11.0",
|
||||
"tinymce": "^6.2.0",
|
||||
"vue": "^3.2.39",
|
||||
"vue-router": "^4.1.5"
|
||||
"tinymce": "^6.3.1",
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.27.0",
|
||||
"@iconify-json/ep": "^1.1.7",
|
||||
"@iconify/vue": "^4.0.0",
|
||||
"@antfu/eslint-config": "^0.34.0",
|
||||
"@iconify-json/ep": "^1.1.8",
|
||||
"@iconify/vue": "^4.0.2",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@vitejs/plugin-vue": "^3.1.0",
|
||||
"@vitejs/plugin-vue-jsx": "^2.0.1",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"bumpp": "^8.2.1",
|
||||
"cz-git": "^1.3.11",
|
||||
"eslint": "^8.24.0",
|
||||
"cz-git": "^1.4.1",
|
||||
"eslint": "^8.31.0",
|
||||
"esno": "^0.16.3",
|
||||
"fs-extra": "^10.1.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"http-server": "^14.1.1",
|
||||
"husky": "^8.0.1",
|
||||
"lint-staged": "^13.0.3",
|
||||
"lint-staged": "^13.1.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"plop": "^3.1.1",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.5",
|
||||
"sass": "^1.55.0",
|
||||
"stylelint": "^14.12.1",
|
||||
"stylelint-config-standard": "^28.0.0",
|
||||
"stylelint-config-standard-scss": "^5.0.0",
|
||||
"sass": "^1.57.1",
|
||||
"simple-git-hooks": "^2.8.1",
|
||||
"stylelint": "^14.16.1",
|
||||
"stylelint-config-standard": "^29.0.0",
|
||||
"stylelint-config-standard-scss": "^6.1.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-scss": "^4.3.0",
|
||||
"svgo": "^2.8.0",
|
||||
"typescript": "^4.8.3",
|
||||
"unplugin-auto-import": "^0.11.2",
|
||||
"unplugin-vue-components": "^0.22.7",
|
||||
"vite": "^3.1.3",
|
||||
"vite-plugin-banner": "^0.5.0",
|
||||
"svgo": "^3.0.2",
|
||||
"typescript": "^4.9.4",
|
||||
"unplugin-auto-import": "^0.12.1",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^4.0.4",
|
||||
"vite-plugin-banner": "^0.7.0",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-pages": "^0.26.0",
|
||||
"vite-plugin-restart": "^0.2.0",
|
||||
"vite-plugin-pages": "^0.28.0",
|
||||
"vite-plugin-spritesmith": "^0.1.1",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-inspector": "^3.0.1",
|
||||
"vite-plugin-vue-layouts": "^0.6.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vue-tsc": "^0.40.13"
|
||||
"vue-tsc": "^1.0.24"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged",
|
||||
"preserveUnused": true
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts" setup{{#if isGlobal}} name="{{ properCase name }}"{{/if}}>
|
||||
// const proxy = useCurrentInstance()
|
||||
// 逻辑代码
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- 内容区 -->
|
||||
<!-- 布局 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// scss
|
||||
// 样式
|
||||
</style>
|
||||
|
||||
@@ -36,22 +36,23 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入组件名称',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '组件名称不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
actions: (data) => {
|
||||
let path = ''
|
||||
if (data.isGlobal)
|
||||
if (data.isGlobal) {
|
||||
path = 'src/components/{{properCase name}}/index.vue'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
path = `${data.path}/components/{{properCase name}}/index.vue`
|
||||
|
||||
}
|
||||
const actions = [
|
||||
{
|
||||
type: 'add',
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
<script lang="ts" setup name="{{ properCase componentName }}">
|
||||
// const proxy = useCurrentInstance()
|
||||
// const router = useRouter()
|
||||
// const route = useRoute()
|
||||
// 逻辑代码
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
页面内容区域
|
||||
<!-- 布局 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// scss
|
||||
// 样式
|
||||
</style>
|
||||
|
||||
@@ -28,11 +28,12 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入文件名',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '文件名不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,8 +4,8 @@ const use{{ properCase name }}Store = defineStore(
|
||||
{
|
||||
state: () => ({}),
|
||||
getters: {},
|
||||
actions: {}
|
||||
}
|
||||
actions: {},
|
||||
},
|
||||
)
|
||||
|
||||
export default use{{ properCase name }}Store
|
||||
|
||||
@@ -6,11 +6,12 @@ module.exports = {
|
||||
name: 'name',
|
||||
message: '请输入模块名称',
|
||||
validate: (v) => {
|
||||
if (!v || v.trim === '')
|
||||
if (!v || v.trim === '') {
|
||||
return '模块名称不能为空'
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -18,7 +19,7 @@ module.exports = {
|
||||
const actions = [
|
||||
{
|
||||
type: 'add',
|
||||
path: 'src/store/modules/{{camelCase name}}.js',
|
||||
path: 'src/store/modules/{{camelCase name}}.ts',
|
||||
templateFile: 'plop-templates/store/index.hbs',
|
||||
},
|
||||
]
|
||||
|
||||
Generated
+1807
-1172
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -186,6 +203,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -215,6 +253,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.3);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.3);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -506,10 +551,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -252,11 +252,24 @@ button::-moz-focus-inner {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 4px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
border-radius: 6px;
|
||||
bottom: -1px;
|
||||
box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #006ce7, 0 0 0 3px rgba(0, 108, 231, 0.25);
|
||||
content: '';
|
||||
left: -1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
.tox .tox-button[disabled] {
|
||||
background-color: #006ce7;
|
||||
background-image: none;
|
||||
@@ -272,6 +285,9 @@ button::-moz-focus-inner {
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
.tox .tox-button:focus-visible:not(:disabled)::before {
|
||||
opacity: 1;
|
||||
}
|
||||
.tox .tox-button:hover:not(:disabled) {
|
||||
background-color: #0060ce;
|
||||
background-image: none;
|
||||
@@ -933,8 +949,9 @@ button::-moz-focus-inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.tox .tox-comment__date {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.tox .tox-comment__body {
|
||||
color: #fff;
|
||||
@@ -1093,20 +1110,30 @@ button::-moz-focus-inner {
|
||||
.tox .tox-user__avatar svg {
|
||||
fill: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.tox .tox-user__avatar img {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
object-fit: cover;
|
||||
vertical-align: middle;
|
||||
width: 36px;
|
||||
}
|
||||
.tox .tox-user__name {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
line-height: 18px;
|
||||
text-transform: none;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg {
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg,
|
||||
.tox:not([dir=rtl]) .tox-user__avatar img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar svg {
|
||||
.tox[dir=rtl] .tox-user__avatar svg,
|
||||
.tox[dir=rtl] .tox-user__avatar img {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar + .tox-user__name {
|
||||
@@ -1151,10 +1178,11 @@ button::-moz-focus-inner {
|
||||
width: 95vw;
|
||||
z-index: 2;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog {
|
||||
align-self: flex-start;
|
||||
margin: 8px auto;
|
||||
max-height: calc(100vh - 8px * 2);
|
||||
width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
@@ -1210,7 +1238,7 @@ button::-moz-focus-inner {
|
||||
text-align: left;
|
||||
text-transform: none;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1221,7 +1249,7 @@ button::-moz-focus-inner {
|
||||
flex-direction: column;
|
||||
padding: 16px 16px;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {
|
||||
flex-direction: row;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@@ -1389,6 +1417,9 @@ button::-moz-focus-inner {
|
||||
padding-bottom: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {
|
||||
background: #fff;
|
||||
}
|
||||
.tox .tox-dialog__popups {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -1421,12 +1452,12 @@ button::-moz-focus-inner {
|
||||
.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -1438,12 +1469,12 @@ button::-moz-focus-inner {
|
||||
.tox[dir=rtl] .tox-dialog__body {
|
||||
text-align: right;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -1500,6 +1531,8 @@ body.tox-dialog__disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
.tox .tox-editor-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
z-index: 1;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header {
|
||||
@@ -1581,8 +1614,7 @@ body.tox-dialog__disable-scroll {
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-menu {
|
||||
border-color: #161f29;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-autocompleter-highlight {
|
||||
@@ -2073,6 +2105,12 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-menu.tox-collection.tox-collection--grid {
|
||||
padding: 8px;
|
||||
}
|
||||
@media only screen and (min-width: 768px ) {
|
||||
.tox .tox-menu .tox-collection__item-label {
|
||||
overflow-wrap: break-word;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
.tox .tox-menu__label h1,
|
||||
.tox .tox-menu__label h2,
|
||||
.tox .tox-menu__label h3,
|
||||
@@ -2085,16 +2123,45 @@ body.tox-dialog__disable-scroll {
|
||||
margin: 0;
|
||||
}
|
||||
.tox .tox-menubar {
|
||||
background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='transparent'/%3E%3C/svg%3E") left 0 top 0 #222F3E;
|
||||
background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;
|
||||
background-color: #222F3E;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
padding: 0 11px 0 12px;
|
||||
}
|
||||
.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid transparent;
|
||||
.tox .tox-promotion + .tox-menubar {
|
||||
grid-column: 1;
|
||||
}
|
||||
.tox .tox-promotion {
|
||||
background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;
|
||||
background-color: #222F3E;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
padding-inline-end: 8px;
|
||||
padding-inline-start: 4px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.tox .tox-promotion-link {
|
||||
align-items: unsafe center;
|
||||
background-color: #E8F1F8;
|
||||
border-radius: 5px;
|
||||
color: #086BE6;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 26.6px;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-promotion-link:hover {
|
||||
background-color: #B4D7FF;
|
||||
}
|
||||
.tox .tox-promotion-link:focus {
|
||||
background-color: #D9EDF7;
|
||||
}
|
||||
/* Deprecated. Remove in next major release */
|
||||
.tox .tox-mbtn {
|
||||
@@ -2874,10 +2941,14 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-toolbar__overflow--shrinking {
|
||||
transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;
|
||||
}
|
||||
.tox .tox-toolbar-overlord,
|
||||
.tox .tox-anchorbar {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.tox .tox-menubar + .tox-toolbar,
|
||||
.tox .tox-menubar + .tox-toolbar-overlord {
|
||||
border-top: 1px solid transparent;
|
||||
margin-top: 0px;
|
||||
margin-top: -1px;
|
||||
padding-bottom: 1px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -252,11 +252,24 @@ button::-moz-focus-inner {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 4px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
border-radius: 6px;
|
||||
bottom: -1px;
|
||||
box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #006ce7, 0 0 0 3px rgba(0, 108, 231, 0.25);
|
||||
content: '';
|
||||
left: -1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
.tox .tox-button[disabled] {
|
||||
background-color: #006ce7;
|
||||
background-image: none;
|
||||
@@ -272,6 +285,9 @@ button::-moz-focus-inner {
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
.tox .tox-button:focus-visible:not(:disabled)::before {
|
||||
opacity: 1;
|
||||
}
|
||||
.tox .tox-button:hover:not(:disabled) {
|
||||
background-color: #0060ce;
|
||||
background-image: none;
|
||||
@@ -933,8 +949,9 @@ button::-moz-focus-inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.tox .tox-comment__date {
|
||||
color: rgba(34, 47, 62, 0.7);
|
||||
color: #222f3e;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.tox .tox-comment__body {
|
||||
color: #222f3e;
|
||||
@@ -1093,20 +1110,30 @@ button::-moz-focus-inner {
|
||||
.tox .tox-user__avatar svg {
|
||||
fill: rgba(34, 47, 62, 0.7);
|
||||
}
|
||||
.tox .tox-user__avatar img {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
object-fit: cover;
|
||||
vertical-align: middle;
|
||||
width: 36px;
|
||||
}
|
||||
.tox .tox-user__name {
|
||||
color: rgba(34, 47, 62, 0.7);
|
||||
font-size: 12px;
|
||||
color: #222f3e;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
line-height: 18px;
|
||||
text-transform: none;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg {
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg,
|
||||
.tox:not([dir=rtl]) .tox-user__avatar img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar svg {
|
||||
.tox[dir=rtl] .tox-user__avatar svg,
|
||||
.tox[dir=rtl] .tox-user__avatar img {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar + .tox-user__name {
|
||||
@@ -1151,10 +1178,11 @@ button::-moz-focus-inner {
|
||||
width: 95vw;
|
||||
z-index: 2;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog {
|
||||
align-self: flex-start;
|
||||
margin: 8px auto;
|
||||
max-height: calc(100vh - 8px * 2);
|
||||
width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
@@ -1210,7 +1238,7 @@ button::-moz-focus-inner {
|
||||
text-align: left;
|
||||
text-transform: none;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1221,7 +1249,7 @@ button::-moz-focus-inner {
|
||||
flex-direction: column;
|
||||
padding: 16px 16px;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {
|
||||
flex-direction: row;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@@ -1389,6 +1417,9 @@ button::-moz-focus-inner {
|
||||
padding-bottom: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {
|
||||
background: #fff;
|
||||
}
|
||||
.tox .tox-dialog__popups {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -1421,12 +1452,12 @@ button::-moz-focus-inner {
|
||||
.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -1438,12 +1469,12 @@ button::-moz-focus-inner {
|
||||
.tox[dir=rtl] .tox-dialog__body {
|
||||
text-align: right;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -1500,6 +1531,8 @@ body.tox-dialog__disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
.tox .tox-editor-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
z-index: 1;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header {
|
||||
@@ -1581,8 +1614,7 @@ body.tox-dialog__disable-scroll {
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-menu {
|
||||
border-color: #eeeeee;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-autocompleter-highlight {
|
||||
@@ -2073,6 +2105,12 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-menu.tox-collection.tox-collection--grid {
|
||||
padding: 8px;
|
||||
}
|
||||
@media only screen and (min-width: 768px ) {
|
||||
.tox .tox-menu .tox-collection__item-label {
|
||||
overflow-wrap: break-word;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
.tox .tox-menu__label h1,
|
||||
.tox .tox-menu__label h2,
|
||||
.tox .tox-menu__label h3,
|
||||
@@ -2085,16 +2123,45 @@ body.tox-dialog__disable-scroll {
|
||||
margin: 0;
|
||||
}
|
||||
.tox .tox-menubar {
|
||||
background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='transparent'/%3E%3C/svg%3E") left 0 top 0 #fff;
|
||||
background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
padding: 0 11px 0 12px;
|
||||
}
|
||||
.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid transparent;
|
||||
.tox .tox-promotion + .tox-menubar {
|
||||
grid-column: 1;
|
||||
}
|
||||
.tox .tox-promotion {
|
||||
background: repeating-linear-gradient(transparent 0px 1px, transparent 1px 39px) center top 39px / 100% calc(100% - 39px) no-repeat;
|
||||
background-color: #fff;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
padding-inline-end: 8px;
|
||||
padding-inline-start: 4px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.tox .tox-promotion-link {
|
||||
align-items: unsafe center;
|
||||
background-color: #E8F1F8;
|
||||
border-radius: 5px;
|
||||
color: #086BE6;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 26.6px;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-promotion-link:hover {
|
||||
background-color: #B4D7FF;
|
||||
}
|
||||
.tox .tox-promotion-link:focus {
|
||||
background-color: #D9EDF7;
|
||||
}
|
||||
/* Deprecated. Remove in next major release */
|
||||
.tox .tox-mbtn {
|
||||
@@ -2874,10 +2941,14 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-toolbar__overflow--shrinking {
|
||||
transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;
|
||||
}
|
||||
.tox .tox-toolbar-overlord,
|
||||
.tox .tox-anchorbar {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.tox .tox-menubar + .tox-toolbar,
|
||||
.tox .tox-menubar + .tox-toolbar-overlord {
|
||||
border-top: 1px solid transparent;
|
||||
margin-top: 0px;
|
||||
margin-top: -1px;
|
||||
padding-bottom: 1px;
|
||||
padding-top: 1px;
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -186,6 +203,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -215,6 +253,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.3);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.3);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -506,10 +551,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -252,11 +252,24 @@ button::-moz-focus-inner {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 4px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
border-radius: 3px;
|
||||
bottom: -1px;
|
||||
box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #207ab7, 0 0 0 3px rgba(32, 122, 183, 0.25);
|
||||
content: '';
|
||||
left: -1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
.tox .tox-button[disabled] {
|
||||
background-color: #207ab7;
|
||||
background-image: none;
|
||||
@@ -272,6 +285,9 @@ button::-moz-focus-inner {
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
.tox .tox-button:focus-visible:not(:disabled)::before {
|
||||
opacity: 1;
|
||||
}
|
||||
.tox .tox-button:hover:not(:disabled) {
|
||||
background-color: #1c6ca1;
|
||||
background-image: none;
|
||||
@@ -933,8 +949,9 @@ button::-moz-focus-inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.tox .tox-comment__date {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.tox .tox-comment__body {
|
||||
color: #fff;
|
||||
@@ -1093,20 +1110,30 @@ button::-moz-focus-inner {
|
||||
.tox .tox-user__avatar svg {
|
||||
fill: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.tox .tox-user__avatar img {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
object-fit: cover;
|
||||
vertical-align: middle;
|
||||
width: 36px;
|
||||
}
|
||||
.tox .tox-user__name {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
line-height: 18px;
|
||||
text-transform: none;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg {
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg,
|
||||
.tox:not([dir=rtl]) .tox-user__avatar img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar svg {
|
||||
.tox[dir=rtl] .tox-user__avatar svg,
|
||||
.tox[dir=rtl] .tox-user__avatar img {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar + .tox-user__name {
|
||||
@@ -1151,10 +1178,11 @@ button::-moz-focus-inner {
|
||||
width: 95vw;
|
||||
z-index: 2;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog {
|
||||
align-self: flex-start;
|
||||
margin: 8px auto;
|
||||
max-height: calc(100vh - 8px * 2);
|
||||
width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
@@ -1210,7 +1238,7 @@ button::-moz-focus-inner {
|
||||
text-align: left;
|
||||
text-transform: none;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1221,7 +1249,7 @@ button::-moz-focus-inner {
|
||||
flex-direction: column;
|
||||
padding: 16px 16px;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {
|
||||
flex-direction: row;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@@ -1389,6 +1417,9 @@ button::-moz-focus-inner {
|
||||
padding-bottom: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {
|
||||
background: #fff;
|
||||
}
|
||||
.tox .tox-dialog__popups {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -1421,12 +1452,12 @@ button::-moz-focus-inner {
|
||||
.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -1438,12 +1469,12 @@ button::-moz-focus-inner {
|
||||
.tox[dir=rtl] .tox-dialog__body {
|
||||
text-align: right;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -1500,6 +1531,8 @@ body.tox-dialog__disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
.tox .tox-editor-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
z-index: 1;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header {
|
||||
@@ -1581,8 +1614,7 @@ body.tox-dialog__disable-scroll {
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-menu {
|
||||
border-color: #000000;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-autocompleter-highlight {
|
||||
@@ -2073,6 +2105,12 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-menu.tox-collection.tox-collection--grid {
|
||||
padding: 4px;
|
||||
}
|
||||
@media only screen and (min-width: 768px ) {
|
||||
.tox .tox-menu .tox-collection__item-label {
|
||||
overflow-wrap: break-word;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
.tox .tox-menu__label h1,
|
||||
.tox .tox-menu__label h2,
|
||||
.tox .tox-menu__label h3,
|
||||
@@ -2091,10 +2129,39 @@ body.tox-dialog__disable-scroll {
|
||||
flex: 0 0 auto;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
padding: 0 4px 0 4px;
|
||||
}
|
||||
.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid #000000;
|
||||
.tox .tox-promotion + .tox-menubar {
|
||||
grid-column: 1;
|
||||
}
|
||||
.tox .tox-promotion {
|
||||
background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;
|
||||
background-color: #222f3e;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
padding-inline-end: 8px;
|
||||
padding-inline-start: 4px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.tox .tox-promotion-link {
|
||||
align-items: unsafe center;
|
||||
background-color: #E8F1F8;
|
||||
border-radius: 5px;
|
||||
color: #086BE6;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 26.6px;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-promotion-link:hover {
|
||||
background-color: #B4D7FF;
|
||||
}
|
||||
.tox .tox-promotion-link:focus {
|
||||
background-color: #D9EDF7;
|
||||
}
|
||||
/* Deprecated. Remove in next major release */
|
||||
.tox .tox-mbtn {
|
||||
@@ -2874,10 +2941,14 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-toolbar__overflow--shrinking {
|
||||
transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;
|
||||
}
|
||||
.tox .tox-toolbar-overlord,
|
||||
.tox .tox-anchorbar {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.tox .tox-menubar + .tox-toolbar,
|
||||
.tox .tox-menubar + .tox-toolbar-overlord {
|
||||
border-top: 1px solid #000000;
|
||||
margin-top: 0;
|
||||
margin-top: -1px;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
@@ -3087,6 +3158,12 @@ body.tox-dialog__disable-scroll {
|
||||
border: 1px solid #000000;
|
||||
padding: 0;
|
||||
}
|
||||
.tox .tox-pop .tox-pop__dialog .tox-toolbar {
|
||||
padding: 0;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid #000000;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child {
|
||||
border-top: 1px solid #000000;
|
||||
@@ -3117,3 +3194,6 @@ body.tox-dialog__disable-scroll {
|
||||
padding-bottom: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
@@ -21,11 +21,28 @@
|
||||
.mce-content-body .mce-item-anchor[data-mce-selected] {
|
||||
outline-offset: 1px;
|
||||
}
|
||||
.tox-comments-visible .tox-comment {
|
||||
background-color: #fff0b7;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"]:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #ffe89d;
|
||||
}
|
||||
.tox-comments-visible .tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #ffe168;
|
||||
.tox-comments-visible .tox-comment[contenteditable="false"][data-mce-annotation-active="true"]:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] img:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > audio:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] > video:not([data-mce-selected]),
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"] span.mce-preview-object:not([data-mce-selected]) {
|
||||
outline: 3px solid #fed635;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment:not([data-mce-selected]) {
|
||||
background-color: #ffe89d;
|
||||
outline: none;
|
||||
}
|
||||
.tox-comments-visible span.tox-comment[data-mce-annotation-active="true"]:not([data-mce-selected="inline-boundary"]) {
|
||||
background-color: #fed635;
|
||||
}
|
||||
.tox-checklist > li:not(.tox-checklist--hidden) {
|
||||
list-style: none;
|
||||
@@ -205,6 +222,27 @@ pre[class*="language-"] {
|
||||
.tox-cursor-format-painter {
|
||||
cursor: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"), default;
|
||||
}
|
||||
div.mce-footnotes hr {
|
||||
margin-inline-end: auto;
|
||||
margin-inline-start: 0;
|
||||
width: 25%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
text-decoration: none;
|
||||
}
|
||||
@media print {
|
||||
sup.mce-footnote a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
div.mce-footnotes {
|
||||
break-inside: avoid;
|
||||
width: 100%;
|
||||
}
|
||||
div.mce-footnotes li > a.mce-footnotes-backlink {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.mce-content-body figure.align-left {
|
||||
float: left;
|
||||
}
|
||||
@@ -234,6 +272,13 @@ pre[class*="language-"] {
|
||||
.mce-preview-object[data-mce-selected="2"] .mce-shim {
|
||||
display: none;
|
||||
}
|
||||
.mce-content-body .mce-mergetag:hover {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
}
|
||||
.mce-content-body .mce-mergetag-affix {
|
||||
background-color: rgba(0, 108, 231, 0.1);
|
||||
color: #006ce7;
|
||||
}
|
||||
.mce-object {
|
||||
background: transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;
|
||||
border: 1px dashed #aaa;
|
||||
@@ -525,10 +570,10 @@ pre[class*="language-"] {
|
||||
border-color: rgba(0, 84, 180, 0.7);
|
||||
}
|
||||
}
|
||||
.mce-content-body img::-moz-selection {
|
||||
.mce-content-body img[data-mce-selected]::-moz-selection {
|
||||
background: none;
|
||||
}
|
||||
.mce-content-body img::selection {
|
||||
.mce-content-body img[data-mce-selected]::selection {
|
||||
background: none;
|
||||
}
|
||||
.ephox-snooker-resizer-bar {
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -252,11 +252,24 @@ button::-moz-focus-inner {
|
||||
margin: 0;
|
||||
outline: none;
|
||||
padding: 4px 16px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
border-radius: 3px;
|
||||
bottom: -1px;
|
||||
box-shadow: inset 0 0 0 2px #fff, 0 0 0 1px #207ab7, 0 0 0 3px rgba(32, 122, 183, 0.25);
|
||||
content: '';
|
||||
left: -1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: -1px;
|
||||
}
|
||||
.tox .tox-button[disabled] {
|
||||
background-color: #207ab7;
|
||||
background-image: none;
|
||||
@@ -272,6 +285,9 @@ button::-moz-focus-inner {
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
.tox .tox-button:focus-visible:not(:disabled)::before {
|
||||
opacity: 1;
|
||||
}
|
||||
.tox .tox-button:hover:not(:disabled) {
|
||||
background-color: #1c6ca1;
|
||||
background-image: none;
|
||||
@@ -933,8 +949,9 @@ button::-moz-focus-inner {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.tox .tox-comment__date {
|
||||
color: rgba(34, 47, 62, 0.7);
|
||||
color: #222f3e;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.tox .tox-comment__body {
|
||||
color: #222f3e;
|
||||
@@ -1093,20 +1110,30 @@ button::-moz-focus-inner {
|
||||
.tox .tox-user__avatar svg {
|
||||
fill: rgba(34, 47, 62, 0.7);
|
||||
}
|
||||
.tox .tox-user__avatar img {
|
||||
border-radius: 50%;
|
||||
height: 36px;
|
||||
object-fit: cover;
|
||||
vertical-align: middle;
|
||||
width: 36px;
|
||||
}
|
||||
.tox .tox-user__name {
|
||||
color: rgba(34, 47, 62, 0.7);
|
||||
font-size: 12px;
|
||||
color: #222f3e;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
line-height: 18px;
|
||||
text-transform: none;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg {
|
||||
.tox:not([dir=rtl]) .tox-user__avatar svg,
|
||||
.tox:not([dir=rtl]) .tox-user__avatar img {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.tox:not([dir=rtl]) .tox-user__avatar + .tox-user__name {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar svg {
|
||||
.tox[dir=rtl] .tox-user__avatar svg,
|
||||
.tox[dir=rtl] .tox-user__avatar img {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.tox[dir=rtl] .tox-user__avatar + .tox-user__name {
|
||||
@@ -1151,10 +1178,11 @@ button::-moz-focus-inner {
|
||||
width: 95vw;
|
||||
z-index: 2;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog {
|
||||
align-self: flex-start;
|
||||
margin: 8px auto;
|
||||
max-height: calc(100vh - 8px * 2);
|
||||
width: calc(100vw - 16px);
|
||||
}
|
||||
}
|
||||
@@ -1210,7 +1238,7 @@ button::-moz-focus-inner {
|
||||
text-align: left;
|
||||
text-transform: none;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body {
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -1221,7 +1249,7 @@ button::-moz-focus-inner {
|
||||
flex-direction: column;
|
||||
padding: 16px 16px;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox .tox-dialog__body-nav {
|
||||
flex-direction: row;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@@ -1389,6 +1417,9 @@ button::-moz-focus-inner {
|
||||
padding-bottom: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.tox .tox-dialog__iframe.tox-dialog__iframe--opaque {
|
||||
background: #fff;
|
||||
}
|
||||
.tox .tox-dialog__popups {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -1421,12 +1452,12 @@ button::-moz-focus-inner {
|
||||
.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -1438,12 +1469,12 @@ button::-moz-focus-inner {
|
||||
.tox[dir=rtl] .tox-dialog__body {
|
||||
text-align: right;
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width:767px) {
|
||||
@media only screen and (max-width: 767px ) {
|
||||
body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -1500,6 +1531,8 @@ body.tox-dialog__disable-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
.tox .tox-editor-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min-content;
|
||||
z-index: 1;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header {
|
||||
@@ -1510,7 +1543,7 @@ body.tox-dialog__disable-scroll {
|
||||
transition: box-shadow 0.5s;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-bottom .tox-editor-header {
|
||||
border-top: 1px solid #c1c1c1;
|
||||
border-top: 1px solid #cccccc;
|
||||
box-shadow: none;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline).tox-tinymce--toolbar-sticky-on .tox-editor-header {
|
||||
@@ -1581,8 +1614,7 @@ body.tox-dialog__disable-scroll {
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-menu {
|
||||
border-color: #cccccc;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
max-width: 25em;
|
||||
}
|
||||
.tox .tox-autocompleter .tox-autocompleter-highlight {
|
||||
@@ -2073,6 +2105,12 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-menu.tox-collection.tox-collection--grid {
|
||||
padding: 4px;
|
||||
}
|
||||
@media only screen and (min-width: 768px ) {
|
||||
.tox .tox-menu .tox-collection__item-label {
|
||||
overflow-wrap: break-word;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
.tox .tox-menu__label h1,
|
||||
.tox .tox-menu__label h2,
|
||||
.tox .tox-menu__label h3,
|
||||
@@ -2091,10 +2129,39 @@ body.tox-dialog__disable-scroll {
|
||||
flex: 0 0 auto;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
padding: 0 4px 0 4px;
|
||||
}
|
||||
.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid #cccccc;
|
||||
.tox .tox-promotion + .tox-menubar {
|
||||
grid-column: 1;
|
||||
}
|
||||
.tox .tox-promotion {
|
||||
background: url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;
|
||||
background-color: #fff;
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
padding-inline-end: 8px;
|
||||
padding-inline-start: 4px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.tox .tox-promotion-link {
|
||||
align-items: unsafe center;
|
||||
background-color: #E8F1F8;
|
||||
border-radius: 5px;
|
||||
color: #086BE6;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 26.6px;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tox .tox-promotion-link:hover {
|
||||
background-color: #B4D7FF;
|
||||
}
|
||||
.tox .tox-promotion-link:focus {
|
||||
background-color: #D9EDF7;
|
||||
}
|
||||
/* Deprecated. Remove in next major release */
|
||||
.tox .tox-mbtn {
|
||||
@@ -2874,10 +2941,14 @@ body.tox-dialog__disable-scroll {
|
||||
.tox .tox-toolbar__overflow--shrinking {
|
||||
transition: opacity 0.3s ease, height 0.2s linear 0.1s, visibility 0s linear 0.3s;
|
||||
}
|
||||
.tox .tox-toolbar-overlord,
|
||||
.tox .tox-anchorbar {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.tox .tox-menubar + .tox-toolbar,
|
||||
.tox .tox-menubar + .tox-toolbar-overlord {
|
||||
border-top: 1px solid #cccccc;
|
||||
margin-top: 0;
|
||||
margin-top: -1px;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
@@ -3087,6 +3158,12 @@ body.tox-dialog__disable-scroll {
|
||||
border: 1px solid #cccccc;
|
||||
padding: 0;
|
||||
}
|
||||
.tox .tox-pop .tox-pop__dialog .tox-toolbar {
|
||||
padding: 0;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar {
|
||||
border-top: 1px solid #cccccc;
|
||||
}
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,
|
||||
.tox:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child {
|
||||
border-top: 1px solid #cccccc;
|
||||
@@ -3117,3 +3194,6 @@ body.tox-dialog__disable-scroll {
|
||||
padding-bottom: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.tox .tox-button::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+8
-42
@@ -14,66 +14,32 @@ const buttonConfig = ref({
|
||||
// 侧边栏主导航当前实际宽度
|
||||
const mainSidebarActualWidth = computed(() => {
|
||||
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-main-sidebar-width'))
|
||||
if (['head', 'single'].includes(settingsStore.menu.menuMode))
|
||||
if (['head', 'single'].includes(settingsStore.settings.menu.menuMode)) {
|
||||
actualWidth = 0
|
||||
|
||||
}
|
||||
return `${actualWidth}px`
|
||||
})
|
||||
|
||||
// 侧边栏次导航当前实际宽度
|
||||
const subSidebarActualWidth = computed(() => {
|
||||
let actualWidth = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--g-sub-sidebar-width'))
|
||||
if (settingsStore.menu.subMenuCollapse)
|
||||
if (settingsStore.settings.menu.subMenuCollapse) {
|
||||
actualWidth = 64
|
||||
|
||||
}
|
||||
return `${actualWidth}px`
|
||||
})
|
||||
|
||||
watch(() => settingsStore.app.colorScheme, () => {
|
||||
if (settingsStore.app.colorScheme === 'dark')
|
||||
document.documentElement.classList.add('dark')
|
||||
|
||||
else
|
||||
document.documentElement.classList.remove('dark')
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
watch(() => settingsStore.mode, () => {
|
||||
if (settingsStore.mode === 'pc') {
|
||||
settingsStore.$patch((state) => {
|
||||
state.menu.subMenuCollapse = settingsStore.subMenuCollapseLastStatus
|
||||
})
|
||||
}
|
||||
else if (settingsStore.mode === 'mobile') {
|
||||
settingsStore.$patch((state) => {
|
||||
state.menu.subMenuCollapse = true
|
||||
})
|
||||
}
|
||||
document.body.setAttribute('data-mode', settingsStore.mode)
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
watch(() => settingsStore.menu.menuMode, () => {
|
||||
document.body.setAttribute('data-menu-mode', settingsStore.menu.menuMode)
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
watch([
|
||||
() => settingsStore.app.enableDynamicTitle,
|
||||
() => settingsStore.settings.app.enableDynamicTitle,
|
||||
() => settingsStore.title,
|
||||
], () => {
|
||||
if (settingsStore.app.enableDynamicTitle && settingsStore.title) {
|
||||
const title = settingsStore.title
|
||||
if (settingsStore.settings.app.enableDynamicTitle && settingsStore.title) {
|
||||
const title = typeof settingsStore.title === 'function' ? settingsStore.title() : settingsStore.title
|
||||
document.title = `${title} - ${import.meta.env.VITE_APP_TITLE}`
|
||||
}
|
||||
else {
|
||||
document.title = import.meta.env.VITE_APP_TITLE
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -88,7 +54,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-config-provider :locale="zhCn" :size="settingsStore.app.elementSize" :button="buttonConfig">
|
||||
<el-config-provider :locale="zhCn" :size="settingsStore.settings.app.elementSize" :button="buttonConfig">
|
||||
<RouterView
|
||||
:style="{
|
||||
'--g-main-sidebar-actual-width': mainSidebarActualWidth,
|
||||
|
||||
+19
-19
@@ -1,20 +1,23 @@
|
||||
import axios from 'axios'
|
||||
// import qs from 'qs'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import router from '@/router/index'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
const toLogin = () => {
|
||||
router.push({
|
||||
path: '/login',
|
||||
query: {
|
||||
redirect: router.currentRoute.value.path !== '/login' ? router.currentRoute.value.fullPath : undefined,
|
||||
},
|
||||
useUserStore().logout().then(() => {
|
||||
router.push({
|
||||
path: '/login',
|
||||
query: {
|
||||
redirect: router.currentRoute.value.path !== '/login' ? router.currentRoute.value.fullPath : undefined,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: import.meta.env.DEV && import.meta.env.VITE_OPEN_PROXY === 'true' ? '/proxy/' : import.meta.env.VITE_APP_API_BASEURL,
|
||||
timeout: 10000,
|
||||
timeout: 1000 * 60,
|
||||
responseType: 'json',
|
||||
})
|
||||
|
||||
@@ -25,9 +28,9 @@ api.interceptors.request.use(
|
||||
* 全局拦截请求发送前提交的参数
|
||||
* 以下代码为示例,在请求头里带上 token 信息
|
||||
*/
|
||||
if (userStore.isLogin && request.headers)
|
||||
if (userStore.isLogin && request.headers) {
|
||||
request.headers.Token = userStore.token
|
||||
|
||||
}
|
||||
// 是否将 POST 请求参数进行字符串化处理
|
||||
if (request.method === 'post') {
|
||||
// request.data = qs.stringify(request.data, {
|
||||
@@ -47,11 +50,7 @@ api.interceptors.response.use(
|
||||
* 请求出错时 error 会返回错误信息
|
||||
*/
|
||||
if (response.data.status === 1) {
|
||||
if (response.data.error === '') {
|
||||
// 请求成功并且没有报错
|
||||
return Promise.resolve(response.data)
|
||||
}
|
||||
else {
|
||||
if (response.data.error !== '') {
|
||||
// 这里做错误提示,如果使用了 element plus 则可以使用 Message 进行提示
|
||||
// ElMessage.error(options)
|
||||
return Promise.reject(response.data)
|
||||
@@ -60,18 +59,19 @@ api.interceptors.response.use(
|
||||
else {
|
||||
toLogin()
|
||||
}
|
||||
return Promise.resolve(response.data)
|
||||
},
|
||||
(error) => {
|
||||
let message = error.message
|
||||
if (message === 'Network Error')
|
||||
if (message === 'Network Error') {
|
||||
message = '后端网络故障'
|
||||
|
||||
else if (message.includes('timeout'))
|
||||
}
|
||||
else if (message.includes('timeout')) {
|
||||
message = '接口请求超时'
|
||||
|
||||
else if (message.includes('Request failed with status code'))
|
||||
}
|
||||
else if (message.includes('Request failed with status code')) {
|
||||
message = `接口${message.substr(message.length - 3)}异常`
|
||||
|
||||
}
|
||||
ElMessage({
|
||||
message,
|
||||
type: 'error',
|
||||
|
||||
@@ -6,5 +6,4 @@
|
||||
"overlay": #1d1e1f,
|
||||
)
|
||||
);
|
||||
|
||||
@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;
|
||||
|
||||
@@ -5,6 +5,5 @@
|
||||
// )
|
||||
// )
|
||||
// );
|
||||
|
||||
@use "element-plus/theme-chalk/src/index.scss" as *;
|
||||
@use "./dark.scss";
|
||||
|
||||
@@ -82,3 +82,7 @@ textarea {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-loading-mask {
|
||||
z-index: 900 !important;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// 文字超出隐藏,默认为单行超出隐藏,可设置多行
|
||||
@mixin text-overflow($line: 1, $fixed-width: true) {
|
||||
@if ($line == 1 and $fixed-width == true) {
|
||||
@if $line == 1 and $fixed-width == true {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@else {
|
||||
} @else {
|
||||
/* stylelint-disable-next-line value-no-vendor-prefix */
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
@@ -19,17 +17,17 @@
|
||||
@mixin position-center($type: x) {
|
||||
position: absolute;
|
||||
|
||||
@if ($type == x) {
|
||||
@if $type == x {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@if ($type == y) {
|
||||
@if $type == y {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@if ($type == xy) {
|
||||
@if $type == xy {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
|
||||
Vendored
-30
@@ -1,30 +0,0 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
Auth: typeof import('./components/Auth/index.vue')['default']
|
||||
AuthAll: typeof import('./components/AuthAll/index.vue')['default']
|
||||
BatchActionBar: typeof import('./components/BatchActionBar/index.vue')['default']
|
||||
Copyright: typeof import('./components/Copyright/index.vue')['default']
|
||||
Editor: typeof import('./components/Editor/index.vue')['default']
|
||||
FileUpload: typeof import('./components/FileUpload/index.vue')['default']
|
||||
FixedActionBar: typeof import('./components/FixedActionBar/index.vue')['default']
|
||||
ImagePreview: typeof import('./components/ImagePreview/index.vue')['default']
|
||||
ImagesUpload: typeof import('./components/ImagesUpload/index.vue')['default']
|
||||
ImageUpload: typeof import('./components/ImageUpload/index.vue')['default']
|
||||
PageHeader: typeof import('./components/PageHeader/index.vue')['default']
|
||||
PageMain: typeof import('./components/PageMain/index.vue')['default']
|
||||
PcasCascader: typeof import('./components/PcasCascader/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SearchBar: typeof import('./components/SearchBar/index.vue')['default']
|
||||
SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']
|
||||
SystemInfo: typeof import('./components/SystemInfo/index.vue')['default']
|
||||
Trend: typeof import('./components/Trend/index.vue')['default']
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,9 @@ const emit = defineEmits(['checkAll', 'checkNull'])
|
||||
const checkAll = computed({
|
||||
get() {
|
||||
let flag = false
|
||||
if (props.data.length !== 0 && props.data.length === props.selectionData.length)
|
||||
if (props.data.length !== 0 && props.data.length === props.selectionData.length) {
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
},
|
||||
set(newVal) {
|
||||
@@ -27,9 +27,9 @@ const checkAll = computed({
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
let flag = false
|
||||
if (props.selectionData.length > 0 && props.selectionData.length < props.data.length)
|
||||
if (props.selectionData.length > 0 && props.selectionData.length < props.data.length) {
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -6,15 +6,15 @@ const settingsStore = useSettingsStore()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer v-if="route.meta.copyright ?? settingsStore.copyright.enable" class="copyright">
|
||||
<footer v-if="route.meta.copyright ?? settingsStore.settings.copyright.enable" class="copyright">
|
||||
<span>Copyright</span>
|
||||
<span class="icon">©</span>
|
||||
<span v-if="settingsStore.copyright.dates">{{ settingsStore.copyright.dates }}</span>
|
||||
<template v-if="settingsStore.copyright.company">
|
||||
<a v-if="settingsStore.copyright.website" :href="settingsStore.copyright.website" target="_blank" rel="noopener">{{ settingsStore.copyright.company }}</a>
|
||||
<span v-else>{{ settingsStore.copyright.company }}</span>
|
||||
<span v-if="settingsStore.settings.copyright.dates">{{ settingsStore.settings.copyright.dates }}</span>
|
||||
<template v-if="settingsStore.settings.copyright.company">
|
||||
<a v-if="settingsStore.settings.copyright.website" :href="settingsStore.settings.copyright.website" target="_blank" rel="noopener">{{ settingsStore.settings.copyright.company }}</a>
|
||||
<span v-else>{{ settingsStore.settings.copyright.company }}</span>
|
||||
</template>
|
||||
<a v-if="settingsStore.copyright.beian" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">{{ settingsStore.copyright.beian }}</a>
|
||||
<a v-if="settingsStore.settings.copyright.beian" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">{{ settingsStore.settings.copyright.beian }}</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ const settingsStore = useSettingsStore()
|
||||
const defaultSetting = ref({
|
||||
language_url: 'tinymce/langs/zh-Hans.js',
|
||||
language: 'zh-Hans',
|
||||
skin_url: settingsStore.app.colorScheme === 'light' ? 'tinymce/skins/ui/oxide' : 'tinymce/skins/ui/oxide-dark',
|
||||
content_css: settingsStore.app.colorScheme === 'light' ? 'tinymce/skins/content/default/content.min.css' : 'tinymce/skins/content/dark/content.min.css',
|
||||
skin_url: settingsStore.settings.app.colorScheme === 'light' ? 'tinymce/skins/ui/oxide' : 'tinymce/skins/ui/oxide-dark',
|
||||
content_css: settingsStore.settings.app.colorScheme === 'light' ? 'tinymce/skins/content/default/content.min.css' : 'tinymce/skins/content/dark/content.min.css',
|
||||
min_height: 250,
|
||||
max_height: 600,
|
||||
selector: 'textarea',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup name="FileUpload">
|
||||
import { UploadUserFile } from 'element-plus'
|
||||
import { ElMessage, UploadUserFile } from 'element-plus'
|
||||
import type { UploadProps } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -48,12 +48,12 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传文件只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传文件大小不能超过 ${props.size}MB!`)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const realHeight = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-image :src="src" fit="cover" :style="`width:${realWidth};height:${realHeight};`" :preview-src-list="[src]">
|
||||
<el-image :src="src" fit="cover" :style="`width:${realWidth};height:${realHeight};`" :preview-src-list="[src]" preview-teleported>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup name="ImageUpload">
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -75,15 +76,15 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
|
||||
if (isTypeOk && isSizeOk)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
const onProgress: UploadProps['onProgress'] = (file) => {
|
||||
@@ -146,7 +147,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
|
||||
<el-alert :title="`上传图片支持 ${ext.join(' / ')} 格式,且图片大小不超过 ${size}MB,建议图片尺寸为 ${width}*${height}`" type="info" show-icon :closable="false" />
|
||||
</div>
|
||||
</div>
|
||||
<el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="[url]" @close="previewClose" />
|
||||
<el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="[url]" teleported @close="previewClose" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup name="ImagesUpload">
|
||||
import type { UploadProps } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
action: {
|
||||
@@ -81,12 +82,12 @@ function remove(index: number) {
|
||||
// 移动
|
||||
function move(index: number, type: 'left' | 'right') {
|
||||
const url = props.url
|
||||
if (type === 'left' && index !== 0)
|
||||
if (type === 'left' && index !== 0) {
|
||||
url[index] = url.splice(index - 1, 1, url[index])[0]
|
||||
|
||||
if (type === 'right' && index !== url.length - 1)
|
||||
}
|
||||
if (type === 'right' && index !== url.length - 1) {
|
||||
url[index] = url.splice(index + 1, 1, url[index])[0]
|
||||
|
||||
}
|
||||
emit('update:url', url)
|
||||
}
|
||||
|
||||
@@ -95,15 +96,15 @@ const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
const fileExt = fileName.at(-1)
|
||||
const isTypeOk = props.ext.includes(fileExt)
|
||||
const isSizeOk = file.size / 1024 / 1024 < props.size
|
||||
if (!isTypeOk)
|
||||
if (!isTypeOk) {
|
||||
ElMessage.error(`上传图片只支持 ${props.ext.join(' / ')} 格式!`)
|
||||
|
||||
if (!isSizeOk)
|
||||
}
|
||||
if (!isSizeOk) {
|
||||
ElMessage.error(`上传图片大小不能超过 ${props.size}MB!`)
|
||||
|
||||
if (isTypeOk && isSizeOk)
|
||||
}
|
||||
if (isTypeOk && isSizeOk) {
|
||||
uploadData.value.progress.preview = URL.createObjectURL(file)
|
||||
|
||||
}
|
||||
return isTypeOk && isSizeOk
|
||||
}
|
||||
const onProgress: UploadProps['onProgress'] = (file) => {
|
||||
@@ -173,7 +174,7 @@ const onSuccess: UploadProps['onSuccess'] = (res) => {
|
||||
<el-alert :title="`上传图片支持 ${ext.join(' / ')} 格式,单张图片大小不超过 ${size}MB,建议图片尺寸为 ${width}*${height},且图片数量不超过 ${max} 张`" type="info" show-icon :closable="false" />
|
||||
</div>
|
||||
</div>
|
||||
<el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="url as string[]" :initial-index="uploadData.dialogImageIndex" @close="previewClose" />
|
||||
<el-image-viewer v-if="uploadData.imageViewerVisible" :url-list="url as string[]" :initial-index="uploadData.dialogImageIndex" teleported @close="previewClose" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ defineProps({
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
|
||||
const slots = useSlots()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -23,7 +25,7 @@ defineProps({
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sub">
|
||||
<div v-if="slots.default" class="sub">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,4 +67,15 @@ defineProps({
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
[data-mode="mobile"] {
|
||||
.header {
|
||||
flex-direction: column;
|
||||
|
||||
.main {
|
||||
margin-right: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -91,18 +91,20 @@ function anyToCode(value: any[], dictionarie: any[] = pcasData.value) {
|
||||
const input: string[] = []
|
||||
if (value.length > 0) {
|
||||
const findItem = dictionarie.find((item) => {
|
||||
if (props.format === 'code')
|
||||
if (props.format === 'code') {
|
||||
return item.code === value[0]
|
||||
|
||||
else if (props.format === 'name')
|
||||
}
|
||||
else if (props.format === 'name') {
|
||||
return item.name === value[0]
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return item.name === value[0].name && item.code === value[0].code
|
||||
}
|
||||
})
|
||||
input.push(findItem.code)
|
||||
if (findItem.children)
|
||||
if (findItem.children) {
|
||||
input.push(...anyToCode(value.slice(1 - value.length), findItem.children))
|
||||
}
|
||||
}
|
||||
return input
|
||||
}
|
||||
@@ -125,8 +127,9 @@ function codeToAny(codes: string[], dictionarie: any[] = pcasData.value): any {
|
||||
})
|
||||
}
|
||||
const newCodes = codes.slice(1 - codes.length)
|
||||
if (newCodes.length > 0 && findItem.children)
|
||||
if (newCodes.length > 0 && findItem.children) {
|
||||
output.push(...codeToAny(newCodes, findItem.children))
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ const transformStyle = computed(() => {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (props.rotate !== 0)
|
||||
if (props.rotate !== 0) {
|
||||
style.push(`rotate(${props.rotate}deg)`)
|
||||
|
||||
}
|
||||
return `transform: ${style.join(' ')};`
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -24,9 +24,9 @@ const props = defineProps({
|
||||
|
||||
const isUp = computed(() => {
|
||||
let isUp = props.type === 'up'
|
||||
if (props.reverse)
|
||||
if (props.reverse) {
|
||||
isUp = !isUp
|
||||
|
||||
}
|
||||
return isUp
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<script lang="ts" setup name="AppSetting">
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import eventBus from '@/utils/eventBus'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import globalSettingsDefault from '@/settings.default'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -12,14 +13,13 @@ const menuStore = useMenuStore()
|
||||
|
||||
const isShow = ref(false)
|
||||
|
||||
const settings = ref(globalSettingsDefault)
|
||||
|
||||
watch(() => settings, () => {
|
||||
settingsStore.updateSettings(settings.value)
|
||||
menuStore.setActived(0)
|
||||
settings.value.menu.menuMode !== 'single' && menuStore.setActived(route.fullPath)
|
||||
}, {
|
||||
deep: true,
|
||||
watch(() => settingsStore.settings.menu.menuMode, (value) => {
|
||||
if (value === 'single') {
|
||||
menuStore.setActived(0)
|
||||
}
|
||||
else {
|
||||
menuStore.setActived(route.fullPath)
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@@ -31,12 +31,13 @@ onMounted(() => {
|
||||
const { copy, copied, isSupported } = useClipboard()
|
||||
|
||||
watch(copied, (val) => {
|
||||
if (val)
|
||||
if (val) {
|
||||
ElMessage.success('复制成功,请粘贴到 src/settings.ts 文件中!')
|
||||
}
|
||||
})
|
||||
|
||||
function handleCopy() {
|
||||
copy(JSON.stringify(settings.value, null, 4))
|
||||
copy(JSON.stringify(settingsStore.settings, null, 2))
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -46,9 +47,9 @@ function handleCopy() {
|
||||
<el-alert title="应用配置可实时预览效果,但只是临时生效,要想真正作用于项目,可以点击下方的“复制配置”按钮,并将配置粘贴到 src/settings.ts 文件中。同时建议在生产环境隐藏应用配置功能。" type="error" :closable="false" />
|
||||
<el-divider>颜色主题</el-divider>
|
||||
<div class="color-scheme">
|
||||
<div class="switch" :class="settings.app.colorScheme" @click="settings.app.colorScheme = settings.app.colorScheme === 'dark' ? 'light' : 'dark'">
|
||||
<div class="switch" :class="settingsStore.settings.app.colorScheme" @click="settingsStore.settings.app.colorScheme = settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark'">
|
||||
<el-icon class="icon">
|
||||
<svg-icon :name="settings.app.colorScheme === 'light' ? 'ep:sunny' : 'ep:moon'" />
|
||||
<svg-icon :name="settingsStore.settings.app.colorScheme === 'light' ? 'ep:sunny' : 'ep:moon'" />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,7 +58,7 @@ function handleCopy() {
|
||||
</el-divider>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="menu-mode">
|
||||
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500">
|
||||
<div class="mode mode-side" :class="{ active: settings.menu.menuMode === 'side' }" @click="settings.menu.menuMode = 'side'">
|
||||
<div class="mode mode-side" :class="{ active: settingsStore.settings.menu.menuMode === 'side' }" @click="settingsStore.settings.menu.menuMode = 'side'">
|
||||
<div class="mode-container" />
|
||||
<el-icon>
|
||||
<svg-icon name="ep:check" />
|
||||
@@ -65,7 +66,7 @@ function handleCopy() {
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="顶部模式" placement="top" :show-after="500">
|
||||
<div class="mode mode-head" :class="{ active: settings.menu.menuMode === 'head' }" @click="settings.menu.menuMode = 'head'">
|
||||
<div class="mode mode-head" :class="{ active: settingsStore.settings.menu.menuMode === 'head' }" @click="settingsStore.settings.menu.menuMode = 'head'">
|
||||
<div class="mode-container" />
|
||||
<el-icon>
|
||||
<svg-icon name="ep:check" />
|
||||
@@ -73,7 +74,7 @@ function handleCopy() {
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="侧边栏模式(不含主导航)" placement="top" :show-after="500">
|
||||
<div class="mode mode-single" :class="{ active: settings.menu.menuMode === 'single' }" @click="settings.menu.menuMode = 'single'">
|
||||
<div class="mode mode-single" :class="{ active: settingsStore.settings.menu.menuMode === 'single' }" @click="settingsStore.settings.menu.menuMode = 'single'">
|
||||
<div class="mode-container" />
|
||||
<el-icon>
|
||||
<svg-icon name="ep:check" />
|
||||
@@ -91,7 +92,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.menu.switchMainMenuAndPageJump" :disabled="['single'].includes(settings.menu.menuMode)" />
|
||||
<el-switch v-model="settingsStore.settings.menu.switchMainMenuAndPageJump" :disabled="['single'].includes(settingsStore.settings.menu.menuMode)" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
@@ -102,32 +103,42 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.menu.subMenuUniqueOpened" />
|
||||
<el-switch v-model="settingsStore.settings.menu.subMenuUniqueOpened" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
次导航是否折叠
|
||||
</div>
|
||||
<el-switch v-model="settings.menu.subMenuCollapse" />
|
||||
<el-switch v-model="settingsStore.settings.menu.subMenuCollapse" />
|
||||
</div>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
<div class="label">
|
||||
显示次导航折叠按钮
|
||||
</div>
|
||||
<el-switch v-model="settings.menu.enableSubMenuCollapseButton" />
|
||||
<el-switch v-model="settingsStore.settings.menu.enableSubMenuCollapseButton" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否启用快捷键
|
||||
</div>
|
||||
<el-switch v-model="settings.menu.enableHotkeys" :disabled="['single'].includes(settingsStore.menu.menuMode)" />
|
||||
<el-switch v-model="settingsStore.settings.menu.enableHotkeys" :disabled="['single'].includes(settingsStore.settings.menu.menuMode)" />
|
||||
</div>
|
||||
<el-divider>顶栏</el-divider>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否固定
|
||||
模式
|
||||
</div>
|
||||
<el-switch v-model="settings.topbar.fixed" />
|
||||
<el-radio-group v-model="settingsStore.settings.topbar.mode" size="small">
|
||||
<el-radio-button label="static">
|
||||
静止
|
||||
</el-radio-button>
|
||||
<el-radio-button label="fixed">
|
||||
固定
|
||||
</el-radio-button>
|
||||
<el-radio-button label="sticky">
|
||||
粘性
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-divider>工具栏</el-divider>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
@@ -139,7 +150,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.toolbar.enableFullscreen" />
|
||||
<el-switch v-model="settingsStore.settings.toolbar.enableFullscreen" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
@@ -150,7 +161,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.toolbar.enablePageReload" />
|
||||
<el-switch v-model="settingsStore.settings.toolbar.enablePageReload" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
@@ -161,7 +172,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.toolbar.enableColorScheme" />
|
||||
<el-switch v-model="settingsStore.settings.toolbar.enableColorScheme" />
|
||||
</div>
|
||||
<el-divider v-if="settingsStore.mode === 'pc'">
|
||||
面包屑导航
|
||||
@@ -170,7 +181,7 @@ function handleCopy() {
|
||||
<div class="label">
|
||||
是否启用
|
||||
</div>
|
||||
<el-switch v-model="settings.breadcrumb.enable" />
|
||||
<el-switch v-model="settingsStore.settings.breadcrumb.enable" />
|
||||
</div>
|
||||
<el-divider>导航搜索</el-divider>
|
||||
<div class="setting-item">
|
||||
@@ -182,62 +193,62 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.navSearch.enable" />
|
||||
<el-switch v-model="settingsStore.settings.navSearch.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否启用快捷键
|
||||
</div>
|
||||
<el-switch v-model="settings.navSearch.enableHotkeys" :disabled="!settings.navSearch.enable" />
|
||||
<el-switch v-model="settingsStore.settings.navSearch.enableHotkeys" :disabled="!settingsStore.settings.navSearch.enable" />
|
||||
</div>
|
||||
<el-divider>底部版权</el-divider>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否启用
|
||||
</div>
|
||||
<el-switch v-model="settings.copyright.enable" />
|
||||
<el-switch v-model="settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
日期
|
||||
</div>
|
||||
<el-input v-model="settings.copyright.dates" size="small" :disabled="!settings.copyright.enable" />
|
||||
<el-input v-model="settingsStore.settings.copyright.dates" size="small" :disabled="!settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
公司
|
||||
</div>
|
||||
<el-input v-model="settings.copyright.company" size="small" :disabled="!settings.copyright.enable" />
|
||||
<el-input v-model="settingsStore.settings.copyright.company" size="small" :disabled="!settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
网址
|
||||
</div>
|
||||
<el-input v-model="settings.copyright.website" size="small" :disabled="!settings.copyright.enable" />
|
||||
<el-input v-model="settingsStore.settings.copyright.website" size="small" :disabled="!settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
备案
|
||||
</div>
|
||||
<el-input v-model="settings.copyright.beian" size="small" :disabled="!settings.copyright.enable" />
|
||||
<el-input v-model="settingsStore.settings.copyright.beian" size="small" :disabled="!settingsStore.settings.copyright.enable" />
|
||||
</div>
|
||||
<el-divider>控制台</el-divider>
|
||||
<el-divider>主页</el-divider>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否开启
|
||||
<el-tooltip content="该功能开启时,登录成功默认进入控制台页面,反之则默认进入导航栏里第一个导航页面" placement="top">
|
||||
<el-tooltip content="该功能开启时,登录成功默认进入主页,反之则默认进入导航栏里第一个导航页面" placement="top">
|
||||
<el-icon>
|
||||
<svg-icon name="ep:question-filled" />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.dashboard.enable" />
|
||||
<el-switch v-model="settingsStore.settings.home.enable" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
控制台名称
|
||||
主页名称
|
||||
</div>
|
||||
<el-input v-model="settings.dashboard.title" size="small" />
|
||||
<el-input v-model="settingsStore.settings.home.title" size="small" />
|
||||
</div>
|
||||
<el-divider>其它</el-divider>
|
||||
<div class="setting-item">
|
||||
@@ -249,7 +260,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-radio-group v-model="settings.app.elementSize" size="small">
|
||||
<el-radio-group v-model="settingsStore.settings.app.elementSize" size="small">
|
||||
<el-radio-button label="large">
|
||||
较大
|
||||
</el-radio-button>
|
||||
@@ -265,7 +276,7 @@ function handleCopy() {
|
||||
<div class="label">
|
||||
是否启用权限
|
||||
</div>
|
||||
<el-switch v-model="settings.app.enablePermission" />
|
||||
<el-switch v-model="settingsStore.settings.app.enablePermission" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
@@ -276,7 +287,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.app.enableProgress" />
|
||||
<el-switch v-model="settingsStore.settings.app.enableProgress" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
@@ -287,7 +298,7 @@ function handleCopy() {
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-switch v-model="settings.app.enableDynamicTitle" />
|
||||
<el-switch v-model="settingsStore.settings.app.enableDynamicTitle" />
|
||||
</div>
|
||||
<template v-if="isSupported" #footer>
|
||||
<el-button type="primary" @click="handleCopy">
|
||||
|
||||
@@ -21,7 +21,7 @@ function handlerMouserScroll(event: WheelEvent) {
|
||||
|
||||
<template>
|
||||
<transition name="header">
|
||||
<header v-if="settingsStore.mode === 'pc' && settingsStore.menu.menuMode === 'head'">
|
||||
<header v-if="settingsStore.mode === 'pc' && settingsStore.settings.menu.menuMode === 'head'">
|
||||
<div class="header-container">
|
||||
<div class="main">
|
||||
<Logo />
|
||||
@@ -30,10 +30,10 @@ function handlerMouserScroll(event: WheelEvent) {
|
||||
<template v-for="(item, index) in menuStore.allMenus" :key="index">
|
||||
<div v-if="item.children && item.children.length !== 0" class="item-container" :class="{ active: index === menuStore.actived }">
|
||||
<div class="item" @click="switchTo(index)">
|
||||
<el-icon>
|
||||
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
||||
<el-icon v-if="item.meta?.icon">
|
||||
<svg-icon :name="item.meta.icon" />
|
||||
</el-icon>
|
||||
<span v-if="item.meta.title">{{ item.meta.title }}</span>
|
||||
<span v-if="item.meta?.title">{{ item.meta.title }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -20,11 +20,11 @@ onMounted(() => {
|
||||
<el-descriptions-item label="查看系统信息">
|
||||
Alt + I
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item v-if="settingsStore.navSearch.enable && settingsStore.navSearch.enableHotkeys" label="唤起导航搜索">
|
||||
<el-descriptions-item v-if="settingsStore.settings.navSearch.enable && settingsStore.settings.navSearch.enableHotkeys" label="唤起导航搜索">
|
||||
Alt + S
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-descriptions v-if="settingsStore.menu.enableHotkeys && ['side', 'head'].includes(settingsStore.menu.menuMode)" title="主导航" :column="1" border>
|
||||
<el-descriptions v-if="settingsStore.settings.menu.enableHotkeys && ['side', 'head'].includes(settingsStore.settings.menu.menuMode)" title="主导航" :column="1" border>
|
||||
<el-descriptions-item label="激活下一个主导航">
|
||||
Alt + `
|
||||
</el-descriptions-item>
|
||||
|
||||
@@ -22,15 +22,15 @@ const to = computed(() => {
|
||||
const rtn: {
|
||||
name?: string
|
||||
} = {}
|
||||
if (settingsStore.dashboard.enable)
|
||||
rtn.name = 'dashboard'
|
||||
|
||||
if (settingsStore.settings.home.enable) {
|
||||
rtn.name = 'home'
|
||||
}
|
||||
return rtn
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<router-link :to="to" class="title" :class="{ 'is-link': settingsStore.dashboard.enable }" :title="title">
|
||||
<router-link :to="to" class="title" :class="{ 'is-link': settingsStore.settings.home.enable }" :title="title">
|
||||
<img v-if="showLogo" :src="logo" class="logo">
|
||||
<span v-if="showTitle">{{ title }}</span>
|
||||
</router-link>
|
||||
@@ -58,6 +58,7 @@ const to = computed(() => {
|
||||
.logo {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
object-fit: contain;
|
||||
|
||||
& + span {
|
||||
margin-left: 10px;
|
||||
|
||||
@@ -11,7 +11,7 @@ const { switchTo } = useMenu()
|
||||
|
||||
<template>
|
||||
<transition name="main-sidebar">
|
||||
<div v-if="settingsStore.menu.menuMode === 'side' || (settingsStore.mode === 'mobile' && settingsStore.menu.menuMode !== 'single')" class="main-sidebar-container">
|
||||
<div v-if="settingsStore.settings.menu.menuMode === 'side' || (settingsStore.mode === 'mobile' && settingsStore.settings.menu.menuMode !== 'single')" class="main-sidebar-container">
|
||||
<Logo :show-title="false" class="sidebar-logo" />
|
||||
<!-- 侧边栏模式(含主导航) -->
|
||||
<div class="nav">
|
||||
@@ -19,12 +19,12 @@ const { switchTo } = useMenu()
|
||||
<div
|
||||
v-if="item.children && item.children.length !== 0" :key="index" class="item" :class="{
|
||||
active: index === menuStore.actived,
|
||||
}" :title="item.meta.title" @click="switchTo(index)"
|
||||
}" :title="item.meta?.title ?? '[ 无标题 ]'" @click="switchTo(index)"
|
||||
>
|
||||
<el-icon>
|
||||
<svg-icon v-if="item.meta.icon" :name="item.meta.icon" />
|
||||
<el-icon v-if="item.meta?.icon">
|
||||
<svg-icon :name="item.meta.icon" />
|
||||
</el-icon>
|
||||
<span>{{ item.meta.title }}</span>
|
||||
<span>{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts" setup name="Search">
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import { isExternalLink } from '@/utils'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import eventBus from '@/utils/eventBus'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useRouteStore from '@/store/modules/route'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import type { Menu, Route } from '@/global'
|
||||
import type { Menu, Route } from '#/global'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -15,10 +15,11 @@ const routeStore = useRouteStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
interface listTypes {
|
||||
icon?: string
|
||||
title: string
|
||||
breadcrumb: string[]
|
||||
path: string
|
||||
icon?: string
|
||||
title?: string | Function
|
||||
link?: string
|
||||
breadcrumb: (string | Function | undefined)[]
|
||||
}
|
||||
|
||||
const isShow = ref(false)
|
||||
@@ -38,15 +39,39 @@ const resultList = computed(() => {
|
||||
let result = []
|
||||
result = sourceList.value.filter((item) => {
|
||||
let flag = false
|
||||
if (item.title.includes(searchInput.value))
|
||||
if (item.title) {
|
||||
if (typeof item.title === 'function') {
|
||||
if (item.title().includes(searchInput.value)) {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (item.title.includes(searchInput.value)) {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.path.includes(searchInput.value)) {
|
||||
flag = true
|
||||
|
||||
if (item.path.includes(searchInput.value))
|
||||
}
|
||||
if (item.breadcrumb.some((b) => {
|
||||
let flag = false
|
||||
if (b) {
|
||||
if (typeof b === 'function') {
|
||||
if (b().includes(searchInput.value)) {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (b.includes(searchInput.value)) {
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag
|
||||
})) {
|
||||
flag = true
|
||||
|
||||
if (item.breadcrumb.some(b => b.includes(searchInput.value)))
|
||||
flag = true
|
||||
|
||||
}
|
||||
return flag
|
||||
})
|
||||
return result
|
||||
@@ -86,18 +111,18 @@ onMounted(() => {
|
||||
isShow.value = !isShow.value
|
||||
})
|
||||
hotkeys('alt+s', (e) => {
|
||||
if (settingsStore.navSearch.enable && settingsStore.navSearch.enableHotkeys) {
|
||||
if (settingsStore.settings.navSearch.enable && settingsStore.settings.navSearch.enableHotkeys) {
|
||||
e.preventDefault()
|
||||
isShow.value = true
|
||||
}
|
||||
})
|
||||
hotkeys('esc', (e) => {
|
||||
if (settingsStore.navSearch.enable && settingsStore.navSearch.enableHotkeys) {
|
||||
if (settingsStore.settings.navSearch.enable && settingsStore.settings.navSearch.enableHotkeys) {
|
||||
e.preventDefault()
|
||||
isShow.value = false
|
||||
}
|
||||
})
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
routeStore.routes.forEach((item) => {
|
||||
item.children && getSourceList(item.children)
|
||||
})
|
||||
@@ -109,52 +134,46 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
function hasChildren(item: Route.recordRaw) {
|
||||
function hasChildren(item: RouteRecordRaw) {
|
||||
let flag = true
|
||||
if (item.children && item.children.every(i => i.meta.sidebar === false))
|
||||
if (item.children?.every(i => i.meta?.sidebar === false)) {
|
||||
flag = false
|
||||
|
||||
}
|
||||
return flag
|
||||
}
|
||||
function getSourceList(arr: Route.recordRaw[], basePath?: string, icon?: string, breadcrumb?: string[]) {
|
||||
function getSourceList(arr: RouteRecordRaw[], basePath?: string, icon?: string, breadcrumb?: (string | Function | undefined)[]) {
|
||||
arr.forEach((item) => {
|
||||
if (item.meta.sidebar !== false) {
|
||||
if (item.meta?.sidebar !== false) {
|
||||
const breadcrumbTemp = cloneDeep(breadcrumb) || []
|
||||
if (item.children && hasChildren(item)) {
|
||||
breadcrumbTemp.push(typeof item.meta.title === 'function' ? item.meta.title() : item.meta.title)
|
||||
getSourceList(item.children, basePath ? [basePath, item.path].join('/') : item.path, item.meta.icon || icon, breadcrumbTemp)
|
||||
breadcrumbTemp.push(item.meta?.title)
|
||||
getSourceList(item.children, basePath ? [basePath, item.path].join('/') : item.path, item.meta?.icon ?? icon, breadcrumbTemp)
|
||||
}
|
||||
else {
|
||||
breadcrumbTemp.push(typeof item.meta.title === 'function' ? item.meta.title() : item.meta.title)
|
||||
let path = ''
|
||||
if (isExternalLink(item.path))
|
||||
path = item.path
|
||||
|
||||
else
|
||||
path = basePath ? [basePath, item.path].join('/') : item.path
|
||||
|
||||
breadcrumbTemp.push(item.meta?.title)
|
||||
sourceList.value.push({
|
||||
icon: item.meta.icon || icon,
|
||||
title: typeof item.meta.title === 'function' ? item.meta.title() : item.meta.title,
|
||||
path: basePath ? [basePath, item.path].join('/') : item.path,
|
||||
icon: item.meta?.icon ?? icon,
|
||||
title: item.meta?.title,
|
||||
link: item.meta?.link,
|
||||
breadcrumb: breadcrumbTemp,
|
||||
path,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?: string[]) {
|
||||
function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?: (string | Function | undefined)[]) {
|
||||
arr.forEach((item) => {
|
||||
const breadcrumbTemp = cloneDeep(breadcrumb) || []
|
||||
if (item.children && item.children.length > 0) {
|
||||
breadcrumbTemp.push(item.meta.title)
|
||||
getSourceListByMenus(item.children, item.meta.icon, breadcrumbTemp)
|
||||
breadcrumbTemp.push(item.meta?.title)
|
||||
getSourceListByMenus(item.children, item.meta?.icon ?? icon, breadcrumbTemp)
|
||||
}
|
||||
else {
|
||||
breadcrumbTemp.push(item.meta.title)
|
||||
breadcrumbTemp.push(item.meta?.title)
|
||||
sourceList.value.push({
|
||||
icon: item.meta.icon || icon,
|
||||
title: item.meta.title,
|
||||
icon: item.meta?.icon ?? icon,
|
||||
title: item.meta?.title,
|
||||
path: item.path as string,
|
||||
breadcrumb: breadcrumbTemp,
|
||||
})
|
||||
@@ -165,24 +184,25 @@ function getSourceListByMenus(arr: Menu.recordRaw[], icon?: string, breadcrumb?:
|
||||
function keyUp() {
|
||||
if (resultList.value.length) {
|
||||
actived.value -= 1
|
||||
if (actived.value < 0)
|
||||
if (actived.value < 0) {
|
||||
actived.value = resultList.value.length - 1
|
||||
|
||||
}
|
||||
handleScroll()
|
||||
}
|
||||
}
|
||||
function keyDown() {
|
||||
if (resultList.value.length) {
|
||||
actived.value += 1
|
||||
if (actived.value > resultList.value.length - 1)
|
||||
if (actived.value > resultList.value.length - 1) {
|
||||
actived.value = 0
|
||||
|
||||
}
|
||||
handleScroll()
|
||||
}
|
||||
}
|
||||
function keyEnter() {
|
||||
if (actived.value !== -1)
|
||||
if (actived.value !== -1) {
|
||||
searchResultItemRef.value[actived.value].click()
|
||||
}
|
||||
}
|
||||
function handleScroll() {
|
||||
let scrollTo = 0
|
||||
@@ -192,23 +212,25 @@ function handleScroll() {
|
||||
const activedClientHeight = searchResultItemRef.value[actived.value].clientHeight
|
||||
const searchScrollTop = searchResultRef.value.scrollTop
|
||||
const searchClientHeight = searchResultRef.value.clientHeight
|
||||
if (activedOffsetTop + activedClientHeight > searchScrollTop + searchClientHeight)
|
||||
if (activedOffsetTop + activedClientHeight > searchScrollTop + searchClientHeight) {
|
||||
scrollTo = activedOffsetTop + activedClientHeight - searchClientHeight
|
||||
|
||||
else if (activedOffsetTop <= searchScrollTop)
|
||||
}
|
||||
else if (activedOffsetTop <= searchScrollTop) {
|
||||
scrollTo = activedOffsetTop
|
||||
}
|
||||
}
|
||||
searchResultRef.value.scrollTo({
|
||||
top: scrollTo,
|
||||
})
|
||||
}
|
||||
|
||||
function pageJump(url: string) {
|
||||
if (isExternalLink(url))
|
||||
window.open(url, 'top')
|
||||
|
||||
else
|
||||
router.push(url)
|
||||
function pageJump(path: listTypes['path'], link: listTypes['link']) {
|
||||
if (link) {
|
||||
window.open(link, '_blank')
|
||||
}
|
||||
else {
|
||||
router.push(path)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -225,48 +247,60 @@ function pageJump(url: string) {
|
||||
</el-input>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="tips">
|
||||
<div class="tip">
|
||||
<span>Alt</span>+<span>S</span>
|
||||
唤醒搜索面板
|
||||
<el-tag type="info" size="large">
|
||||
Alt + S
|
||||
</el-tag>
|
||||
<el-tag type="info" size="large">
|
||||
唤醒搜索面板
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span>
|
||||
<el-tag type="info" size="large">
|
||||
<el-icon>
|
||||
<svg-icon name="search-up" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span>
|
||||
</el-tag>
|
||||
<el-tag type="info" size="large">
|
||||
<el-icon>
|
||||
<svg-icon name="search-down" />
|
||||
</el-icon>
|
||||
</span>
|
||||
切换搜索结果
|
||||
</el-tag>
|
||||
<el-tag type="info" size="large">
|
||||
切换搜索结果
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span>
|
||||
<el-tag type="info" size="large">
|
||||
<el-icon>
|
||||
<svg-icon name="search-enter" />
|
||||
</el-icon>
|
||||
</span>
|
||||
访问页面
|
||||
</el-tag>
|
||||
<el-tag type="info" size="large">
|
||||
访问页面
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="tip">
|
||||
<span>ESC</span>
|
||||
退出
|
||||
<el-tag type="info" size="large">
|
||||
ESC
|
||||
</el-tag>
|
||||
<el-tag type="info" size="large">
|
||||
退出
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="searchResultRef" class="result" :class="{ mobile: settingsStore.mode === 'mobile' }">
|
||||
<a v-for="(item, index) in resultList" :key="item.path" :ref="setSearchResultItemRef" class="item" :class="{ actived: index === actived }" @click="pageJump(item.path)" @mouseover="actived = index">
|
||||
<div ref="searchResultRef" class="result">
|
||||
<a v-for="(item, index) in resultList" :key="item.path" :ref="setSearchResultItemRef" class="item" :class="{ actived: index === actived }" @click="pageJump(item.path, item.link)" @mouseover="actived = index">
|
||||
<el-icon class="icon">
|
||||
<svg-icon v-if="item.icon" :name="item.icon" />
|
||||
</el-icon>
|
||||
<div class="info">
|
||||
<div class="title">
|
||||
{{ item.title }}
|
||||
{{ item.title ?? '[ 无标题 ]' }}
|
||||
</div>
|
||||
<div class="breadcrumb">
|
||||
<span v-for="(bc, bcIndex) in item.breadcrumb" :key="bcIndex">
|
||||
{{ bc }}
|
||||
{{ bc ?? '[ 无标题 ]' }}
|
||||
<el-icon>
|
||||
<svg-icon name="ep:arrow-right" />
|
||||
</el-icon>
|
||||
@@ -288,10 +322,10 @@ function pageJump(url: string) {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: var(--el-overlay-color-lighter);
|
||||
backdrop-filter: blur(10px);
|
||||
background-image: radial-gradient(transparent 1px, rgb(0 0 0 / 30%) 1px);
|
||||
background-size: 4px 4px;
|
||||
backdrop-filter: saturate(50%) blur(4px);
|
||||
transition: all 0.2s;
|
||||
transform: translateZ(0);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
||||
@@ -316,7 +350,7 @@ function pageJump(url: string) {
|
||||
filter: blur(10px);
|
||||
|
||||
.search-box {
|
||||
margin: 50px 20px 40px;
|
||||
margin: 50px 20px 20px;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
height: 52px;
|
||||
@@ -332,39 +366,39 @@ function pageJump(url: string) {
|
||||
|
||||
.tips {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
margin-top: 20px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
justify-content: center;
|
||||
padding-top: 20px;
|
||||
font-weight: bold;
|
||||
|
||||
span {
|
||||
margin: 0 5px;
|
||||
padding: 3px 8px 5px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: var(--el-text-color-primary);
|
||||
background-color: var(--el-fill-color);
|
||||
box-shadow: inset 0 -2px var(--el-fill-color-lighter), inset 0 0 1px 1px var(--el-fill-color-darker);
|
||||
.tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 20px;
|
||||
|
||||
.el-tag {
|
||||
margin: 0 5px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result {
|
||||
position: relative;
|
||||
margin: 0 20px;
|
||||
max-height: calc(100% - 250px);
|
||||
margin: 0 20px 50px;
|
||||
border-radius: 5px;
|
||||
overflow: auto;
|
||||
background-color: var(--el-bg-color);
|
||||
box-shadow: 0 0 0 1px var(--el-border-color-darker);
|
||||
|
||||
&.mobile {
|
||||
max-height: calc(100% - 200px);
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts" setup name="SidebarItem">
|
||||
// import SidebarItem from './index.vue'
|
||||
import { isExternalLink, resolveRoutePath } from '@/utils'
|
||||
import type { Menu } from '@/global'
|
||||
import { resolveRoutePath } from '@/utils'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import type { Menu } from '#/global'
|
||||
|
||||
const props = defineProps({
|
||||
item: {
|
||||
@@ -14,11 +15,14 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const hasChildren = computed(() => {
|
||||
let flag = true
|
||||
if (props.item.children) {
|
||||
if (props.item.children.every(item => item.meta.sidebar === false))
|
||||
if (props.item.children.every(item => item.meta?.sidebar === false)) {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
flag = false
|
||||
@@ -29,25 +33,25 @@ const hasChildren = computed(() => {
|
||||
|
||||
<template>
|
||||
<div class="sidebar-item">
|
||||
<router-link v-if="!hasChildren" v-slot="{ href, navigate, isActive, isExactActive }" custom :to="resolveRoutePath(basePath, item.path || '')">
|
||||
<a :href="isExternalLink(resolveRoutePath(basePath, item.path || '')) ? resolveRoutePath(basePath, item.path || '') : href" :class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']" :target="isExternalLink(resolveRoutePath(basePath, item.path || '')) ? '_blank' : '_self'" @click="navigate">
|
||||
<el-menu-item :title="item.meta.title" :index="resolveRoutePath(basePath, item.path || '')">
|
||||
<el-icon v-if="item.meta.icon" class="title-icon">
|
||||
<router-link v-if="!hasChildren" v-slot="{ href, navigate, isActive, isExactActive }" custom :to="resolveRoutePath(basePath, item.path)">
|
||||
<a :href="item.meta?.link ? item.meta.link : href" :class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']" :target="item.meta?.link ? '_blank' : '_self'" @click="navigate">
|
||||
<el-menu-item :title="item.meta?.title ?? '[ 无标题 ]'" :index="resolveRoutePath(basePath, item.path || '')">
|
||||
<el-icon v-if="item.meta?.icon" class="title-icon">
|
||||
<svg-icon :name="item.meta.icon" />
|
||||
</el-icon>
|
||||
<span class="title">{{ item.meta.title }}</span>
|
||||
<span class="title">{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
|
||||
</el-menu-item>
|
||||
</a>
|
||||
</router-link>
|
||||
<el-sub-menu v-else :title="item.meta.title" :index="resolveRoutePath(basePath, item.path || '')">
|
||||
<el-sub-menu v-else :title="item.meta?.title ?? '[ 无标题 ]'" :index="settingsStore.settings.app.routeBaseOn !== 'filesystem' ? resolveRoutePath(basePath, item.path) : JSON.stringify(item)">
|
||||
<template #title>
|
||||
<el-icon v-if="item.meta.icon" class="title-icon">
|
||||
<el-icon v-if="item.meta?.icon" class="title-icon">
|
||||
<svg-icon :name="item.meta.icon" />
|
||||
</el-icon>
|
||||
<span class="title">{{ item.meta.title }}</span>
|
||||
<span class="title">{{ item.meta?.title ?? '[ 无标题 ]' }}</span>
|
||||
</template>
|
||||
<template v-for="route in item.children">
|
||||
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path" :item="route" :base-path="resolveRoutePath(basePath, item.path || '')" />
|
||||
<SidebarItem v-if="route.meta?.sidebar !== false" :key="route.path" :item="route" :base-path="resolveRoutePath(basePath, item.path)" />
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</div>
|
||||
@@ -64,8 +68,7 @@ const hasChildren = computed(() => {
|
||||
@include text-overflow;
|
||||
}
|
||||
|
||||
:deep(.el-menu-item),
|
||||
:deep(.el-sub-menu__title) {
|
||||
:deep(.el-menu-item) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -15,22 +15,22 @@ function onSidebarScroll(e: Event) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="['side', 'head', 'single'].includes(settingsStore.menu.menuMode) || settingsStore.mode === 'mobile'" class="sub-sidebar-container" :class="{ 'is-collapse': settingsStore.mode === 'pc' && settingsStore.menu.subMenuCollapse }" @scroll="onSidebarScroll">
|
||||
<div v-if="['side', 'head', 'single'].includes(settingsStore.settings.menu.menuMode) || settingsStore.mode === 'mobile'" class="sub-sidebar-container" :class="{ 'is-collapse': settingsStore.mode === 'pc' && settingsStore.settings.menu.subMenuCollapse }" @scroll="onSidebarScroll">
|
||||
<Logo
|
||||
:show-logo="settingsStore.menu.menuMode === 'single'" class="sidebar-logo" :class="{
|
||||
'sidebar-logo-bg': settingsStore.menu.menuMode === 'single',
|
||||
:show-logo="settingsStore.settings.menu.menuMode === 'single'" class="sidebar-logo" :class="{
|
||||
'sidebar-logo-bg': settingsStore.settings.menu.menuMode === 'single',
|
||||
'shadow': sidebarScrollTop,
|
||||
}"
|
||||
/>
|
||||
<!-- 侧边栏模式(无主导航) -->
|
||||
<el-menu
|
||||
:unique-opened="settingsStore.menu.subMenuUniqueOpened" :default-openeds="menuStore.defaultOpenedPaths" :default-active="$route.meta.activeMenu || $route.path" :collapse="settingsStore.mode === 'pc' && settingsStore.menu.subMenuCollapse" :collapse-transition="false" :class="{
|
||||
'is-collapse-without-logo': settingsStore.menu.menuMode !== 'single' && settingsStore.menu.subMenuCollapse,
|
||||
:unique-opened="settingsStore.settings.menu.subMenuUniqueOpened" :default-openeds="menuStore.defaultOpenedPaths" :default-active="$route.meta.activeMenu || $route.path" :collapse="settingsStore.mode === 'pc' && settingsStore.settings.menu.subMenuCollapse" :collapse-transition="false" :class="{
|
||||
'is-collapse-without-logo': settingsStore.settings.menu.menuMode !== 'single' && settingsStore.settings.menu.subMenuCollapse,
|
||||
}"
|
||||
>
|
||||
<transition-group name="sub-sidebar">
|
||||
<template v-for="(route, index) in menuStore.sidebarMenus">
|
||||
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path || index" :item="route" :base-path="route.path" />
|
||||
<SidebarItem v-if="route.meta?.sidebar !== false" :key="route.path || index" :item="route" :base-path="route.path" />
|
||||
</template>
|
||||
</transition-group>
|
||||
</el-menu>
|
||||
|
||||
@@ -12,11 +12,11 @@ const userStore = useUserStore()
|
||||
const mainPage = useMainPage()
|
||||
const { isFullscreen, toggle } = useFullscreen()
|
||||
|
||||
function userCommand(command: 'dashboard' | 'setting' | 'hotkeys' | 'logout') {
|
||||
function userCommand(command: 'home' | 'setting' | 'hotkeys' | 'logout') {
|
||||
switch (command) {
|
||||
case 'dashboard':
|
||||
case 'home':
|
||||
router.push({
|
||||
name: 'dashboard',
|
||||
name: 'home',
|
||||
})
|
||||
break
|
||||
case 'setting':
|
||||
@@ -37,35 +37,35 @@ function userCommand(command: 'dashboard' | 'setting' | 'hotkeys' | 'logout') {
|
||||
}
|
||||
}
|
||||
function pro() {
|
||||
window.open('https://hooray.gitee.io/fantastic-admin-pro-example/', 'top')
|
||||
window.open('https://hooray.gitee.io/fantastic-admin-pro-example/', '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tools">
|
||||
<div class="buttons">
|
||||
<span v-if="settingsStore.navSearch.enable" class="item" @click="eventBus.emit('global-search-toggle')">
|
||||
<span v-if="settingsStore.settings.navSearch.enable" class="item" @click="eventBus.emit('global-search-toggle')">
|
||||
<el-icon>
|
||||
<svg-icon name="ep:search" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-if="settingsStore.mode === 'pc' && settingsStore.toolbar.enableFullscreen" class="item" @click="toggle">
|
||||
<span v-if="settingsStore.mode === 'pc' && settingsStore.settings.toolbar.enableFullscreen" class="item" @click="toggle">
|
||||
<el-icon>
|
||||
<svg-icon :name="isFullscreen ? 'fullscreen-exit' : 'fullscreen'" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-if="settingsStore.toolbar.enablePageReload" class="item" @click="mainPage.reload()">
|
||||
<span v-if="settingsStore.settings.toolbar.enablePageReload" class="item" @click="mainPage.reload()">
|
||||
<el-icon>
|
||||
<svg-icon name="ep:refresh-right" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-if="settingsStore.toolbar.enableColorScheme" class="item" @click="settingsStore.setColorScheme(settingsStore.app.colorScheme === 'dark' ? 'light' : 'dark')">
|
||||
<span v-if="settingsStore.settings.toolbar.enableColorScheme" class="item" @click="settingsStore.setColorScheme(settingsStore.settings.app.colorScheme === 'dark' ? 'light' : 'dark')">
|
||||
<el-icon>
|
||||
<svg-icon v-show="settingsStore.app.colorScheme === 'light'" name="ep:sunny" />
|
||||
<svg-icon v-show="settingsStore.app.colorScheme === 'dark'" name="ep:moon" />
|
||||
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'light'" name="ep:sunny" />
|
||||
<svg-icon v-show="settingsStore.settings.app.colorScheme === 'dark'" name="ep:moon" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-if="settingsStore.toolbar.enableAppSetting" class="item" @click="eventBus.emit('global-theme-toggle')">
|
||||
<span v-if="settingsStore.settings.toolbar.enableAppSetting" class="item" @click="eventBus.emit('global-theme-toggle')">
|
||||
<el-icon>
|
||||
<svg-icon name="ep:setting" />
|
||||
</el-icon>
|
||||
@@ -85,8 +85,8 @@ function pro() {
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="user-dropdown">
|
||||
<el-dropdown-item v-if="settingsStore.dashboard.enable" command="dashboard">
|
||||
控制台
|
||||
<el-dropdown-item v-if="settingsStore.settings.home.enable" command="home">
|
||||
{{ settingsStore.settings.home.title }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="setting">
|
||||
个人设置
|
||||
|
||||
@@ -9,26 +9,27 @@ const settingsStore = useSettingsStore()
|
||||
|
||||
const enableSubMenuCollapseButton = computed(() => {
|
||||
return settingsStore.mode === 'mobile' || (
|
||||
['side', 'head', 'single'].includes(settingsStore.menu.menuMode)
|
||||
&& settingsStore.menu.enableSubMenuCollapseButton
|
||||
['side', 'head', 'single'].includes(settingsStore.settings.menu.menuMode)
|
||||
&& settingsStore.settings.menu.enableSubMenuCollapseButton
|
||||
)
|
||||
})
|
||||
|
||||
const breadcrumbList = computed(() => {
|
||||
const breadcrumbList = []
|
||||
if (settingsStore.dashboard.enable) {
|
||||
if (settingsStore.settings.home.enable) {
|
||||
breadcrumbList.push({
|
||||
path: '/dashboard',
|
||||
title: settingsStore.dashboard.title,
|
||||
path: '/',
|
||||
title: settingsStore.settings.home.title,
|
||||
})
|
||||
}
|
||||
if (route.meta.breadcrumbNeste)
|
||||
if (route.meta.breadcrumbNeste) {
|
||||
breadcrumbList.push(...route.meta.breadcrumbNeste.filter(item => item.hide === false))
|
||||
|
||||
}
|
||||
return breadcrumbList
|
||||
})
|
||||
|
||||
const scrollTop = ref(0)
|
||||
const scrollOnHide = ref(false)
|
||||
onMounted(() => {
|
||||
window.addEventListener('scroll', onScroll)
|
||||
})
|
||||
@@ -36,8 +37,12 @@ onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
})
|
||||
function onScroll() {
|
||||
scrollTop.value = document.documentElement.scrollTop || document.body.scrollTop
|
||||
scrollTop.value = (document.documentElement || document.body).scrollTop
|
||||
}
|
||||
watch(scrollTop, (val, oldVal) => {
|
||||
const topbarHeight = parseInt(getComputedStyle(document.documentElement || document.body).getPropertyValue('--g-topbar-height'))
|
||||
scrollOnHide.value = settingsStore.settings.topbar.mode === 'sticky' && val > oldVal && val > topbarHeight
|
||||
})
|
||||
|
||||
function pathCompile(path: string) {
|
||||
const toPath = compile(path)
|
||||
@@ -48,20 +53,21 @@ function pathCompile(path: string) {
|
||||
<template>
|
||||
<div
|
||||
class="topbar-container" :class="{
|
||||
fixed: settingsStore.topbar.fixed,
|
||||
[`topbar-${settingsStore.settings.topbar.mode}`]: true,
|
||||
shadow: scrollTop,
|
||||
hide: scrollOnHide,
|
||||
}" data-fixed-calc-width
|
||||
>
|
||||
<div class="left-box">
|
||||
<div v-if="enableSubMenuCollapseButton" class="sidebar-collapse" :class="{ 'is-collapse': settingsStore.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()">
|
||||
<div v-if="enableSubMenuCollapseButton" class="sidebar-collapse" :class="{ 'is-collapse': settingsStore.settings.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()">
|
||||
<el-icon>
|
||||
<svg-icon name="toolbar-collapse" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-breadcrumb v-if="settingsStore.breadcrumb.enable && settingsStore.mode === 'pc' && settingsStore.app.routeBaseOn !== 'filesystem'">
|
||||
<el-breadcrumb v-if="settingsStore.settings.breadcrumb.enable && settingsStore.mode === 'pc' && settingsStore.settings.app.routeBaseOn !== 'filesystem'">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path" :to="index < breadcrumbList.length - 1 ? pathCompile(item.path) : ''">
|
||||
{{ item.title }}
|
||||
{{ item.title ?? '[ 无标题 ]' }}
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
@@ -82,7 +88,8 @@ function pathCompile(path: string) {
|
||||
background-color: var(--g-toolbar-bg);
|
||||
transition: width 0.3s, top 0.3s, transform 0.3s, background-color 0.3s, var(--el-transition-box-shadow);
|
||||
|
||||
&.fixed {
|
||||
&.topbar-fixed,
|
||||
&.topbar-sticky {
|
||||
position: fixed;
|
||||
|
||||
&.shadow {
|
||||
@@ -90,6 +97,10 @@ function pathCompile(path: string) {
|
||||
}
|
||||
}
|
||||
|
||||
&.topbar-sticky.hide {
|
||||
top: calc(var(--g-topbar-height) * -1) !important;
|
||||
}
|
||||
|
||||
.left-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<script lang="ts" setup name="LinkView">
|
||||
const route = useRoute()
|
||||
|
||||
function open() {
|
||||
window.open(route.meta.link, '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="link-view">
|
||||
<transition name="link" mode="out-in" appear>
|
||||
<page-main :key="route.meta.link" title="⚠️访问提醒">
|
||||
<div class="container">
|
||||
<div class="title">
|
||||
是否访问此链接
|
||||
</div>
|
||||
<div class="link">
|
||||
{{ route.meta.link }}
|
||||
</div>
|
||||
<el-button type="primary" plain round @click="open">
|
||||
<template #icon>
|
||||
<el-icon>
|
||||
<svg-icon name="ep:link" />
|
||||
</el-icon>
|
||||
</template>
|
||||
立即访问
|
||||
</el-button>
|
||||
</div>
|
||||
</page-main>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.link-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.page-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
margin: 0 50px;
|
||||
|
||||
.title {
|
||||
margin: 10px 0;
|
||||
font-size: 22px;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
|
||||
.link {
|
||||
margin: 10px 0;
|
||||
max-width: 300px;
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-disabled);
|
||||
|
||||
@include text-overflow(3);
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// link 区动画
|
||||
.link-enter-active {
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.link-leave-active {
|
||||
transition: 0.15s;
|
||||
}
|
||||
|
||||
.link-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.link-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
</style>
|
||||
+20
-15
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup name="Layout">
|
||||
import hotkeys from 'hotkeys-js'
|
||||
import LinkView from './components/views/link.vue'
|
||||
import Header from './components/Header/index.vue'
|
||||
import MainSidebar from './components/MainSidebar/index.vue'
|
||||
import SubSidebar from './components/SubSidebar/index.vue'
|
||||
@@ -17,33 +18,36 @@ const settingsStore = useSettingsStore()
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
watch(() => settingsStore.menu.subMenuCollapse, (val) => {
|
||||
if (settingsStore.mode === 'mobile') {
|
||||
if (!val)
|
||||
document.body.classList.add('hidden')
|
||||
const isLink = computed(() => !!routeInfo.meta.link)
|
||||
|
||||
else
|
||||
watch(() => settingsStore.settings.menu.subMenuCollapse, (val) => {
|
||||
if (settingsStore.mode === 'mobile') {
|
||||
if (!val) {
|
||||
document.body.classList.add('hidden')
|
||||
}
|
||||
else {
|
||||
document.body.classList.remove('hidden')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => routeInfo.path, () => {
|
||||
if (settingsStore.mode === 'mobile') {
|
||||
settingsStore.$patch((state) => {
|
||||
state.menu.subMenuCollapse = true
|
||||
state.settings.menu.subMenuCollapse = true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
hotkeys('f5', (e) => {
|
||||
if (settingsStore.toolbar.enablePageReload) {
|
||||
if (settingsStore.settings.toolbar.enablePageReload) {
|
||||
e.preventDefault()
|
||||
useMainPage().reload()
|
||||
}
|
||||
})
|
||||
hotkeys('alt+`', (e) => {
|
||||
if (settingsStore.menu.enableHotkeys) {
|
||||
if (settingsStore.settings.menu.enableHotkeys) {
|
||||
e.preventDefault()
|
||||
useMenu().switchTo(menuStore.actived + 1 < menuStore.allMenus.length ? menuStore.actived + 1 : 0)
|
||||
}
|
||||
@@ -60,21 +64,22 @@ onUnmounted(() => {
|
||||
<div id="app-main">
|
||||
<Header />
|
||||
<div class="wrapper">
|
||||
<div class="sidebar-container" :class="{ show: settingsStore.mode === 'mobile' && !settingsStore.menu.subMenuCollapse }">
|
||||
<div class="sidebar-container" :class="{ show: settingsStore.mode === 'mobile' && !settingsStore.settings.menu.subMenuCollapse }">
|
||||
<MainSidebar />
|
||||
<SubSidebar />
|
||||
</div>
|
||||
<div class="sidebar-mask" :class="{ show: settingsStore.mode === 'mobile' && !settingsStore.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()" />
|
||||
<div class="sidebar-mask" :class="{ show: settingsStore.mode === 'mobile' && !settingsStore.settings.menu.subMenuCollapse }" @click="settingsStore.toggleSidebarCollapse()" />
|
||||
<div class="main-container" :style="{ 'padding-bottom': $route.meta.paddingBottom } as any">
|
||||
<Topbar v-if="!(settingsStore.menu.menuMode === 'head' && !settingsStore.menu.enableSubMenuCollapseButton && !settingsStore.breadcrumb.enable)" />
|
||||
<Topbar v-if="!(settingsStore.settings.menu.menuMode === 'head' && !settingsStore.settings.menu.enableSubMenuCollapseButton && !settingsStore.settings.breadcrumb.enable)" />
|
||||
<div class="main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="main" mode="out-in" appear>
|
||||
<keep-alive :include="keepAliveStore.list">
|
||||
<component :is="Component" :key="route.fullPath" />
|
||||
<component :is="Component" v-show="!isLink" :key="route.fullPath" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
<LinkView v-if="isLink" />
|
||||
</div>
|
||||
<Copyright />
|
||||
</div>
|
||||
@@ -147,10 +152,10 @@ onUnmounted(() => {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
backdrop-filter: blur(2px);
|
||||
background-image: radial-gradient(transparent 1px, rgb(0 0 0 / 30%) 1px);
|
||||
background-size: 4px 4px;
|
||||
backdrop-filter: saturate(50%) blur(4px);
|
||||
transition: all 0.2s;
|
||||
transform: translateZ(0);
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
|
||||
|
||||
+3
-2
@@ -13,13 +13,14 @@ import 'virtual:svg-icons-register'
|
||||
import '@/assets/styles/globals.scss'
|
||||
// 加载 iconify 图标(element plus)
|
||||
import { downloadAndInstall } from '@/iconify-ep'
|
||||
if (useSettingsStore().app.iconifyOfflineUse)
|
||||
downloadAndInstall()
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(ElementPlus)
|
||||
app.use(pinia)
|
||||
app.use(router)
|
||||
directive(app)
|
||||
if (useSettingsStore().settings.app.iconifyOfflineUse) {
|
||||
downloadAndInstall()
|
||||
}
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import MultilevelMenuExample from './modules/multilevel.menu.example'
|
||||
|
||||
import type { Menu } from '@/global'
|
||||
import type { Menu } from '#/global'
|
||||
|
||||
const menu: Menu.recordMainRaw[] = [
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Menu } from '@/global'
|
||||
import type { Menu } from '#/global'
|
||||
|
||||
const menus: Menu.recordRaw = {
|
||||
meta: {
|
||||
title: '多级导航',
|
||||
icon: 'heroicons-solid:menu-alt-3',
|
||||
icon: 'sidebar-menu',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
||||
|
||||
const mocks: any[] = []
|
||||
const mockContext: any = import.meta.glob('./mock/*.js', { eager: true })
|
||||
const mockContext = import.meta.glob('./mock/*.ts', { eager: true })
|
||||
Object.keys(mockContext).forEach((v) => {
|
||||
mocks.push(...mockContext[v].default)
|
||||
mocks.push(...(mockContext[v] as any).default)
|
||||
})
|
||||
|
||||
export function setupProdMockServer() {
|
||||
|
||||
+49
-49
@@ -15,7 +15,7 @@ const { isLoading } = useNProgress()
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: useSettingsStore(pinia).app.routeBaseOn === 'filesystem' ? constantRoutesByFilesystem : constantRoutes as RouteRecordRaw[],
|
||||
routes: useSettingsStore(pinia).settings.app.routeBaseOn === 'filesystem' ? constantRoutesByFilesystem : constantRoutes as RouteRecordRaw[],
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
@@ -23,51 +23,41 @@ router.beforeEach(async (to, from, next) => {
|
||||
const userStore = useUserStore()
|
||||
const menuStore = useMenuStore()
|
||||
const routeStore = useRouteStore()
|
||||
settingsStore.app.enableProgress && (isLoading.value = true)
|
||||
settingsStore.settings.app.enableProgress && (isLoading.value = true)
|
||||
// 是否已登录
|
||||
if (userStore.isLogin) {
|
||||
// 是否已根据权限动态生成并挂载路由
|
||||
// 是否已根据权限动态生成并注册路由
|
||||
if (routeStore.isGenerate) {
|
||||
// 导航栏如果不是 single 模式,则需要根据 path 定位主导航的选中状态
|
||||
settingsStore.menu.menuMode !== 'single' && menuStore.setActived(to.path)
|
||||
if (to.name) {
|
||||
if (to.matched.length !== 0) {
|
||||
// 如果已登录状态下,进入登录页会强制跳转到控制台页面
|
||||
if (to.name === 'login') {
|
||||
next({
|
||||
name: 'dashboard',
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
else if (!settingsStore.dashboard.enable && to.name === 'dashboard') {
|
||||
// 如果未开启控制台页面,则默认进入侧边栏导航第一个模块
|
||||
if (menuStore.sidebarMenus.length > 0) {
|
||||
next({
|
||||
path: menuStore.sidebarMenusFirstDeepestPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 如果是通过 name 跳转,并且 name 对应的路由没有权限时,需要做这步处理,手动指向到 404 页面
|
||||
settingsStore.settings.menu.menuMode !== 'single' && menuStore.setActived(to.path)
|
||||
// 如果已登录状态下,进入登录页会强制跳转到主页
|
||||
if (to.name === 'login') {
|
||||
next({
|
||||
name: 'home',
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
// 如果未开启主页,但进入的是主页,则会进入侧边栏导航第一个模块
|
||||
else if (!settingsStore.settings.home.enable && to.name === 'home') {
|
||||
if (menuStore.sidebarMenus.length > 0) {
|
||||
next({
|
||||
path: '/404',
|
||||
path: menuStore.sidebarMenusFirstDeepestPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
// 如果侧边栏导航第一个模块无法命中,则还是进入主页
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
// 正常访问页面
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (settingsStore.app.routeBaseOn) {
|
||||
// 生成动态路由
|
||||
switch (settingsStore.settings.app.routeBaseOn) {
|
||||
case 'frontend':
|
||||
await routeStore.generateRoutesAtFront(asyncRoutes)
|
||||
break
|
||||
@@ -76,7 +66,8 @@ router.beforeEach(async (to, from, next) => {
|
||||
break
|
||||
case 'filesystem':
|
||||
await routeStore.generateRoutesAtFilesystem(asyncRoutesByFilesystem)
|
||||
switch (settingsStore.menu.baseOn) {
|
||||
// 文件系统生成的路由,需要手动生成导航数据
|
||||
switch (settingsStore.settings.menu.baseOn) {
|
||||
case 'frontend':
|
||||
await menuStore.generateMenusAtFront()
|
||||
break
|
||||
@@ -86,18 +77,21 @@ router.beforeEach(async (to, from, next) => {
|
||||
}
|
||||
break
|
||||
}
|
||||
// 注册并记录路由数据
|
||||
// 记录的数据会在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||
const removeRoutes: Function[] = []
|
||||
routeStore.flatRoutes.forEach((route) => {
|
||||
if (!/^(https?:|mailto:|tel:)/.test(route.path))
|
||||
if (!/^(https?:|mailto:|tel:)/.test(route.path)) {
|
||||
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
|
||||
}
|
||||
})
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
routeStore.flatSystemRoutes.forEach((route) => {
|
||||
removeRoutes.push(router.addRoute(route as RouteRecordRaw))
|
||||
})
|
||||
}
|
||||
// 记录路由数据,在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||
routeStore.setCurrentRemoveRoutes(removeRoutes)
|
||||
// 动态路由生成并注册后,重新进入当前路由
|
||||
next({
|
||||
path: to.path,
|
||||
query: to.query,
|
||||
@@ -110,7 +104,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
next({
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.fullPath,
|
||||
redirect: to.fullPath !== '/' ? to.fullPath : undefined,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -123,17 +117,23 @@ router.beforeEach(async (to, from, next) => {
|
||||
router.afterEach((to, from) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
settingsStore.app.enableProgress && (isLoading.value = false)
|
||||
settingsStore.settings.app.enableProgress && (isLoading.value = false)
|
||||
// 设置页面 title
|
||||
to.meta.title && settingsStore.setTitle(typeof to.meta.title === 'function' ? to.meta.title() : to.meta.title)
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
settingsStore.setTitle(to.meta.breadcrumbNeste?.at(-1)?.title)
|
||||
}
|
||||
else {
|
||||
settingsStore.setTitle(to.meta.title)
|
||||
}
|
||||
// 判断当前页面是否开启缓存,如果开启,则将当前页面的 name 信息存入 keep-alive 全局状态
|
||||
if (to.meta.cache) {
|
||||
const componentName = to.matched.at(-1)?.components?.default.name
|
||||
if (componentName)
|
||||
if (componentName) {
|
||||
keepAliveStore.add(componentName)
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
console.warn('该页面组件未设置组件名,会导致缓存失效,请检查')
|
||||
}
|
||||
}
|
||||
// 判断离开页面是否开启缓存,如果开启,则根据缓存规则判断是否需要清空 keep-alive 全局状态里离开页面的 name 信息
|
||||
if (from.meta.cache) {
|
||||
@@ -142,22 +142,22 @@ router.afterEach((to, from) => {
|
||||
// 通过 meta.cache 判断针对哪些页面进行缓存
|
||||
switch (typeof from.meta.cache) {
|
||||
case 'string':
|
||||
if (from.meta.cache !== to.name)
|
||||
if (from.meta.cache !== to.name) {
|
||||
keepAliveStore.remove(componentName)
|
||||
|
||||
}
|
||||
break
|
||||
case 'object':
|
||||
if (!from.meta.cache.includes(to.name as string))
|
||||
if (!from.meta.cache.includes(to.name as string)) {
|
||||
keepAliveStore.remove(componentName)
|
||||
|
||||
}
|
||||
break
|
||||
}
|
||||
// 如果进入的是 reload 页面,则也将离开页面的缓存清空
|
||||
if (to.name === 'reload')
|
||||
if (to.name === 'reload') {
|
||||
keepAliveStore.remove(componentName)
|
||||
}
|
||||
}
|
||||
}
|
||||
document.body.scrollTop = 0
|
||||
document.documentElement.scrollTop = 0
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Route } from '@/global'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
|
||||
const Layout = () => import('@/layouts/index.vue')
|
||||
|
||||
const routes: Route.recordRaw = {
|
||||
const routes: RouteRecordRaw = {
|
||||
path: '/breadcrumb_example',
|
||||
component: Layout,
|
||||
redirect: '/breadcrumb_example/list1',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Route } from '@/global'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
|
||||
const Layout = () => import('@/layouts/index.vue')
|
||||
|
||||
const routes: Route.recordRaw = {
|
||||
const routes: RouteRecordRaw = {
|
||||
path: '/multilevel_menu_example',
|
||||
component: Layout,
|
||||
redirect: '/multilevel_menu_example/page',
|
||||
|
||||
+18
-45
@@ -1,19 +1,13 @@
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
import generatedRoutes from 'virtual:generated-pages'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import MultilevelMenuExample from './modules/multilevel.menu.example'
|
||||
import BreadcrumbExample from './modules/breadcrumb.example'
|
||||
import type { Route } from '@/global'
|
||||
import type { Route } from '#/global'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
|
||||
// 固定路由(默认路由)
|
||||
const constantRoutes: Route.recordRaw[] = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
meta: {
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
const constantRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
@@ -33,35 +27,33 @@ const constantRoutes: Route.recordRaw[] = [
|
||||
]
|
||||
|
||||
// 系统路由
|
||||
const systemRoutes: Route.recordRaw[] = [
|
||||
const systemRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
path: '/',
|
||||
component: () => import('@/layouts/index.vue'),
|
||||
meta: {
|
||||
title: () => useSettingsStore().dashboard.title,
|
||||
title: () => useSettingsStore().settings.home.title,
|
||||
breadcrumb: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'dashboard',
|
||||
name: 'home',
|
||||
component: () => import('@/views/index.vue'),
|
||||
meta: {
|
||||
title: () => useSettingsStore().dashboard.title,
|
||||
title: () => useSettingsStore().settings.home.title,
|
||||
breadcrumb: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'reload',
|
||||
name: 'reload',
|
||||
component: () => import('@/views/reload.vue'),
|
||||
meta: {
|
||||
title: '重新加载',
|
||||
breadcrumb: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/personal',
|
||||
component: () => import('@/layouts/index.vue'),
|
||||
redirect: '/personal/setting',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
breadcrumb: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'setting',
|
||||
name: 'personalSetting',
|
||||
@@ -81,25 +73,6 @@ const systemRoutes: Route.recordRaw[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/reload',
|
||||
component: () => import('@/layouts/index.vue'),
|
||||
meta: {
|
||||
title: '重新加载',
|
||||
breadcrumb: false,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'reload',
|
||||
component: () => import('@/views/reload.vue'),
|
||||
meta: {
|
||||
title: '重新加载',
|
||||
breadcrumb: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
// 动态路由(异步路由、导航栏路由)
|
||||
@@ -122,7 +95,7 @@ const constantRoutesByFilesystem = generatedRoutes.filter((item) => {
|
||||
|
||||
const asyncRoutesByFilesystem = setupLayouts(generatedRoutes.filter((item) => {
|
||||
return item.meta?.enabled !== false && item.meta?.constant !== true && item.meta?.layout !== false
|
||||
})) as Route.recordRaw[]
|
||||
}))
|
||||
|
||||
export {
|
||||
constantRoutes,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 该文件为系统默认配置,请勿修改!!!
|
||||
|
||||
import type { RecursiveRequired, Settings } from './global'
|
||||
import type { RecursiveRequired, Settings } from '#/global'
|
||||
|
||||
const globalSettingsDefault: RecursiveRequired<Settings.all> = {
|
||||
app: {
|
||||
@@ -12,9 +12,9 @@ const globalSettingsDefault: RecursiveRequired<Settings.all> = {
|
||||
enableDynamicTitle: false,
|
||||
routeBaseOn: 'frontend',
|
||||
},
|
||||
dashboard: {
|
||||
home: {
|
||||
enable: true,
|
||||
title: '控制台',
|
||||
title: '主页',
|
||||
},
|
||||
layout: {
|
||||
enableMobileAdaptation: false,
|
||||
@@ -29,7 +29,7 @@ const globalSettingsDefault: RecursiveRequired<Settings.all> = {
|
||||
enableHotkeys: false,
|
||||
},
|
||||
topbar: {
|
||||
fixed: true,
|
||||
mode: 'static',
|
||||
},
|
||||
toolbar: {
|
||||
enableFullscreen: false,
|
||||
@@ -46,7 +46,7 @@ const globalSettingsDefault: RecursiveRequired<Settings.all> = {
|
||||
},
|
||||
copyright: {
|
||||
enable: true,
|
||||
dates: '2020-2022',
|
||||
dates: '2020-2023',
|
||||
company: 'Fantastic-admin',
|
||||
website: 'https://hooray.gitee.io/fantastic-admin/',
|
||||
beian: '',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { Settings } from './global'
|
||||
import type { Settings } from '#/global'
|
||||
|
||||
const globalSettings: Settings.all = {}
|
||||
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
const useKeepAliveStore = defineStore(
|
||||
// 唯一ID
|
||||
'keepAlive',
|
||||
{
|
||||
state: () => ({
|
||||
list: [] as string[],
|
||||
}),
|
||||
actions: {
|
||||
add(name: string | string[]) {
|
||||
if (typeof name === 'string') {
|
||||
!this.list.includes(name) && this.list.push(name)
|
||||
}
|
||||
else {
|
||||
name.forEach((v) => {
|
||||
v && !this.list.includes(v) && this.list.push(v)
|
||||
})
|
||||
}
|
||||
},
|
||||
remove(name: string | string[]) {
|
||||
if (typeof name === 'string') {
|
||||
this.list = this.list.filter((v) => {
|
||||
return v !== name
|
||||
})
|
||||
}
|
||||
else {
|
||||
this.list = this.list.filter((v) => {
|
||||
return !name.includes(v)
|
||||
})
|
||||
}
|
||||
},
|
||||
clean() {
|
||||
this.list = []
|
||||
},
|
||||
},
|
||||
() => {
|
||||
const list = ref<string[]>([])
|
||||
|
||||
function add(name: string | string[]) {
|
||||
if (typeof name === 'string') {
|
||||
!list.value.includes(name) && list.value.push(name)
|
||||
}
|
||||
else {
|
||||
name.forEach((v) => {
|
||||
v && !list.value.includes(v) && list.value.push(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
function remove(name: string | string[]) {
|
||||
if (typeof name === 'string') {
|
||||
list.value = list.value.filter((v) => {
|
||||
return v !== name
|
||||
})
|
||||
}
|
||||
else {
|
||||
list.value = list.value.filter((v) => {
|
||||
return !name.includes(v)
|
||||
})
|
||||
}
|
||||
}
|
||||
function clean() {
|
||||
list.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
list,
|
||||
add,
|
||||
remove,
|
||||
clean,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+162
-178
@@ -2,204 +2,188 @@ import { cloneDeep } from 'lodash-es'
|
||||
import useSettingsStore from './settings'
|
||||
import useUserStore from './user'
|
||||
import useRouteStore from './route'
|
||||
import type { Menu } from '@/global'
|
||||
import type { Menu } from '#/global'
|
||||
|
||||
import { resolveRoutePath } from '@/utils'
|
||||
import api from '@/api'
|
||||
import menu from '@/menu'
|
||||
|
||||
function getDeepestPath(menu: Menu.recordRaw, rootPath = '') {
|
||||
let retnPath = ''
|
||||
if (menu.path) {
|
||||
if (menu.children) {
|
||||
if (
|
||||
menu.children.some((item) => {
|
||||
return item.meta.sidebar !== false
|
||||
})
|
||||
) {
|
||||
for (let i = 0; i < menu.children.length; i++) {
|
||||
if (menu.children[i].meta.sidebar !== false) {
|
||||
retnPath = getDeepestPath(menu.children[i], resolveRoutePath(rootPath, menu.path))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
retnPath = getDeepestPath(menu.children[0], resolveRoutePath(rootPath, menu.path))
|
||||
}
|
||||
}
|
||||
else {
|
||||
retnPath = resolveRoutePath(rootPath, menu.path)
|
||||
}
|
||||
}
|
||||
return retnPath
|
||||
}
|
||||
|
||||
function hasPermission(permissions: string[], menu: Menu.recordMainRaw | Menu.recordRaw) {
|
||||
let isAuth = false
|
||||
if (menu.meta && menu.meta.auth) {
|
||||
isAuth = permissions.some((auth) => {
|
||||
return typeof menu.meta.auth === 'string'
|
||||
? menu.meta.auth === auth
|
||||
: typeof menu.meta.auth === 'object'
|
||||
? menu.meta.auth.includes(auth)
|
||||
: false
|
||||
})
|
||||
}
|
||||
else {
|
||||
isAuth = true
|
||||
}
|
||||
return isAuth
|
||||
}
|
||||
|
||||
function filterAsyncMenus<T extends Menu.recordMainRaw[] | Menu.recordRaw[]>(menus: T, permissions: string[]): T {
|
||||
const res: any = []
|
||||
menus.forEach((menu) => {
|
||||
const tmpMenu = cloneDeep(menu)
|
||||
if (hasPermission(permissions, tmpMenu)) {
|
||||
if (tmpMenu.children) {
|
||||
tmpMenu.children = filterAsyncMenus(tmpMenu.children, permissions) as Menu.recordRaw[]
|
||||
tmpMenu.children.length && res.push(tmpMenu)
|
||||
}
|
||||
else {
|
||||
res.push(tmpMenu)
|
||||
}
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
function getDefaultOpenedPaths(menus: Menu.recordRaw[], rootPath = '') {
|
||||
const defaultOpenedPaths: string[] = []
|
||||
menus.forEach((item) => {
|
||||
if (item.path && item.meta.defaultOpened && item.children) {
|
||||
defaultOpenedPaths.push(resolveRoutePath(rootPath, item.path))
|
||||
const childrenDefaultOpenedPaths = getDefaultOpenedPaths(item.children, resolveRoutePath(rootPath, item.path))
|
||||
if (childrenDefaultOpenedPaths.length > 0)
|
||||
defaultOpenedPaths.push(...childrenDefaultOpenedPaths)
|
||||
}
|
||||
})
|
||||
return defaultOpenedPaths
|
||||
}
|
||||
|
||||
const useMenuStore = defineStore(
|
||||
// 唯一ID
|
||||
'menu',
|
||||
{
|
||||
state: () => ({
|
||||
menus: [{
|
||||
() => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
const routeStore = useRouteStore()
|
||||
|
||||
const menus = ref<Menu.recordMainRaw[]>([{
|
||||
meta: {},
|
||||
children: [],
|
||||
}])
|
||||
const actived = ref(0)
|
||||
|
||||
// 完整导航数据
|
||||
const allMenus = computed(() => {
|
||||
let returnMenus: Menu.recordMainRaw[] = [{
|
||||
meta: {},
|
||||
children: [],
|
||||
}] as Menu.recordMainRaw[],
|
||||
actived: 0,
|
||||
}),
|
||||
getters: {
|
||||
// 完整导航数据
|
||||
allMenus() {
|
||||
const settingsStore = useSettingsStore()
|
||||
let menus: Menu.recordMainRaw[] = [{
|
||||
meta: {},
|
||||
children: [],
|
||||
}]
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
const routeStore = useRouteStore()
|
||||
if (settingsStore.menu.menuMode === 'single') {
|
||||
menus[0].children = []
|
||||
routeStore.routes.forEach((item) => {
|
||||
menus[0].children?.push(...item.children as Menu.recordRaw[])
|
||||
})
|
||||
}
|
||||
else {
|
||||
menus = routeStore.routes as Menu.recordMainRaw[]
|
||||
}
|
||||
}]
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
if (settingsStore.settings.menu.menuMode === 'single') {
|
||||
returnMenus[0].children = []
|
||||
routeStore.routes.forEach((item) => {
|
||||
returnMenus[0].children?.push(...item.children as Menu.recordRaw[])
|
||||
})
|
||||
}
|
||||
else {
|
||||
menus = this.menus
|
||||
returnMenus = routeStore.routes as Menu.recordMainRaw[]
|
||||
}
|
||||
return menus
|
||||
},
|
||||
// 次导航数据
|
||||
sidebarMenus(): Menu.recordMainRaw['children'] {
|
||||
return this.allMenus.length > 0
|
||||
? this.allMenus[this.actived].children
|
||||
: []
|
||||
},
|
||||
// 次导航里第一个导航的路径
|
||||
sidebarMenusFirstDeepestPath(): string {
|
||||
return this.allMenus.length > 0
|
||||
? getDeepestPath(this.sidebarMenus[0])
|
||||
: '/'
|
||||
},
|
||||
defaultOpenedPaths() {
|
||||
const settingsStore = useSettingsStore()
|
||||
let defaultOpenedPaths: string[] = []
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem')
|
||||
defaultOpenedPaths = getDefaultOpenedPaths(this.sidebarMenus)
|
||||
}
|
||||
else {
|
||||
returnMenus = menus.value
|
||||
}
|
||||
return returnMenus
|
||||
})
|
||||
// 次导航数据
|
||||
const sidebarMenus = computed<Menu.recordMainRaw['children']>(() => {
|
||||
return allMenus.value.length > 0
|
||||
? allMenus.value[actived.value].children
|
||||
: []
|
||||
})
|
||||
// 次导航第一层最深路径
|
||||
const sidebarMenusFirstDeepestPath = computed(() => {
|
||||
return allMenus.value.length > 0
|
||||
? getDeepestPath(sidebarMenus.value[0])
|
||||
: '/'
|
||||
})
|
||||
function getDeepestPath(menu: Menu.recordRaw, rootPath = '') {
|
||||
let retnPath = ''
|
||||
if (menu.children) {
|
||||
const item = menu.children.find(item => item.meta?.sidebar !== false)
|
||||
if (item) {
|
||||
retnPath = getDeepestPath(item, resolveRoutePath(rootPath, menu.path))
|
||||
}
|
||||
else {
|
||||
retnPath = getDeepestPath(menu.children[0], resolveRoutePath(rootPath, menu.path))
|
||||
}
|
||||
}
|
||||
else {
|
||||
retnPath = resolveRoutePath(rootPath, menu.path)
|
||||
}
|
||||
return retnPath
|
||||
}
|
||||
// 默认展开的导航路径
|
||||
const defaultOpenedPaths = computed(() => {
|
||||
let defaultOpenedPaths: string[] = []
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
defaultOpenedPaths = getDefaultOpenedPaths(sidebarMenus.value)
|
||||
}
|
||||
return defaultOpenedPaths
|
||||
})
|
||||
function getDefaultOpenedPaths(menus: Menu.recordRaw[], rootPath = '') {
|
||||
const defaultOpenedPaths: string[] = []
|
||||
menus.forEach((item) => {
|
||||
if (item.meta?.defaultOpened && item.children) {
|
||||
defaultOpenedPaths.push(resolveRoutePath(rootPath, item.path))
|
||||
const childrenDefaultOpenedPaths = getDefaultOpenedPaths(item.children, resolveRoutePath(rootPath, item.path))
|
||||
if (childrenDefaultOpenedPaths.length > 0) {
|
||||
defaultOpenedPaths.push(...childrenDefaultOpenedPaths)
|
||||
}
|
||||
}
|
||||
})
|
||||
return defaultOpenedPaths
|
||||
}
|
||||
|
||||
return defaultOpenedPaths
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 生成导航(前端生成)
|
||||
generateMenusAtFront() {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedMenus
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(menu, permissions)
|
||||
// 判断是否有权限
|
||||
function hasPermission(permissions: string[], menu: Menu.recordMainRaw | Menu.recordRaw) {
|
||||
let isAuth = false
|
||||
if (menu.meta && menu.meta.auth) {
|
||||
isAuth = permissions.some((auth) => {
|
||||
return typeof menu.meta?.auth === 'string'
|
||||
? menu.meta.auth === auth
|
||||
: typeof menu.meta?.auth === 'object'
|
||||
? menu.meta.auth.includes(auth)
|
||||
: false
|
||||
})
|
||||
}
|
||||
else {
|
||||
isAuth = true
|
||||
}
|
||||
return isAuth
|
||||
}
|
||||
// 根据权限过滤导航
|
||||
function filterAsyncMenus<T extends Menu.recordMainRaw[] | Menu.recordRaw[]>(menus: T, permissions: string[]): T {
|
||||
const res: any = []
|
||||
menus.forEach((menu) => {
|
||||
const tmpMenu = cloneDeep(menu)
|
||||
if (hasPermission(permissions, tmpMenu)) {
|
||||
if (tmpMenu.children) {
|
||||
tmpMenu.children = filterAsyncMenus(tmpMenu.children, permissions) as Menu.recordRaw[]
|
||||
tmpMenu.children.length && res.push(tmpMenu)
|
||||
}
|
||||
else {
|
||||
accessedMenus = cloneDeep(menu)
|
||||
res.push(tmpMenu)
|
||||
}
|
||||
this.menus = accessedMenus.filter(item => item.children.length !== 0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 生成导航(后端生成)
|
||||
generateMenusAtBack() {
|
||||
return new Promise<void>((resolve) => {
|
||||
api.get('menu/list', {
|
||||
baseURL: '/mock/',
|
||||
}).then(async (res) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedMenus: Menu.recordMainRaw[]
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(res.data, permissions)
|
||||
}
|
||||
else {
|
||||
accessedMenus = cloneDeep(res.data)
|
||||
}
|
||||
this.menus = accessedMenus.filter(item => item.children.length !== 0)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
// 切换主导航
|
||||
setActived(data: number | string) {
|
||||
if (typeof data === 'number') {
|
||||
// 如果是 number 类型,则认为是主导航的索引
|
||||
this.actived = data
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
// 生成导航(前端生成)
|
||||
async function generateMenusAtFront() {
|
||||
let accessedMenus
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(menu, permissions)
|
||||
}
|
||||
else {
|
||||
accessedMenus = cloneDeep(menu)
|
||||
}
|
||||
menus.value = accessedMenus.filter(item => item.children.length !== 0)
|
||||
}
|
||||
// 生成导航(后端生成)
|
||||
async function generateMenusAtBack() {
|
||||
await api.get('menu/list', {
|
||||
baseURL: '/mock/',
|
||||
}).then(async (res) => {
|
||||
let accessedMenus: Menu.recordMainRaw[]
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(res.data, permissions)
|
||||
}
|
||||
else {
|
||||
// 如果是 string 类型,则认为是路由,需要查找对应的主导航索引
|
||||
this.allMenus.forEach((item, index) => {
|
||||
if (
|
||||
item.children.some((r) => {
|
||||
return data.indexOf(`${r.path}/`) === 0 || data === r.path
|
||||
})
|
||||
)
|
||||
this.actived = index
|
||||
})
|
||||
accessedMenus = cloneDeep(res.data)
|
||||
}
|
||||
},
|
||||
},
|
||||
menus.value = accessedMenus.filter(item => item.children.length !== 0)
|
||||
}).catch(() => {})
|
||||
}
|
||||
// 切换主导航
|
||||
function setActived(data: number | string) {
|
||||
if (typeof data === 'number') {
|
||||
// 如果是 number 类型,则认为是主导航的索引
|
||||
actived.value = data
|
||||
}
|
||||
else {
|
||||
// 如果是 string 类型,则认为是路由,需要查找对应的主导航索引
|
||||
const findIndex = allMenus.value.findIndex(item => item.children.some(r => data.indexOf(`${r.path}/`) === 0 || data === r.path))
|
||||
if (findIndex >= 0) {
|
||||
actived.value = findIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
menus,
|
||||
actived,
|
||||
allMenus,
|
||||
sidebarMenus,
|
||||
sidebarMenusFirstDeepestPath,
|
||||
defaultOpenedPaths,
|
||||
generateMenusAtFront,
|
||||
generateMenusAtBack,
|
||||
setActived,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+217
-214
@@ -1,238 +1,241 @@
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import useSettingsStore from './settings'
|
||||
import useUserStore from './user'
|
||||
import api from '@/api'
|
||||
import { resolveRoutePath } from '@/utils'
|
||||
import { systemRoutes } from '@/router/routes'
|
||||
import type { Route } from '@/global'
|
||||
|
||||
function hasPermission(permissions: string[], route: Route.recordMainRaw | Route.recordRaw) {
|
||||
let isAuth = false
|
||||
if (route.meta?.auth) {
|
||||
isAuth = permissions.some((auth) => {
|
||||
return typeof route.meta.auth === 'string'
|
||||
? route.meta.auth === auth
|
||||
: typeof route.meta.auth === 'object'
|
||||
? route.meta.auth.includes(auth)
|
||||
: false
|
||||
})
|
||||
}
|
||||
else {
|
||||
isAuth = true
|
||||
}
|
||||
return isAuth
|
||||
}
|
||||
|
||||
function filterAsyncRoutes<T extends Route.recordMainRaw[] | Route.recordRaw[]>(routes: T, permissions: string[]): T {
|
||||
const res: any = []
|
||||
routes.forEach((route) => {
|
||||
if (hasPermission(permissions, route)) {
|
||||
const tmpRoute = cloneDeep(route)
|
||||
if (tmpRoute.children) {
|
||||
tmpRoute.children = filterAsyncRoutes(tmpRoute.children, permissions)
|
||||
tmpRoute.children.length && res.push(tmpRoute)
|
||||
}
|
||||
else {
|
||||
res.push(tmpRoute)
|
||||
}
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
|
||||
return routes.map((route: any) => {
|
||||
switch (route.component) {
|
||||
case 'Layout':
|
||||
route.component = () => import('@/layouts/index.vue')
|
||||
break
|
||||
default:
|
||||
if (route.component)
|
||||
route.component = views[`../../views/${route.component}`]
|
||||
}
|
||||
if (route.children)
|
||||
route.children = formatBackRoutes(route.children, views)
|
||||
|
||||
return route
|
||||
})
|
||||
}
|
||||
|
||||
// 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平
|
||||
function flatAsyncRoutes<T extends Route.recordRaw>(routes: T): T {
|
||||
if (routes.children) {
|
||||
routes.children = flatAsyncRoutesRecursive(routes.children, [{
|
||||
path: routes.path,
|
||||
title: routes.meta.title,
|
||||
hide: !routes.meta.breadcrumb && routes.meta.breadcrumb === false,
|
||||
}], routes.path)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
function flatAsyncRoutesRecursive(routes: Route.recordRaw[], breadcrumb: Route.breadcrumb[] = [], baseUrl = ''): Route.recordRaw[] {
|
||||
const res: Route.recordRaw[] = []
|
||||
routes.forEach((route) => {
|
||||
if (route.children) {
|
||||
const childrenBaseUrl = resolveRoutePath(baseUrl, route.path)
|
||||
const tmpBreadcrumb = cloneDeep(breadcrumb)
|
||||
if (route.meta.breadcrumb !== false) {
|
||||
tmpBreadcrumb.push({
|
||||
path: childrenBaseUrl,
|
||||
title: route.meta.title,
|
||||
hide: !route.meta.breadcrumb && route.meta.breadcrumb === false,
|
||||
})
|
||||
}
|
||||
const tmpRoute = cloneDeep(route)
|
||||
tmpRoute.path = childrenBaseUrl
|
||||
tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
|
||||
delete tmpRoute.children
|
||||
res.push(tmpRoute)
|
||||
const childrenRoutes = flatAsyncRoutesRecursive(route.children, tmpBreadcrumb, childrenBaseUrl)
|
||||
childrenRoutes.forEach((item) => {
|
||||
// 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复
|
||||
if (res.some(v => v.path === item.path)) {
|
||||
res.forEach((v, i) => {
|
||||
if (v.path === item.path)
|
||||
res[i] = item
|
||||
})
|
||||
}
|
||||
else {
|
||||
res.push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
const tmpRoute = cloneDeep(route)
|
||||
tmpRoute.path = resolveRoutePath(baseUrl, tmpRoute.path)
|
||||
// 处理面包屑导航
|
||||
const tmpBreadcrumb = cloneDeep(breadcrumb)
|
||||
tmpBreadcrumb.push({
|
||||
path: tmpRoute.path,
|
||||
title: tmpRoute.meta.title,
|
||||
hide: !tmpRoute.meta.breadcrumb && tmpRoute.meta.breadcrumb === false,
|
||||
})
|
||||
tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
|
||||
res.push(tmpRoute)
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
import type { Route } from '#/global'
|
||||
|
||||
const useRouteStore = defineStore(
|
||||
// 唯一ID
|
||||
'route',
|
||||
{
|
||||
state: () => ({
|
||||
isGenerate: false,
|
||||
routes: [] as Route.recordMainRaw[],
|
||||
fileSystemRoutes: [] as Route.recordRaw[],
|
||||
currentRemoveRoutes: [] as Function[],
|
||||
}),
|
||||
getters: {
|
||||
// 扁平化路由(将三级及以上路由数据拍平成二级)
|
||||
flatRoutes: (state) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const routes: Route.recordRaw[] = []
|
||||
if (state.routes) {
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
state.routes.forEach((item) => {
|
||||
routes.push(...cloneDeep(item.children))
|
||||
() => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const isGenerate = ref(false)
|
||||
const routes = ref<Route.recordMainRaw[]>([])
|
||||
const fileSystemRoutes = ref<RouteRecordRaw[]>([])
|
||||
const currentRemoveRoutes = ref<Function[]>([])
|
||||
|
||||
// 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平
|
||||
function flatAsyncRoutes<T extends RouteRecordRaw>(routes: T): T {
|
||||
if (routes.children) {
|
||||
routes.children = flatAsyncRoutesRecursive(routes.children, [{
|
||||
path: routes.path,
|
||||
title: routes.meta?.title,
|
||||
hide: !routes.meta?.breadcrumb && routes.meta?.breadcrumb === false,
|
||||
}], routes.path)
|
||||
}
|
||||
return routes
|
||||
}
|
||||
function flatAsyncRoutesRecursive(routes: RouteRecordRaw[], breadcrumb: Route.breadcrumb[] = [], baseUrl = ''): RouteRecordRaw[] {
|
||||
const res: RouteRecordRaw[] = []
|
||||
routes.forEach((route) => {
|
||||
if (route.children) {
|
||||
const childrenBaseUrl = resolveRoutePath(baseUrl, route.path)
|
||||
const tmpBreadcrumb = cloneDeep(breadcrumb)
|
||||
if (route.meta?.breadcrumb !== false) {
|
||||
tmpBreadcrumb.push({
|
||||
path: childrenBaseUrl,
|
||||
title: route.meta?.title,
|
||||
hide: !route.meta?.breadcrumb,
|
||||
})
|
||||
routes.forEach(item => flatAsyncRoutes(item))
|
||||
}
|
||||
else {
|
||||
routes.push(...cloneDeep(state.fileSystemRoutes))
|
||||
const tmpRoute = cloneDeep(route)
|
||||
tmpRoute.path = childrenBaseUrl
|
||||
if (!tmpRoute.meta) {
|
||||
tmpRoute.meta = {}
|
||||
}
|
||||
}
|
||||
return routes
|
||||
},
|
||||
flatSystemRoutes: () => {
|
||||
const routes = [...systemRoutes]
|
||||
routes.forEach(item => flatAsyncRoutes(item))
|
||||
return routes
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 根据权限动态生成路由(前端生成)
|
||||
generateRoutesAtFront(asyncRoutes: Route.recordMainRaw[]) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
this.isGenerate = true
|
||||
this.routes = accessedRoutes.filter(item => item.children?.length !== 0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 根据权限动态生成路由(后端获取)
|
||||
generateRoutesAtBack() {
|
||||
return new Promise<void>((resolve) => {
|
||||
api.get('route/list', {
|
||||
baseURL: '/mock/',
|
||||
}).then(async (res) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
const asyncRoutes = formatBackRoutes(res.data)
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
|
||||
delete tmpRoute.children
|
||||
res.push(tmpRoute)
|
||||
const childrenRoutes = flatAsyncRoutesRecursive(route.children, tmpBreadcrumb, childrenBaseUrl)
|
||||
childrenRoutes.forEach((item) => {
|
||||
// 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复
|
||||
if (res.some(v => v.path === item.path)) {
|
||||
res.forEach((v, i) => {
|
||||
if (v.path === item.path) {
|
||||
res[i] = item
|
||||
}
|
||||
})
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
res.push(item)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
this.isGenerate = true
|
||||
this.routes = accessedRoutes.filter(item => item.children.length !== 0)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
else {
|
||||
const tmpRoute = cloneDeep(route)
|
||||
tmpRoute.path = resolveRoutePath(baseUrl, tmpRoute.path)
|
||||
// 处理面包屑导航
|
||||
const tmpBreadcrumb = cloneDeep(breadcrumb)
|
||||
tmpBreadcrumb.push({
|
||||
path: tmpRoute.path,
|
||||
title: tmpRoute.meta?.title,
|
||||
hide: !tmpRoute.meta?.breadcrumb && tmpRoute.meta?.breadcrumb === false,
|
||||
})
|
||||
if (!tmpRoute.meta) {
|
||||
tmpRoute.meta = {}
|
||||
}
|
||||
tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
|
||||
res.push(tmpRoute)
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
// 扁平化路由(将三级及以上路由数据拍平成二级)
|
||||
const flatRoutes = computed(() => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const returnRoutes: RouteRecordRaw[] = []
|
||||
if (routes.value) {
|
||||
if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
|
||||
routes.value.forEach((item) => {
|
||||
returnRoutes.push(...cloneDeep(item.children))
|
||||
})
|
||||
returnRoutes.forEach(item => flatAsyncRoutes(item))
|
||||
}
|
||||
else {
|
||||
returnRoutes.push(...cloneDeep(fileSystemRoutes.value))
|
||||
}
|
||||
}
|
||||
return returnRoutes
|
||||
})
|
||||
const flatSystemRoutes = computed(() => {
|
||||
const routes = [...systemRoutes]
|
||||
routes.forEach(item => flatAsyncRoutes(item))
|
||||
return routes
|
||||
})
|
||||
|
||||
// 判断是否有权限
|
||||
function hasPermission(permissions: string[], route: Route.recordMainRaw | RouteRecordRaw) {
|
||||
let isAuth = false
|
||||
if (route.meta?.auth) {
|
||||
isAuth = permissions.some((auth) => {
|
||||
return typeof route.meta?.auth === 'string'
|
||||
? route.meta.auth === auth
|
||||
: typeof route.meta?.auth === 'object'
|
||||
? route.meta.auth.includes(auth)
|
||||
: false
|
||||
})
|
||||
},
|
||||
// 根据权限动态生成路由(文件系统生成)
|
||||
generateRoutesAtFilesystem(asyncRoutes: Route.recordRaw[]) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
}
|
||||
else {
|
||||
isAuth = true
|
||||
}
|
||||
return isAuth
|
||||
}
|
||||
// 根据权限过滤路由
|
||||
function filterAsyncRoutes<T extends Route.recordMainRaw[] | RouteRecordRaw[]>(routes: T, permissions: string[]): T {
|
||||
const res: any = []
|
||||
routes.forEach((route) => {
|
||||
if (hasPermission(permissions, route)) {
|
||||
const tmpRoute = cloneDeep(route)
|
||||
if (tmpRoute.children) {
|
||||
tmpRoute.children = filterAsyncRoutes(tmpRoute.children, permissions)
|
||||
tmpRoute.children.length && res.push(tmpRoute)
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
res.push(tmpRoute)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
this.isGenerate = true
|
||||
this.fileSystemRoutes = accessedRoutes.filter(item => item.children?.length !== 0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 记录 accessRoutes 路由,用于登出时删除路由
|
||||
setCurrentRemoveRoutes(routes: Function[]) {
|
||||
this.currentRemoveRoutes = routes
|
||||
},
|
||||
// 清空动态路由
|
||||
removeRoutes() {
|
||||
this.isGenerate = false
|
||||
this.routes = []
|
||||
this.currentRemoveRoutes.forEach((removeRoute) => {
|
||||
removeRoute()
|
||||
})
|
||||
this.currentRemoveRoutes = []
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
// 根据权限动态生成路由(前端生成)
|
||||
async function generateRoutesAtFront(asyncRoutes: Route.recordMainRaw[]) {
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
isGenerate.value = true
|
||||
routes.value = accessedRoutes.filter(item => item.children?.length !== 0) as any
|
||||
}
|
||||
// 格式化后端路由数据
|
||||
function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
|
||||
return routes.map((route: any) => {
|
||||
switch (route.component) {
|
||||
case 'Layout':
|
||||
route.component = () => import('@/layouts/index.vue')
|
||||
break
|
||||
default:
|
||||
if (route.component) {
|
||||
route.component = views[`../../views/${route.component}`]
|
||||
}
|
||||
}
|
||||
if (route.children) {
|
||||
route.children = formatBackRoutes(route.children, views)
|
||||
}
|
||||
return route
|
||||
})
|
||||
}
|
||||
// 根据权限动态生成路由(后端获取)
|
||||
async function generateRoutesAtBack() {
|
||||
await api.get('route/list', {
|
||||
baseURL: '/mock/',
|
||||
}).then(async (res) => {
|
||||
const asyncRoutes = formatBackRoutes(res.data)
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
isGenerate.value = true
|
||||
routes.value = accessedRoutes.filter(item => item.children.length !== 0) as any
|
||||
}).catch(() => {})
|
||||
}
|
||||
// 根据权限动态生成路由(文件系统生成)
|
||||
async function generateRoutesAtFilesystem(asyncRoutes: RouteRecordRaw[]) {
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
}
|
||||
else {
|
||||
accessedRoutes = cloneDeep(asyncRoutes)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
isGenerate.value = true
|
||||
fileSystemRoutes.value = accessedRoutes.filter(item => item.children?.length !== 0) as any
|
||||
}
|
||||
// 记录 accessRoutes 路由,用于登出时删除路由
|
||||
function setCurrentRemoveRoutes(routes: Function[]) {
|
||||
currentRemoveRoutes.value = routes
|
||||
}
|
||||
// 清空动态路由
|
||||
function removeRoutes() {
|
||||
isGenerate.value = false
|
||||
routes.value = []
|
||||
currentRemoveRoutes.value.forEach((removeRoute) => {
|
||||
removeRoute()
|
||||
})
|
||||
currentRemoveRoutes.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
isGenerate,
|
||||
routes,
|
||||
fileSystemRoutes,
|
||||
flatRoutes,
|
||||
flatSystemRoutes,
|
||||
generateRoutesAtFront,
|
||||
generateRoutesAtBack,
|
||||
generateRoutesAtFilesystem,
|
||||
setCurrentRemoveRoutes,
|
||||
removeRoutes,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -1,71 +1,106 @@
|
||||
import type { RecursiveRequired, Settings } from '@/global'
|
||||
import settings from '@/settings'
|
||||
import { defaultsDeep } from 'lodash-es'
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
import type { RecursiveRequired, Settings } from '#/global'
|
||||
import settingsCustom from '@/settings'
|
||||
import settingsDefault from '@/settings.default'
|
||||
|
||||
interface SettingsStore extends RecursiveRequired<Settings.all> {
|
||||
subMenuCollapseLastStatus: boolean
|
||||
mode: 'pc' | 'mobile'
|
||||
title: string
|
||||
}
|
||||
|
||||
// 合并配置
|
||||
Object.keys(settingsDefault).forEach((key) => {
|
||||
Object.assign(settingsDefault[key as keyof Settings.all], settings[key as keyof Settings.all])
|
||||
})
|
||||
|
||||
const useSettingsStore = defineStore(
|
||||
// 唯一ID
|
||||
'settings',
|
||||
{
|
||||
state: (): SettingsStore => ({
|
||||
...settingsDefault,
|
||||
// 侧边栏是否收起(用于记录 pc 模式下最后的状态)
|
||||
subMenuCollapseLastStatus: settingsDefault.menu.subMenuCollapse,
|
||||
// 显示模式,支持:mobile、pc
|
||||
mode: 'pc',
|
||||
// 页面标题
|
||||
title: '',
|
||||
}),
|
||||
actions: {
|
||||
// 设置网页标题
|
||||
setTitle(title: string) {
|
||||
this.title = title
|
||||
},
|
||||
// 设置访问模式
|
||||
setMode(width: number) {
|
||||
if (this.layout.enableMobileAdaptation) {
|
||||
// 先判断 UA 是否为移动端设备(手机&平板)
|
||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
this.mode = 'mobile'
|
||||
}
|
||||
else {
|
||||
// 如果为桌面设备,再根据页面宽度判断是否需要切换为移动端展示
|
||||
if (width < 992)
|
||||
this.mode = 'mobile'
|
||||
() => {
|
||||
const mergeSettings: RecursiveRequired<Settings.all> = defaultsDeep(settingsCustom, settingsDefault)
|
||||
const settings = ref(mergeSettings)
|
||||
watch(() => settings.value.app.colorScheme, (val) => {
|
||||
if (val === '') {
|
||||
val = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
switch (val) {
|
||||
case 'dark':
|
||||
document.documentElement.classList.add('dark')
|
||||
break
|
||||
case 'light':
|
||||
document.documentElement.classList.remove('dark')
|
||||
break
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
watch(() => settings.value.menu.menuMode, (val) => {
|
||||
document.body.setAttribute('data-menu-mode', val)
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
else
|
||||
this.mode = 'pc'
|
||||
}
|
||||
// 页面标题
|
||||
const title = ref<RouteMeta['title']>()
|
||||
// 设置网页标题
|
||||
function setTitle(_title: RouteMeta['title']) {
|
||||
title.value = _title
|
||||
}
|
||||
|
||||
// 显示模式
|
||||
const mode = ref<'pc' | 'mobile'>('pc')
|
||||
// 设置访问模式
|
||||
function setMode(width: number) {
|
||||
if (settings.value.layout.enableMobileAdaptation) {
|
||||
// 先判断 UA 是否为移动端设备(手机&平板)
|
||||
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
||||
mode.value = 'mobile'
|
||||
}
|
||||
else {
|
||||
this.mode = 'pc'
|
||||
// 如果为桌面设备,再根据页面宽度判断是否需要切换为移动端展示
|
||||
mode.value = width < 992 ? 'mobile' : 'pc'
|
||||
}
|
||||
},
|
||||
// 切换侧边栏导航展开/收起
|
||||
toggleSidebarCollapse() {
|
||||
this.menu.subMenuCollapse = !this.menu.subMenuCollapse
|
||||
if (this.mode === 'pc')
|
||||
this.subMenuCollapseLastStatus = !this.subMenuCollapseLastStatus
|
||||
},
|
||||
// 设置主题颜色模式
|
||||
setColorScheme(color: Required<Settings.app>['colorScheme']) {
|
||||
this.app.colorScheme = color
|
||||
},
|
||||
// 更新应用配置
|
||||
updateSettings(data: Settings.all) {
|
||||
Object.assign(this, data)
|
||||
},
|
||||
},
|
||||
}
|
||||
else {
|
||||
mode.value = 'pc'
|
||||
}
|
||||
}
|
||||
|
||||
// 侧边栏是否收起(用于记录 pc 模式下最后的状态)
|
||||
const subMenuCollapseLastStatus = ref(mergeSettings.menu.subMenuCollapse)
|
||||
// 切换侧边栏导航展开/收起
|
||||
function toggleSidebarCollapse() {
|
||||
settings.value.menu.subMenuCollapse = !settings.value.menu.subMenuCollapse
|
||||
if (mode.value === 'pc') {
|
||||
subMenuCollapseLastStatus.value = !subMenuCollapseLastStatus.value
|
||||
}
|
||||
}
|
||||
|
||||
watch(mode, (val) => {
|
||||
switch (val) {
|
||||
case 'pc':
|
||||
settings.value.menu.subMenuCollapse = subMenuCollapseLastStatus.value
|
||||
break
|
||||
case 'mobile':
|
||||
settings.value.menu.subMenuCollapse = true
|
||||
break
|
||||
}
|
||||
document.body.setAttribute('data-mode', val)
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
// 设置主题颜色模式
|
||||
function setColorScheme(color: Required<Settings.app>['colorScheme']) {
|
||||
settings.value.app.colorScheme = color
|
||||
}
|
||||
// 更新应用配置
|
||||
function updateSettings(data: Settings.all) {
|
||||
settings.value = defaultsDeep(data, settings.value)
|
||||
}
|
||||
|
||||
return {
|
||||
settings,
|
||||
title,
|
||||
setTitle,
|
||||
mode,
|
||||
setMode,
|
||||
subMenuCollapseLastStatus,
|
||||
toggleSidebarCollapse,
|
||||
setColorScheme,
|
||||
updateSettings,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+80
-85
@@ -5,92 +5,87 @@ import api from '@/api'
|
||||
const useUserStore = defineStore(
|
||||
// 唯一ID
|
||||
'user',
|
||||
{
|
||||
state: () => ({
|
||||
account: localStorage.account || '',
|
||||
token: localStorage.token || '',
|
||||
failure_time: localStorage.failure_time || '',
|
||||
permissions: [] as string[],
|
||||
}),
|
||||
getters: {
|
||||
isLogin: (state) => {
|
||||
let retn = false
|
||||
if (state.token) {
|
||||
if (new Date().getTime() < parseInt(state.failure_time) * 1000)
|
||||
retn = true
|
||||
() => {
|
||||
const routeStore = useRouteStore()
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
const account = ref(localStorage.account ?? '')
|
||||
const token = ref(localStorage.token ?? '')
|
||||
const failure_time = ref(localStorage.failure_time ?? '')
|
||||
const permissions = ref<string[]>([])
|
||||
const isLogin = computed(() => {
|
||||
let retn = false
|
||||
if (token.value) {
|
||||
if (new Date().getTime() < parseInt(failure_time.value) * 1000) {
|
||||
retn = true
|
||||
}
|
||||
return retn
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
login(data: {
|
||||
account: string
|
||||
password: string
|
||||
}) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
// 通过 mock 进行登录
|
||||
api.post('member/login', data, {
|
||||
baseURL: '/mock/',
|
||||
}).then((res) => {
|
||||
localStorage.setItem('account', res.data.account)
|
||||
localStorage.setItem('token', res.data.token)
|
||||
localStorage.setItem('failure_time', res.data.failure_time)
|
||||
this.account = res.data.account
|
||||
this.token = res.data.token
|
||||
this.failure_time = res.data.failure_time
|
||||
resolve()
|
||||
}).catch((error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
logout() {
|
||||
return new Promise<void>((resolve) => {
|
||||
const routeStore = useRouteStore()
|
||||
const menuStore = useMenuStore()
|
||||
localStorage.removeItem('account')
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('failure_time')
|
||||
this.account = ''
|
||||
this.token = ''
|
||||
this.failure_time = ''
|
||||
routeStore.removeRoutes()
|
||||
menuStore.setActived(0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 获取我的权限
|
||||
getPermissions() {
|
||||
return new Promise<string[]>((resolve) => {
|
||||
// 通过 mock 获取权限
|
||||
api.get('member/permission', {
|
||||
baseURL: '/mock/',
|
||||
params: {
|
||||
account: this.account,
|
||||
},
|
||||
}).then((res) => {
|
||||
this.permissions = res.data.permissions
|
||||
resolve(res.data.permissions)
|
||||
})
|
||||
})
|
||||
},
|
||||
editPassword(data: {
|
||||
password: string
|
||||
newpassword: string
|
||||
}) {
|
||||
return new Promise<void>((resolve) => {
|
||||
api.post('member/edit/password', {
|
||||
account: this.account,
|
||||
password: data.password,
|
||||
newpassword: data.newpassword,
|
||||
}, {
|
||||
baseURL: '/mock/',
|
||||
}).then(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
return retn
|
||||
})
|
||||
|
||||
// 登录
|
||||
async function login(data: {
|
||||
account: string
|
||||
password: string
|
||||
}) {
|
||||
// 通过 mock 进行登录
|
||||
const res = await api.post('member/login', data, {
|
||||
baseURL: '/mock/',
|
||||
})
|
||||
localStorage.setItem('account', res.data.account)
|
||||
localStorage.setItem('token', res.data.token)
|
||||
localStorage.setItem('failure_time', res.data.failure_time)
|
||||
account.value = res.data.account
|
||||
token.value = res.data.token
|
||||
failure_time.value = res.data.failure_time
|
||||
}
|
||||
// 登出
|
||||
async function logout() {
|
||||
localStorage.removeItem('account')
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('failure_time')
|
||||
account.value = ''
|
||||
token.value = ''
|
||||
failure_time.value = ''
|
||||
routeStore.removeRoutes()
|
||||
menuStore.setActived(0)
|
||||
}
|
||||
// 获取我的权限
|
||||
async function getPermissions() {
|
||||
// 通过 mock 获取权限
|
||||
const res = await api.get('member/permission', {
|
||||
baseURL: '/mock/',
|
||||
params: {
|
||||
account: account.value,
|
||||
},
|
||||
})
|
||||
permissions.value = res.data.permissions
|
||||
return permissions.value
|
||||
}
|
||||
// 修改密码
|
||||
async function editPassword(data: {
|
||||
password: string
|
||||
newpassword: string
|
||||
}) {
|
||||
await api.post('member/edit/password', {
|
||||
account: account.value,
|
||||
password: data.password,
|
||||
newpassword: data.newpassword,
|
||||
}, {
|
||||
baseURL: '/mock/',
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
account,
|
||||
token,
|
||||
permissions,
|
||||
isLogin,
|
||||
login,
|
||||
logout,
|
||||
getPermissions,
|
||||
editPassword,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
+8
-5
@@ -2,7 +2,6 @@
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
@@ -30,6 +29,8 @@ declare global {
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
@@ -46,6 +47,7 @@ declare global {
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const resolveDirective: typeof import('vue')['resolveDirective']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
@@ -58,12 +60,13 @@ declare global {
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useAuth: typeof import('./utils/composables/useAuth')['default']
|
||||
const useAuth: typeof import('../utils/composables/useAuth')['default']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useCurrentInstance: typeof import('./utils/composables/useCurrentInstance')['default']
|
||||
const useMainPage: typeof import('./utils/composables/useMainPage')['default']
|
||||
const useMenu: typeof import('./utils/composables/useMenu')['default']
|
||||
const useGlobalProperties: typeof import('../utils/composables/useGlobalProperties')['default']
|
||||
const useLink: typeof import('vue-router')['useLink']
|
||||
const useMainPage: typeof import('../utils/composables/useMainPage')['default']
|
||||
const useMenu: typeof import('../utils/composables/useMenu')['default']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
Auth: typeof import('./../components/Auth/index.vue')['default']
|
||||
AuthAll: typeof import('./../components/AuthAll/index.vue')['default']
|
||||
BatchActionBar: typeof import('./../components/BatchActionBar/index.vue')['default']
|
||||
Copyright: typeof import('./../components/Copyright/index.vue')['default']
|
||||
Editor: typeof import('./../components/Editor/index.vue')['default']
|
||||
FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
|
||||
FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default']
|
||||
ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
|
||||
ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default']
|
||||
ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']
|
||||
PageHeader: typeof import('./../components/PageHeader/index.vue')['default']
|
||||
PageMain: typeof import('./../components/PageMain/index.vue')['default']
|
||||
PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SearchBar: typeof import('./../components/SearchBar/index.vue')['default']
|
||||
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
|
||||
SystemInfo: typeof import('./../components/SystemInfo/index.vue')['default']
|
||||
Trend: typeof import('./../components/Trend/index.vue')['default']
|
||||
}
|
||||
}
|
||||
+22
-19
@@ -4,6 +4,9 @@ import type { RouteRecordRaw, RouteMeta, NavigationGuard } from 'vue-router'
|
||||
type RecursiveRequired<T> = {
|
||||
[P in keyof T]-?: RecursiveRequired<T[P]>
|
||||
}
|
||||
type RecursivePartial<T> = {
|
||||
[P in keyof T]?: RecursivePartial<T[P]>
|
||||
}
|
||||
|
||||
declare namespace Settings {
|
||||
interface app {
|
||||
@@ -49,15 +52,15 @@ declare namespace Settings {
|
||||
*/
|
||||
routeBaseOn?: 'frontend' | 'backend' | 'filesystem'
|
||||
}
|
||||
interface dashboard {
|
||||
interface home {
|
||||
/**
|
||||
* 是否开启控制台页面
|
||||
* 是否开启主页页面
|
||||
* @默认值 `true`
|
||||
*/
|
||||
enable?: boolean
|
||||
/**
|
||||
* 控制台名称
|
||||
* @默认值 `'控制台'`
|
||||
* 主页名称
|
||||
* @默认值 `'主页'`
|
||||
*/
|
||||
title?: string
|
||||
}
|
||||
@@ -110,10 +113,12 @@ declare namespace Settings {
|
||||
}
|
||||
interface topbar {
|
||||
/**
|
||||
* 是否固定顶栏
|
||||
* @默认值 `true`
|
||||
* 模式
|
||||
* @默认值 `'static'` 静止,跟随页面滚动
|
||||
* @可选值 `'fixed'` 固定,不跟随页面滚动,始终固定在顶部
|
||||
* @可选值 `'sticky'` 粘性,页面往下滚动时隐藏,往上滚动时显示
|
||||
*/
|
||||
fixed?: boolean
|
||||
mode?: 'static' | 'fixed' | 'sticky'
|
||||
}
|
||||
interface toolbar {
|
||||
/**
|
||||
@@ -186,8 +191,8 @@ declare namespace Settings {
|
||||
interface all {
|
||||
/** 应用设置 */
|
||||
app?: app
|
||||
/** 控制台设置 */
|
||||
dashboard?: dashboard
|
||||
/** 主页设置 */
|
||||
home?: home
|
||||
/** 布局设置 */
|
||||
layout?: layout
|
||||
/** 导航栏设置 */
|
||||
@@ -207,7 +212,7 @@ declare namespace Settings {
|
||||
|
||||
declare module 'vue-router' {
|
||||
interface RouteMeta {
|
||||
title: string | Function
|
||||
title?: string | Function
|
||||
i18n?: string
|
||||
icon?: string
|
||||
activeIcon?: string
|
||||
@@ -217,17 +222,14 @@ declare module 'vue-router' {
|
||||
breadcrumb?: boolean
|
||||
activeMenu?: string
|
||||
cache?: boolean | string | string[]
|
||||
link?: string
|
||||
breadcrumbNeste?: Route.breadcrumb[]
|
||||
}
|
||||
}
|
||||
|
||||
declare namespace Route {
|
||||
interface recordRaw extends Omit<RouteRecordRaw, 'meta' | 'children'> {
|
||||
meta: RouteMeta
|
||||
children?: recordRaw[]
|
||||
}
|
||||
interface recordMainRaw {
|
||||
meta: {
|
||||
meta?: {
|
||||
title?: string | Function
|
||||
icon?: string
|
||||
auth?: string | string[]
|
||||
@@ -236,7 +238,7 @@ declare namespace Route {
|
||||
}
|
||||
interface breadcrumb {
|
||||
path: string
|
||||
title: string | Function
|
||||
title?: string | Function
|
||||
hide: boolean
|
||||
}
|
||||
}
|
||||
@@ -245,18 +247,19 @@ declare namespace Menu {
|
||||
/** 原始 */
|
||||
interface recordRaw {
|
||||
path?: string
|
||||
meta: {
|
||||
title: string
|
||||
meta?: {
|
||||
title?: string
|
||||
icon?: string
|
||||
defaultOpened?: boolean
|
||||
auth?: string | string[]
|
||||
sidebar?: boolean
|
||||
link?: string
|
||||
}
|
||||
children?: recordRaw[]
|
||||
}
|
||||
/** 主导航 */
|
||||
interface recordMainRaw {
|
||||
meta: {
|
||||
meta?: {
|
||||
title?: string
|
||||
icon?: string
|
||||
auth?: string | string[]
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
declare interface Window {
|
||||
webkitDevicePixelRatio: any
|
||||
mozDevicePixelRatio: any
|
||||
}
|
||||
|
||||
declare module '*.vue' {
|
||||
import { type DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
declare const __SYSTEM_INFO__: {
|
||||
pkg: {
|
||||
version: string
|
||||
dependencies: Recordable<string>
|
||||
devDependencies: Recordable<string>
|
||||
}
|
||||
lastBuildTime: string
|
||||
}
|
||||
declare interface Window {
|
||||
webkitDevicePixelRatio: any
|
||||
mozDevicePixelRatio: any
|
||||
}
|
||||
|
||||
declare module '*.vue' {
|
||||
import { type DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
declare const __SYSTEM_INFO__: {
|
||||
pkg: {
|
||||
version: string
|
||||
dependencies: Recordable<string>
|
||||
devDependencies: Recordable<string>
|
||||
}
|
||||
lastBuildTime: string
|
||||
}
|
||||
@@ -5,11 +5,12 @@ export default function useAuth() {
|
||||
function hasPermission(permission: string) {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
if (settingsStore.app.enablePermission)
|
||||
if (settingsStore.settings.app.enablePermission) {
|
||||
return userStore.permissions.includes(permission)
|
||||
|
||||
else
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function auth(value: string | string[]) {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { ComponentInternalInstance } from 'vue'
|
||||
|
||||
export default function useCurrentInstance() {
|
||||
export default function useGlobalProperties() {
|
||||
const { appContext } = getCurrentInstance() as ComponentInternalInstance
|
||||
return appContext.config.globalProperties
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import router from '@/router'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import useMenuStore from '@/store/modules/menu'
|
||||
import { isExternalLink } from '@/utils'
|
||||
|
||||
export default function useMenu() {
|
||||
const settingsStore = useSettingsStore()
|
||||
@@ -9,12 +8,8 @@ export default function useMenu() {
|
||||
|
||||
function switchTo(index: number | string) {
|
||||
menuStore.setActived(index)
|
||||
if (settingsStore.menu.switchMainMenuAndPageJump) {
|
||||
if (isExternalLink(menuStore.sidebarMenusFirstDeepestPath))
|
||||
window.open(menuStore.sidebarMenusFirstDeepestPath, '_blank')
|
||||
|
||||
else
|
||||
router.push(menuStore.sidebarMenusFirstDeepestPath)
|
||||
if (settingsStore.settings.menu.switchMainMenuAndPageJump) {
|
||||
router.push(menuStore.sidebarMenusFirstDeepestPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,16 @@ export default function directive(app: App) {
|
||||
// 注册 v-auth 和 v-auth-all 指令
|
||||
app.directive('auth', {
|
||||
mounted: (el, binding) => {
|
||||
if (!useAuth().auth(binding.value))
|
||||
if (!useAuth().auth(binding.value)) {
|
||||
el.remove()
|
||||
}
|
||||
},
|
||||
})
|
||||
app.directive('auth-all', {
|
||||
mounted: (el, binding) => {
|
||||
if (!useAuth().authAll(binding.value))
|
||||
if (!useAuth().authAll(binding.value)) {
|
||||
el.remove()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+2
-12
@@ -1,15 +1,5 @@
|
||||
import path from 'path-browserify'
|
||||
|
||||
export function isExternalLink(path: string) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path)
|
||||
}
|
||||
|
||||
export function resolveRoutePath(basePath: string, routePath: string) {
|
||||
if (isExternalLink(routePath))
|
||||
return routePath
|
||||
|
||||
if (isExternalLink(basePath))
|
||||
return basePath
|
||||
|
||||
return basePath ? path.resolve(basePath, routePath) : routePath
|
||||
export function resolveRoutePath(basePath: string, routePath?: string) {
|
||||
return basePath ? path.resolve(basePath, routePath ?? '') : routePath ?? ''
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user