mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-07 01:03:40 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20f012323e | ||
|
|
b661e0e834 | ||
|
|
5bf2664a04 | ||
|
|
3fded8abac | ||
|
|
d707522b6c | ||
|
|
a177b8c6a6 | ||
|
|
ac9b5d5b3b | ||
|
|
f0582cf721 | ||
|
|
7be9641d46 | ||
|
|
7fb2864072 | ||
|
|
658b51a747 | ||
|
|
b5832c3256 | ||
|
|
ba6eb5480a | ||
|
|
cf73c14561 | ||
|
|
e712a75a7f | ||
|
|
8ae890213e | ||
|
|
7cc4f64274 | ||
|
|
17f0af6542 | ||
|
|
1262fe9e1d | ||
|
|
c9c69464f3 | ||
|
|
7bcdc1b4cd | ||
|
|
69665852a1 | ||
|
|
09807dd115 | ||
|
|
4daeb67e19 | ||
|
|
439d6e9f60 |
@@ -1,87 +0,0 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
|
||||
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
upload-archive:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
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@v5
|
||||
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/* /public/.github/* /public/.nojekyll'
|
||||
|
||||
- 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
|
||||
@@ -1,23 +0,0 @@
|
||||
name: repo-sync
|
||||
|
||||
on:
|
||||
# 每天 00:05 自动同步
|
||||
schedule:
|
||||
- cron: '5 0 * * *'
|
||||
# 手动触发部署
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
sync-to-gitee:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Sync to Gitee
|
||||
uses: wearerequired/git-mirror-action@master
|
||||
env:
|
||||
# 注意在 Settings->Secrets 配置 GITEE_RSA_PRIVATE_KEY
|
||||
SSH_PRIVATE_KEY: ${{ secrets.GITEE_RSA_PRIVATE_KEY }}
|
||||
with:
|
||||
# 注意替换为你的 GitHub 源仓库地址
|
||||
source-repo: git@github.com:fantastic-admin/basic.git
|
||||
# 注意替换为你的 Gitee 目标仓库地址
|
||||
destination-repo: git@gitee.com:fantastic-admin/basic.git
|
||||
Vendored
+9
-8
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"eslint.useFlatConfig": true,
|
||||
"npm.packageManager": "pnpm",
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
@@ -7,12 +8,7 @@
|
||||
"source.fixAll.stylelint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"stylelint.validate": [
|
||||
"css",
|
||||
"postcss",
|
||||
"scss",
|
||||
"vue"
|
||||
],
|
||||
"eslint.useFlatConfig": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
@@ -25,5 +21,10 @@
|
||||
"jsonc",
|
||||
"yaml"
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
"stylelint.validate": [
|
||||
"css",
|
||||
"postcss",
|
||||
"scss",
|
||||
"vue"
|
||||
]
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,12 +1,12 @@
|
||||
[中文](./README.md) | **English**
|
||||
|
||||
<p align="center">
|
||||
<img src="https://fantastic-admin.hurui.me/logo.svg" width="200" height="200" />
|
||||
<img src="https://fantastic-admin.hurui.me/logo.svg" width="200" height="200" alt="Fantastic-admin" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Fantastic-admin</h1>
|
||||
|
||||
<p align="center">A <b>out-of-the-box</b> Vue3 back-office management system framework</p>
|
||||
<p align="center">An <b>out-of-the-box</b> Vue3 management system framework</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fantastic-admin.hurui.me" target="_blank">Official Website</a>
|
||||
@@ -15,8 +15,8 @@
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<a href="###"><img src="https://img.shields.io/github/license/fantastic-admin/basic?label=License&style=flat-square" /></a>
|
||||
<a href="https://github.com/fantastic-admin/basic/releases" target="_blank"><img src="https://img.shields.io/github/v/release/fantastic-admin/basic?label=Version&style=flat-square" /></a>
|
||||
<a href="###"><img src="https://img.shields.io/github/license/fantastic-admin/basic?label=License&style=flat-square" alt="" /></a>
|
||||
<a href="https://github.com/fantastic-admin/basic/releases" target="_blank"><img src="https://img.shields.io/github/v/release/fantastic-admin/basic?label=Version&style=flat-square" alt="" /></a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
@@ -48,14 +48,14 @@
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview3.png" alt="" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview5.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview6.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview4.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview5.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview6.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -98,14 +98,14 @@ If you think the Fantastic-admin framework is good, or you are already using it,
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview3.png" alt="" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview5.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview6.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview4.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview5.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview6.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -121,9 +121,9 @@ If you think the Fantastic-admin framework is good, or you are already using it,
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview3.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview4.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
**中文** | [English](./README.EN.md)
|
||||
|
||||
<p align="center">
|
||||
<img src="https://fantastic-admin.hurui.me/logo.svg" width="200" height="200" />
|
||||
<img src="https://fantastic-admin.hurui.me/logo.svg" width="200" height="200" alt="Fantastic-admin" />
|
||||
</p>
|
||||
|
||||
<h1 align="center">Fantastic-admin</h1>
|
||||
|
||||
<p align="center">一款<b>开箱即用</b>的 Vue3 中后台管理系统框架</p>
|
||||
<p align="center">一款<b>开箱即用</b>的 Vue3 管理系统框架</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fantastic-admin.hurui.me" target="_blank">官网</a>
|
||||
@@ -15,8 +15,8 @@
|
||||
<p>
|
||||
|
||||
<p align="center">
|
||||
<a href="###"><img src="https://img.shields.io/github/license/fantastic-admin/basic?label=%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&style=flat-square" /></a>
|
||||
<a href="https://github.com/fantastic-admin/basic/releases" target="_blank"><img src="https://img.shields.io/github/v/release/fantastic-admin/basic?label=%E5%BD%93%E5%89%8D%E7%89%88%E6%9C%AC&style=flat-square" /></a>
|
||||
<a href="###"><img src="https://img.shields.io/github/license/fantastic-admin/basic?label=%E5%BC%80%E6%BA%90%E5%8D%8F%E8%AE%AE&style=flat-square" alt="" /></a>
|
||||
<a href="https://github.com/fantastic-admin/basic/releases" target="_blank"><img src="https://img.shields.io/github/v/release/fantastic-admin/basic?label=%E5%BD%93%E5%89%8D%E7%89%88%E6%9C%AC&style=flat-square" alt="" /></a>
|
||||
</p>
|
||||
|
||||
## 特点
|
||||
@@ -48,25 +48,27 @@
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview3.png" alt="" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview5.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview6.png" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview4.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview5.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-admin.hurui.me/preview6.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 支持
|
||||
|
||||
如果觉得 Fantastic-admin 这个框架不错,或者已经在使用了,希望你可以在 **Github** 或者 **Gitee(码云)** 帮我点个 ⭐ ,这将对我是极大的鼓励。
|
||||
如果觉得 Fantastic-admin 这个框架不错,或者已经在使用了,希望你可以在 **Github** / **Gitee** / **GitCode** 帮我点个 ⭐ ,这将对我是极大的鼓励。
|
||||
|
||||
[](https://github.com/fantastic-admin/basic)
|
||||
|
||||
[](https://gitee.com/fantastic-admin/basic)
|
||||
|
||||
[](https://atomgit.com/fantastic-admin/basic)
|
||||
|
||||
<details>
|
||||
<summary>Github Stars 曲线</summary>
|
||||
|
||||
@@ -96,18 +98,18 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="3" align="center">
|
||||
一款干啥都快人一步的 Vue 中后台管理系统框架
|
||||
一款干啥都快人一步的 Vue 后台管理系统框架
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview3.png" alt="" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview5.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview6.png" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview4.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview5.png" alt="" /></td>
|
||||
<td><img src="https://one-step-admin.hurui.me/preview6.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -123,9 +125,9 @@
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview1.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview2.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview3.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview4.png" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview1.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview2.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview3.png" alt="" /></td>
|
||||
<td><img src="https://fantastic-mobile.hurui.me/preview4.png" alt="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -10,6 +10,7 @@ export default antfu(
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'e18e/prefer-static-regex': 'off',
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
'curly': ['error', 'all'],
|
||||
'ts/no-unused-expressions': ['error', {
|
||||
|
||||
+62
-61
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"version": "5.8.0",
|
||||
"packageManager": "pnpm@10.18.1",
|
||||
"version": "5.10.1",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
@@ -26,95 +26,96 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vee-validate/zod": "^4.15.1",
|
||||
"@vueuse/components": "^13.9.0",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"@vueuse/integrations": "^13.9.0",
|
||||
"axios": "^1.12.2",
|
||||
"@vueuse/components": "^14.2.1",
|
||||
"@vueuse/core": "^14.2.1",
|
||||
"@vueuse/integrations": "^14.2.1",
|
||||
"axios": "^1.14.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.20",
|
||||
"defu": "^6.1.4",
|
||||
"disable-devtool": "^0.3.9",
|
||||
"element-plus": "^2.11.4",
|
||||
"element-plus": "^2.13.6",
|
||||
"eruda": "^3.4.3",
|
||||
"es-toolkit": "^1.39.10",
|
||||
"filesize": "^11.0.13",
|
||||
"hotkeys-js": "^3.13.15",
|
||||
"lucide-vue-next": "^0.545.0",
|
||||
"es-toolkit": "^1.45.1",
|
||||
"filesize": "^11.0.15",
|
||||
"hotkeys-js": "^4.0.2",
|
||||
"lucide-vue-next": "^1.0.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"path-to-regexp": "^8.3.0",
|
||||
"pinia": "^3.0.3",
|
||||
"qs": "^6.14.0",
|
||||
"reka-ui": "^2.5.1",
|
||||
"path-to-regexp": "^8.4.0",
|
||||
"pinia": "^3.0.4",
|
||||
"qs": "^6.15.0",
|
||||
"reka-ui": "^2.9.2",
|
||||
"scule": "^1.3.0",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"ua-parser-js": "^2.0.5",
|
||||
"tailwind-merge": "^3.5.0",
|
||||
"ua-parser-js": "^2.0.9",
|
||||
"vconsole": "^3.15.1",
|
||||
"vee-validate": "^4.15.1",
|
||||
"vue": "^3.5.22",
|
||||
"vue-router": "^4.5.1",
|
||||
"vue": "^3.5.31",
|
||||
"vue-router": "^5.0.4",
|
||||
"vue-sonner": "^2.0.9",
|
||||
"zod": "^4.1.12"
|
||||
"zod": "^4.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^5.4.1",
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"@faker-js/faker": "^10.0.0",
|
||||
"@iconify/json": "^2.2.392",
|
||||
"@antfu/eslint-config": "^7.7.3",
|
||||
"@clack/prompts": "^1.1.0",
|
||||
"@faker-js/faker": "^10.4.0",
|
||||
"@iconify/json": "^2.2.456",
|
||||
"@iconify/vue": "^5.0.0",
|
||||
"@stylistic/stylelint-config": "^3.0.1",
|
||||
"@stylistic/stylelint-config": "^5.0.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/path-browserify": "^1.0.3",
|
||||
"@types/qs": "^6.14.0",
|
||||
"@unocss/eslint-plugin": "^66.5.2",
|
||||
"@unocss/preset-legacy-compat": "^66.5.2",
|
||||
"@vitejs/plugin-legacy": "^7.2.1",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.1",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"@types/qs": "^6.15.0",
|
||||
"@unocss/eslint-plugin": "^66.6.7",
|
||||
"@unocss/preset-legacy-compat": "^66.6.7",
|
||||
"@unocss/reset": "^66.6.7",
|
||||
"@vitejs/plugin-legacy": "^8.0.1",
|
||||
"@vitejs/plugin-vue": "^6.0.5",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||
"@vue/tsconfig": "^0.9.1",
|
||||
"autoprefixer": "^10.4.27",
|
||||
"boxen": "^8.0.1",
|
||||
"bumpp": "^10.3.1",
|
||||
"bumpp": "^11.0.1",
|
||||
"cz-git": "^1.12.0",
|
||||
"eslint": "^9.37.0",
|
||||
"fs-extra": "^11.3.2",
|
||||
"eslint": "^10.1.0",
|
||||
"fs-extra": "^11.3.4",
|
||||
"http-server": "^14.1.1",
|
||||
"lint-staged": "^16.2.3",
|
||||
"lint-staged": "^16.4.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"plop": "^4.0.4",
|
||||
"postcss": "^8.5.6",
|
||||
"plop": "^4.0.5",
|
||||
"postcss": "^8.5.8",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"sass-embedded": "^1.93.2",
|
||||
"sass-embedded": "^1.98.0",
|
||||
"simple-git-hooks": "^2.13.1",
|
||||
"stylelint": "^16.25.0",
|
||||
"stylelint-config-recess-order": "^7.3.0",
|
||||
"stylelint-config-standard-scss": "^16.0.0",
|
||||
"stylelint": "^17.6.0",
|
||||
"stylelint-config-recess-order": "^7.7.0",
|
||||
"stylelint-config-standard-scss": "^17.0.0",
|
||||
"stylelint-config-standard-vue": "^1.0.0",
|
||||
"stylelint-scss": "^6.12.1",
|
||||
"svgo": "^4.0.0",
|
||||
"taze": "^19.7.0",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3",
|
||||
"unocss": "^66.5.2",
|
||||
"unocss-preset-animations": "^1.2.1",
|
||||
"unplugin-auto-import": "^20.2.0",
|
||||
"unplugin-turbo-console": "^2.2.0",
|
||||
"unplugin-vue-components": "^29.1.0",
|
||||
"vite": "^7.1.9",
|
||||
"stylelint-scss": "^7.0.0",
|
||||
"svgo": "^4.0.1",
|
||||
"taze": "^19.10.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^6.0.2",
|
||||
"unocss": "^66.6.7",
|
||||
"unocss-preset-animations": "^1.3.0",
|
||||
"unplugin-auto-import": "^21.0.0",
|
||||
"unplugin-turbo-console": "^2.3.0",
|
||||
"unplugin-vue-components": "^32.0.0",
|
||||
"vite": "^8.0.3",
|
||||
"vite-plugin-app-loading": "^0.4.0",
|
||||
"vite-plugin-archiver": "^0.2.0",
|
||||
"vite-plugin-banner": "^0.8.1",
|
||||
"vite-plugin-compression2": "^2.2.1",
|
||||
"vite-plugin-compression2": "^2.5.3",
|
||||
"vite-plugin-env-parse": "^1.0.15",
|
||||
"vite-plugin-fake-server": "^2.2.0",
|
||||
"vite-plugin-pages": "^0.33.1",
|
||||
"vite-plugin-fake-server": "^2.2.3",
|
||||
"vite-plugin-pages": "^0.33.3",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-devtools": "^8.0.2",
|
||||
"vite-plugin-vue-meta-layouts": "^0.6.0",
|
||||
"vue-tsc": "^3.1.1"
|
||||
"vite-plugin-vue-devtools": "^8.1.1",
|
||||
"vite-plugin-vue-meta-layouts": "^0.6.1",
|
||||
"vue-tsc": "^3.2.6"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged",
|
||||
|
||||
Generated
+4682
-3708
File diff suppressed because it is too large
Load Diff
@@ -59,6 +59,12 @@
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.disable-color-scheme-transition-duration *,
|
||||
.disable-color-scheme-transition-duration *::before,
|
||||
.disable-color-scheme-transition-duration *::after {
|
||||
transition-duration: 0ms !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
|
||||
@@ -55,7 +55,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, errors }" name="password">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请输入原密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请输入原密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
@@ -65,7 +65,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, errors }" name="newPassword">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请输入新密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请输入新密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
@@ -75,7 +75,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, errors }" name="checkPassword">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请确认新密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请确认新密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
|
||||
@@ -83,7 +83,7 @@ function testAccount(account: string) {
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
@@ -93,7 +93,7 @@ function testAccount(account: string) {
|
||||
<FormField v-slot="{ componentField, errors }" name="password">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请输入密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请输入密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
|
||||
@@ -23,7 +23,6 @@ const form = useForm({
|
||||
validationSchema: toTypedSchema(
|
||||
z.object({
|
||||
account: z.string().min(1, '请输入用户名'),
|
||||
captcha: z.string().min(6, '请输入验证码'),
|
||||
password: z.string().min(1, '请输入密码').min(6, '密码长度为6到18位').max(18, '密码长度为6到18位'),
|
||||
checkPassword: z.string().min(1, '请再次输入密码'),
|
||||
}).refine(data => data.password === data.checkPassword, {
|
||||
@@ -33,7 +32,6 @@ const form = useForm({
|
||||
),
|
||||
initialValues: {
|
||||
account: props.account ?? '',
|
||||
captcha: '',
|
||||
password: '',
|
||||
checkPassword: '',
|
||||
},
|
||||
@@ -58,7 +56,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
@@ -68,7 +66,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, value, errors }" name="password">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请输入密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请输入密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
<FaPasswordStrength :password="value" class="mt-2" />
|
||||
@@ -81,7 +79,7 @@ const onSubmit = form.handleSubmit((values) => {
|
||||
<FormField v-slot="{ componentField, errors }" name="checkPassword">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请再次输入密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请再次输入密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
|
||||
@@ -63,7 +63,7 @@ function handleSendCaptcha() {
|
||||
<FormField v-slot="{ componentField, errors }" name="account">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="text" placeholder="请输入用户名" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
@@ -88,7 +88,7 @@ function handleSendCaptcha() {
|
||||
<FormField v-slot="{ componentField, errors }" name="newPassword">
|
||||
<FormItem class="relative pb-6 space-y-0">
|
||||
<FormControl>
|
||||
<FaInput type="password" placeholder="请输入新密码" class="w-full" :class="errors.length && 'border-destructive'" v-bind="componentField" />
|
||||
<FaInput type="password" placeholder="请输入新密码" class="w-full" :class="errors.length > 0 && 'border-destructive'" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<Transition enter-active-class="transition-opacity" enter-from-class="opacity-0" leave-active-class="transition-opacity" leave-to-class="opacity-0">
|
||||
<FormMessage class="absolute bottom-1 text-xs" />
|
||||
|
||||
@@ -19,7 +19,7 @@ const { switchTo } = useMenu()
|
||||
<component :is="useSlots('header-start')" />
|
||||
<Logo class="title" />
|
||||
<component :is="useSlots('header-after-logo')" />
|
||||
<FaScrollArea :scrollbar="false" mask horizontal gradient-color="var(--g-header-bg)" class="menu-container h-full flex-1">
|
||||
<FaScrollArea :scrollbar="false" mask horizontal gradient-color="var(--g-header-bg)" class="menu-container h-full flex-1 overscroll-contain">
|
||||
<!-- 顶部模式 -->
|
||||
<div class="menu h-full flex of-hidden transition-all">
|
||||
<template v-for="(item, index) in menuStore.allMenus" :key="index">
|
||||
@@ -29,7 +29,7 @@ const { switchTo } = useMenu()
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="item.children && item.children.length !== 0" class="group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg px-3 text-[var(--g-header-menu-color)] transition-all hover-(bg-[var(--g-header-menu-hover-bg)] text-[var(--g-header-menu-hover-color)])" :class="{
|
||||
v-if="item.children && item.children.length !== 0" class="group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg px-3 text-[var(--g-header-menu-color)] transition-colors hover-(bg-[var(--g-header-menu-hover-bg)] text-[var(--g-header-menu-hover-color)])" :class="{
|
||||
'text-[var(--g-header-menu-active-color)]! bg-[var(--g-header-menu-active-bg)]!': index === menuStore.actived,
|
||||
}" :title="typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title" @click="switchTo(index)"
|
||||
>
|
||||
|
||||
@@ -38,7 +38,7 @@ onUnmounted(() => {
|
||||
<component :is="useSlots('main-sidebar-top')" />
|
||||
<Logo :show-title="false" class="sidebar-logo" />
|
||||
<component :is="useSlots('main-sidebar-after-logo')" />
|
||||
<FaScrollArea :scrollbar="false" mask gradient-color="var(--g-main-sidebar-bg)" class="menu flex-1">
|
||||
<FaScrollArea :scrollbar="false" mask gradient-color="var(--g-main-sidebar-bg)" class="menu flex-1 overscroll-contain">
|
||||
<!-- 侧边栏模式(含主导航) -->
|
||||
<div class="w-full flex flex-col of-hidden py-1 transition-all -mt-2">
|
||||
<template v-for="(item, index) in menuStore.allMenus" :key="index">
|
||||
@@ -48,7 +48,7 @@ onUnmounted(() => {
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="item.children && item.children.length !== 0" class="group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg py-4 text-[var(--g-main-sidebar-menu-color)] transition-all hover-(bg-[var(--g-main-sidebar-menu-hover-bg)] text-[var(--g-main-sidebar-menu-hover-color)]) px-2!" :class="{
|
||||
v-if="item.children && item.children.length !== 0" class="group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg py-4 text-[var(--g-main-sidebar-menu-color)] transition-colors hover-(bg-[var(--g-main-sidebar-menu-hover-bg)] text-[var(--g-main-sidebar-menu-hover-color)]) px-2!" :class="{
|
||||
'text-[var(--g-main-sidebar-menu-active-color)]! bg-[var(--g-main-sidebar-menu-active-bg)]!': index === menuStore.actived,
|
||||
}" :title="typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title" @click="switchTo(index)"
|
||||
>
|
||||
|
||||
@@ -170,7 +170,7 @@ provide(rootMenuInjectionKey, reactive({
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('h-full w-full flex flex-col of-hidden transition-all-300', {
|
||||
:class="cn('h-full w-full flex flex-col of-hidden', {
|
||||
'flex-row w-auto': isMenuPopup && props.mode === 'horizontal',
|
||||
'py-1': props.mode === 'vertical',
|
||||
})"
|
||||
|
||||
@@ -37,7 +37,7 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="itemRef" :class="cn('menu-item relative transition-all', {
|
||||
ref="itemRef" :class="cn('menu-item relative', {
|
||||
'active': isItemActive,
|
||||
'py-1 px-2': (rootMenu.isMenuPopup && rootMenu.props.mode === 'vertical') || (rootMenu.isMenuPopup && level !== 0 && rootMenu.props.mode === 'horizontal') || !rootMenu.isMenuPopup,
|
||||
'px-1 py-2': rootMenu.isMenuPopup && level === 0 && rootMenu.props.mode === 'horizontal',
|
||||
@@ -52,7 +52,7 @@ defineExpose({
|
||||
target: item.meta?.link ? '_blank' : '_self',
|
||||
class: 'no-underline',
|
||||
}),
|
||||
}" :class="cn('group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg px-4 py-3 text-[var(--g-sub-sidebar-menu-color)] transition-all hover-(bg-[var(--g-sub-sidebar-menu-hover-bg)] text-[var(--g-sub-sidebar-menu-hover-color)])', {
|
||||
}" :class="cn('group menu-item-container relative h-full w-full flex cursor-pointer items-center justify-between gap-1 rounded-lg px-4 py-3 text-[var(--g-sub-sidebar-menu-color)] transition-colors hover-(bg-[var(--g-sub-sidebar-menu-hover-bg)] text-[var(--g-sub-sidebar-menu-hover-color)])', {
|
||||
'text-[var(--g-sub-sidebar-menu-active-color)]! bg-[var(--g-sub-sidebar-menu-active-bg)]!': isItemActive,
|
||||
'px-3': rootMenu.isMenuPopup && level === 0,
|
||||
})" :title="typeof item.meta?.title === 'function' ? item.meta?.title() : item.meta?.title" v-on="{
|
||||
|
||||
@@ -214,7 +214,7 @@ function handleMouseleave() {
|
||||
'bg-[var(--g-sub-sidebar-bg)]': rootMenu.isMenuPopup,
|
||||
'border shadow-xl fixed z-3000 w-[200px]': rootMenu.isMenuPopup,
|
||||
'mx-1': rootMenu.isMenuPopup && (rootMenu.props.mode === 'vertical' || level !== 0),
|
||||
'py-1': rootMenu.isMenuPopup,
|
||||
'py-1 overscroll-contain': rootMenu.isMenuPopup,
|
||||
})"
|
||||
>
|
||||
<template v-for="item in menu.children" :key="item.path ?? rootMenu.getUseId(item)">
|
||||
|
||||
@@ -54,7 +54,7 @@ watch(() => menuStore.actived, (val, oldVal) => {
|
||||
}"
|
||||
/>
|
||||
<component :is="useSlots('sub-sidebar-after-logo')" />
|
||||
<FaScrollArea :scrollbar="false" mask gradient-color="var(--g-sub-sidebar-bg)" class="flex-1">
|
||||
<FaScrollArea :scrollbar="false" mask gradient-color="var(--g-sub-sidebar-bg)" class="flex-1 overscroll-contain">
|
||||
<TransitionGroup :name="transitionName">
|
||||
<template v-for="(mainItem, mainIndex) in menuStore.allMenus" :key="mainIndex">
|
||||
<div v-show="mainIndex === menuStore.actived">
|
||||
|
||||
@@ -173,7 +173,12 @@ onMounted(() => {
|
||||
}
|
||||
// 切换到最后一个标签页
|
||||
case 'alt+0':
|
||||
router.push(tabbarStore.list[tabbarStore.list.length - 1].fullPath)
|
||||
{
|
||||
const last = tabbarStore.list.at(-1)
|
||||
if (last) {
|
||||
router.push(last.fullPath)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ function pathCompile(path: string) {
|
||||
<template>
|
||||
<Breadcrumb v-if="settingsStore.mode === 'pc' && settingsStore.settings.app.routeBaseOn !== 'filesystem'" class="breadcrumb whitespace-nowrap px-2">
|
||||
<TransitionGroup name="breadcrumb">
|
||||
<BreadcrumbItem v-for="(item, index) in breadcrumbList" :key="`${index}_${item.path}_${item.title}`" :to="index < breadcrumbList.length - 1 && item.path !== '' ? pathCompile(item.path) : ''">
|
||||
<BreadcrumbItem v-for="(item, index) in breadcrumbList" :key="`${index}_${item.path}_${item.title}`" :to="Number(index) < breadcrumbList.length - 1 && item.path !== '' ? pathCompile(item.path) : ''">
|
||||
{{ item.title }}
|
||||
</BreadcrumbItem>
|
||||
</TransitionGroup>
|
||||
|
||||
@@ -33,7 +33,7 @@ function toggleColorScheme(event: MouseEvent) {
|
||||
]
|
||||
document.documentElement.animate(
|
||||
{
|
||||
clipPath: settingsStore.currentColorScheme === 'light' ? clipPath : [...clipPath].reverse(),
|
||||
clipPath: settingsStore.currentColorScheme === 'light' ? clipPath : clipPath.toReversed(),
|
||||
},
|
||||
{
|
||||
duration: 500,
|
||||
|
||||
@@ -30,7 +30,7 @@ const searchInput = ref('')
|
||||
const searchInputRef = useTemplateRef('searchInputRef')
|
||||
const { focused: searchInputFocused } = useFocus(searchInputRef)
|
||||
const sourceList = ref<listTypes[]>([])
|
||||
const actived = ref(-1)
|
||||
const actived = ref(0)
|
||||
|
||||
const searchResultRef = useTemplateRef('searchResultRef')
|
||||
const searchResultItemRef = useTemplateRef<HTMLElement[]>('searchResultItemRef')
|
||||
@@ -69,7 +69,7 @@ const resultList = computed(() => {
|
||||
watch(() => isShow.value, (val) => {
|
||||
if (val) {
|
||||
searchInput.value = ''
|
||||
actived.value = -1
|
||||
actived.value = 0
|
||||
// 当搜索显示的时候绑定上、下、回车快捷键,隐藏的时候再解绑。另外当 input 处于 focus 状态时,采用 vue 来绑定键盘事件
|
||||
hotkeys('up', keyUp)
|
||||
hotkeys('down', keyDown)
|
||||
@@ -89,7 +89,7 @@ watch(() => isShow.value, (val) => {
|
||||
}
|
||||
})
|
||||
watch(() => resultList.value, () => {
|
||||
actived.value = -1
|
||||
actived.value = 0
|
||||
handleScroll()
|
||||
})
|
||||
|
||||
@@ -158,15 +158,13 @@ function keyDown() {
|
||||
}
|
||||
}
|
||||
function keyEnter() {
|
||||
if (actived.value !== -1) {
|
||||
searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
|
||||
}
|
||||
searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.click()
|
||||
}
|
||||
function handleScroll() {
|
||||
if (searchResultRef.value?.areaRef?.ref?.el?.viewportElement) {
|
||||
const contentDom = searchResultRef.value.areaRef.ref.el.viewportElement
|
||||
if (searchResultRef.value?.ref?.el?.viewportElement) {
|
||||
const contentDom = searchResultRef.value.ref.el.viewportElement
|
||||
let scrollTo = 0
|
||||
if (actived.value !== -1) {
|
||||
if (resultList.value.length > 0) {
|
||||
scrollTo = contentDom.scrollTop
|
||||
const activedOffsetTop = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.offsetTop ?? 0
|
||||
const activedClientHeight = searchResultItemRef.value?.find(item => Number.parseInt(item.dataset.index!) === actived.value)?.clientHeight ?? 0
|
||||
@@ -197,7 +195,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FaModal ref="searchResultRef" v-model="isShow" border :footer="settingsStore.mode === 'pc'" :closable="false" class="w-full lg-max-w-2xl" content-class="flex flex-col p-0 min-h-auto" header-class="p-0" footer-class="p-0" @opened="searchInputFocused = true">
|
||||
<FaModal v-model="isShow" border :footer="settingsStore.mode === 'pc'" :closable="false" class="w-full lg-max-w-2xl" content-class="flex flex-col p-0 min-h-auto" header-class="p-0" footer-class="p-0" @opened="searchInputFocused = true">
|
||||
<template #header>
|
||||
<div class="h-12 flex flex-shrink-0 items-center">
|
||||
<div class="h-full w-14 flex-center">
|
||||
@@ -236,7 +234,7 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<FaScrollArea ref="searchResultRef">
|
||||
<template v-if="resultList.length > 0">
|
||||
<div v-for="(item, index) in resultList" ref="searchResultItemRef" :key="item.path" class="p-4" :data-index="index" @click="pageJump(item.path, item.link)" @mouseover="actived = index">
|
||||
<a class="flex cursor-pointer items-center border rounded-lg" :class="{ '-mt-4': index !== 0, 'bg-accent': index === actived }">
|
||||
@@ -268,6 +266,6 @@ function pageJump(path: listTypes['path'], link: listTypes['link']) {
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</FaScrollArea>
|
||||
</FaModal>
|
||||
</template>
|
||||
|
||||
@@ -8,7 +8,7 @@ defineOptions({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="toolbar-container flex items-center justify-between">
|
||||
<div class="toolbar-container h-[var(--g-toolbar-height)] flex items-center justify-between bg-[var(--g-toolbar-bg)]">
|
||||
<div class="left-side h-full flex items-center of-hidden pe-16 ps-2">
|
||||
<LeftSide />
|
||||
</div>
|
||||
@@ -19,13 +19,7 @@ defineOptions({
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toolbar-container {
|
||||
height: var(--g-toolbar-height);
|
||||
background-color: var(--g-toolbar-bg);
|
||||
transition: background-color 0.3s;
|
||||
|
||||
.left-side {
|
||||
mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 50px), transparent);
|
||||
}
|
||||
.left-side {
|
||||
mask-image: linear-gradient(to right, #000 0%, #000 calc(100% - 50px), transparent);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RouteLocationRaw, Router } from 'vue-router'
|
||||
import type { NavigationFailure, RouteLocationRaw, Router } from 'vue-router'
|
||||
import pinia from '@/store'
|
||||
|
||||
function getId(router: Router) {
|
||||
|
||||
+12
-30
@@ -4,7 +4,7 @@ import { asyncRoutes, asyncRoutesByFilesystem } from './routes'
|
||||
import '@/assets/styles/nprogress.css'
|
||||
|
||||
function setupRoutes(router: Router) {
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
router.beforeEach(async (to) => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
const routeStore = useRouteStore()
|
||||
@@ -17,27 +17,19 @@ function setupRoutes(router: Router) {
|
||||
settingsStore.settings.menu.mode !== 'single' && menuStore.setActived(to.path)
|
||||
// 如果已登录状态下,进入登录页会强制跳转到主页
|
||||
if (to.name === 'login') {
|
||||
next({
|
||||
return {
|
||||
path: settingsStore.settings.home.fullPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
// 如果未开启主页,但进入的是主页,则会进入侧边栏导航第一个模块
|
||||
else if (!settingsStore.settings.home.enable && to.fullPath === settingsStore.settings.home.fullPath) {
|
||||
if (menuStore.sidebarMenus.length > 0) {
|
||||
next({
|
||||
return {
|
||||
path: menuStore.sidebarMenusFirstDeepestPath,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
// 如果侧边栏导航第一个模块均无法命中,则还是进入主页
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
// 正常访问页面
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -84,24 +76,21 @@ function setupRoutes(router: Router) {
|
||||
userStore.logout()
|
||||
}
|
||||
// 动态路由生成并注册后,重新进入当前路由
|
||||
next({
|
||||
return {
|
||||
path: to.path,
|
||||
query: to.query,
|
||||
replace: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (to.name !== 'login') {
|
||||
next({
|
||||
return {
|
||||
name: 'login',
|
||||
query: {
|
||||
redirect: to.fullPath !== settingsStore.settings.home.fullPath ? to.fullPath : undefined,
|
||||
},
|
||||
})
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -109,20 +98,14 @@ function setupRoutes(router: Router) {
|
||||
|
||||
// 当父级路由未配置重定向时,自动重定向到有访问权限的子路由
|
||||
function setupRedirectAuthChildrenRoute(router: Router) {
|
||||
router.beforeEach((to, _from, next) => {
|
||||
router.beforeEach((to) => {
|
||||
const { auth } = useAuth()
|
||||
const currentRoute = router.getRoutes().find(route => route.path === (to.matched.at(-1)?.path ?? ''))
|
||||
if (!currentRoute?.redirect) {
|
||||
const findAuthRoute = currentRoute?.children?.find(route => route.meta?.menu !== false && auth(route.meta?.auth ?? ''))
|
||||
if (findAuthRoute) {
|
||||
next(findAuthRoute)
|
||||
return findAuthRoute
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -130,12 +113,11 @@ function setupRedirectAuthChildrenRoute(router: Router) {
|
||||
// 进度条
|
||||
function setupProgress(router: Router) {
|
||||
const { isLoading } = useNProgress()
|
||||
router.beforeEach((_to, _from, next) => {
|
||||
router.beforeEach(() => {
|
||||
const settingsStore = useSettingsStore()
|
||||
if (settingsStore.settings.app.enableProgress) {
|
||||
isLoading.value = true
|
||||
}
|
||||
next()
|
||||
})
|
||||
router.afterEach(() => {
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
@@ -73,9 +73,9 @@ const constantRoutesByFilesystem = generatedRoutes.filter((item) => {
|
||||
return item.meta?.enabled !== false && item.meta?.constant === true
|
||||
})
|
||||
|
||||
const asyncRoutesByFilesystem = setupLayouts(generatedRoutes.filter((item) => {
|
||||
const asyncRoutesByFilesystem = [...setupLayouts(generatedRoutes.filter((item) => {
|
||||
return item.meta?.enabled !== false && item.meta?.constant !== true && item.meta?.layout !== false
|
||||
}))
|
||||
}))]
|
||||
|
||||
export {
|
||||
asyncRoutes,
|
||||
|
||||
@@ -12,6 +12,12 @@ export const useSettingsStore = defineStore(
|
||||
|
||||
const prefersColorScheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
watch(() => settings.value.app.colorScheme, (val) => {
|
||||
document.documentElement.classList.add('disable-color-scheme-transition-duration')
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.classList.remove('disable-color-scheme-transition-duration')
|
||||
})
|
||||
})
|
||||
if (val === '') {
|
||||
prefersColorScheme.addEventListener('change', updateTheme)
|
||||
}
|
||||
|
||||
Vendored
+86
-86
@@ -6,92 +6,92 @@
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
|
||||
const h: typeof import('vue')['h']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const isShallow: typeof import('vue')['isShallow']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
const mapStores: typeof import('pinia')['mapStores']
|
||||
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
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']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
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 useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useGlobalProperties: typeof import('../utils/composables/useGlobalProperties')['default']
|
||||
const useId: typeof import('vue')['useId']
|
||||
const useKeepAliveStore: typeof import('../store/modules/keepAlive')['useKeepAliveStore']
|
||||
const useLink: typeof import('vue-router')['useLink']
|
||||
const useMainPage: typeof import('../utils/composables/useMainPage')['default']
|
||||
const useMenu: typeof import('../utils/composables/useMenu')['default']
|
||||
const useMenuStore: typeof import('../store/modules/menu')['useMenuStore']
|
||||
const useModel: typeof import('vue')['useModel']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouteStore: typeof import('../store/modules/route')['useRouteStore']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useSettingsStore: typeof import('../store/modules/settings')['useSettingsStore']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const useTabbar: typeof import('../utils/composables/useTabbar')['default']
|
||||
const useTabbarStore: typeof import('../store/modules/tabbar')['useTabbarStore']
|
||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||
const useUserStore: typeof import('../store/modules/user')['useUserStore']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
const EffectScope: typeof import('vue').EffectScope
|
||||
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
|
||||
const computed: typeof import('vue').computed
|
||||
const createApp: typeof import('vue').createApp
|
||||
const createPinia: typeof import('pinia').createPinia
|
||||
const customRef: typeof import('vue').customRef
|
||||
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
|
||||
const defineComponent: typeof import('vue').defineComponent
|
||||
const defineStore: typeof import('pinia').defineStore
|
||||
const effectScope: typeof import('vue').effectScope
|
||||
const getActivePinia: typeof import('pinia').getActivePinia
|
||||
const getCurrentInstance: typeof import('vue').getCurrentInstance
|
||||
const getCurrentScope: typeof import('vue').getCurrentScope
|
||||
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
|
||||
const h: typeof import('vue').h
|
||||
const inject: typeof import('vue').inject
|
||||
const isProxy: typeof import('vue').isProxy
|
||||
const isReactive: typeof import('vue').isReactive
|
||||
const isReadonly: typeof import('vue').isReadonly
|
||||
const isRef: typeof import('vue').isRef
|
||||
const isShallow: typeof import('vue').isShallow
|
||||
const mapActions: typeof import('pinia').mapActions
|
||||
const mapGetters: typeof import('pinia').mapGetters
|
||||
const mapState: typeof import('pinia').mapState
|
||||
const mapStores: typeof import('pinia').mapStores
|
||||
const mapWritableState: typeof import('pinia').mapWritableState
|
||||
const markRaw: typeof import('vue').markRaw
|
||||
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
|
||||
const onErrorCaptured: typeof import('vue').onErrorCaptured
|
||||
const onMounted: typeof import('vue').onMounted
|
||||
const onRenderTracked: typeof import('vue').onRenderTracked
|
||||
const onRenderTriggered: typeof import('vue').onRenderTriggered
|
||||
const onScopeDispose: typeof import('vue').onScopeDispose
|
||||
const onServerPrefetch: typeof import('vue').onServerPrefetch
|
||||
const onUnmounted: typeof import('vue').onUnmounted
|
||||
const onUpdated: typeof import('vue').onUpdated
|
||||
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
|
||||
const provide: typeof import('vue').provide
|
||||
const reactive: typeof import('vue').reactive
|
||||
const readonly: typeof import('vue').readonly
|
||||
const ref: typeof import('vue').ref
|
||||
const resolveComponent: typeof import('vue').resolveComponent
|
||||
const setActivePinia: typeof import('pinia').setActivePinia
|
||||
const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
|
||||
const shallowReactive: typeof import('vue').shallowReactive
|
||||
const shallowReadonly: typeof import('vue').shallowReadonly
|
||||
const shallowRef: typeof import('vue').shallowRef
|
||||
const storeToRefs: typeof import('pinia').storeToRefs
|
||||
const toRaw: typeof import('vue').toRaw
|
||||
const toRef: typeof import('vue').toRef
|
||||
const toRefs: typeof import('vue').toRefs
|
||||
const toValue: typeof import('vue').toValue
|
||||
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 useCssModule: typeof import('vue').useCssModule
|
||||
const useCssVars: typeof import('vue').useCssVars
|
||||
const useGlobalProperties: typeof import('../utils/composables/useGlobalProperties').default
|
||||
const useId: typeof import('vue').useId
|
||||
const useKeepAliveStore: typeof import('../store/modules/keepAlive').useKeepAliveStore
|
||||
const useLink: typeof import('vue-router').useLink
|
||||
const useMainPage: typeof import('../utils/composables/useMainPage').default
|
||||
const useMenu: typeof import('../utils/composables/useMenu').default
|
||||
const useMenuStore: typeof import('../store/modules/menu').useMenuStore
|
||||
const useModel: typeof import('vue').useModel
|
||||
const useRoute: typeof import('vue-router').useRoute
|
||||
const useRouteStore: typeof import('../store/modules/route').useRouteStore
|
||||
const useRouter: typeof import('vue-router').useRouter
|
||||
const useSettingsStore: typeof import('../store/modules/settings').useSettingsStore
|
||||
const useSlots: typeof import('vue').useSlots
|
||||
const useTabbar: typeof import('../utils/composables/useTabbar').default
|
||||
const useTabbarStore: typeof import('../store/modules/tabbar').useTabbarStore
|
||||
const useTemplateRef: typeof import('vue').useTemplateRef
|
||||
const useUserStore: typeof import('../store/modules/user').useUserStore
|
||||
const watch: typeof import('vue').watch
|
||||
const watchEffect: typeof import('vue').watchEffect
|
||||
const watchPostEffect: typeof import('vue').watchPostEffect
|
||||
const watchSyncEffect: typeof import('vue').watchSyncEffect
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
|
||||
Vendored
+53
-1
@@ -1,8 +1,12 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
// biome-ignore lint: disable
|
||||
import { GlobalComponents } from 'vue'
|
||||
|
||||
export {}
|
||||
|
||||
/* prettier-ignore */
|
||||
@@ -54,3 +58,51 @@ declare module 'vue' {
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
}
|
||||
|
||||
// For TSX support
|
||||
declare global {
|
||||
const AccountButton: typeof import('./../components/AccountButton/index.vue')['default']
|
||||
const FaAuth: typeof import('./../ui/components/FaAuth/index.vue')['default']
|
||||
const FaAvatar: typeof import('./../ui/components/FaAvatar/index.vue')['default']
|
||||
const FaBackToTop: typeof import('./../ui/components/FaBackToTop/index.vue')['default']
|
||||
const FaButton: typeof import('./../ui/components/FaButton/index.vue')['default']
|
||||
const FaButtonGroup: typeof import('./../ui/components/FaButtonGroup/index.vue')['default']
|
||||
const FaCard: typeof import('./../ui/components/FaCard/index.vue')['default']
|
||||
const FaCheckbox: typeof import('./../ui/components/FaCheckbox/index.vue')['default']
|
||||
const FaCollapsible: typeof import('./../ui/components/FaCollapsible/index.vue')['default']
|
||||
const FaContextMenu: typeof import('./../ui/components/FaContextMenu/index.vue')['default']
|
||||
const FaCopyright: typeof import('./../ui/components/FaCopyright/index.vue')['default']
|
||||
const FaDivider: typeof import('./../ui/components/FaDivider/index.vue')['default']
|
||||
const FaDrawer: typeof import('./../ui/components/FaDrawer/index.vue')['default']
|
||||
const FaDropdown: typeof import('./../ui/components/FaDropdown/index.vue')['default']
|
||||
const FaFileUpload: typeof import('./../ui/components/FaFileUpload/index.vue')['default']
|
||||
const FaFixedActionBar: typeof import('./../ui/components/FaFixedActionBar/index.vue')['default']
|
||||
const FaHoverCard: typeof import('./../ui/components/FaHoverCard/index.vue')['default']
|
||||
const FaIcon: typeof import('./../ui/components/FaIcon/index.vue')['default']
|
||||
const FaImagePreview: typeof import('./../ui/components/FaImagePreview/index.vue')['default']
|
||||
const FaImageUpload: typeof import('./../ui/components/FaImageUpload/index.vue')['default']
|
||||
const FaInput: typeof import('./../ui/components/FaInput/index.vue')['default']
|
||||
const FaKbd: typeof import('./../ui/components/FaKbd/index.vue')['default']
|
||||
const FaModal: typeof import('./../ui/components/FaModal/index.vue')['default']
|
||||
const FaNotAllowed: typeof import('./../ui/components/FaNotAllowed/index.vue')['default']
|
||||
const FaNotification: typeof import('./../ui/components/FaNotification/index.vue')['default']
|
||||
const FaPageHeader: typeof import('./../ui/components/FaPageHeader/index.vue')['default']
|
||||
const FaPageMain: typeof import('./../ui/components/FaPageMain/index.vue')['default']
|
||||
const FaPasswordStrength: typeof import('./../ui/components/FaPasswordStrength/index.vue')['default']
|
||||
const FaPinInput: typeof import('./../ui/components/FaPinInput/index.vue')['default']
|
||||
const FaPopover: typeof import('./../ui/components/FaPopover/index.vue')['default']
|
||||
const FaProgress: typeof import('./../ui/components/FaProgress/index.vue')['default']
|
||||
const FaScrollArea: typeof import('./../ui/components/FaScrollArea/index.vue')['default']
|
||||
const FaSearchBar: typeof import('./../ui/components/FaSearchBar/index.vue')['default']
|
||||
const FaSelect: typeof import('./../ui/components/FaSelect/index.vue')['default']
|
||||
const FaSlider: typeof import('./../ui/components/FaSlider/index.vue')['default']
|
||||
const FaSmartFixedBlock: typeof import('./../ui/components/FaSmartFixedBlock/index.vue')['default']
|
||||
const FaSwitch: typeof import('./../ui/components/FaSwitch/index.vue')['default']
|
||||
const FaSystemInfo: typeof import('./../ui/components/FaSystemInfo/index.vue')['default']
|
||||
const FaTabs: typeof import('./../ui/components/FaTabs/index.vue')['default']
|
||||
const FaTextarea: typeof import('./../ui/components/FaTextarea/index.vue')['default']
|
||||
const FaToast: typeof import('./../ui/components/FaToast/index.vue')['default']
|
||||
const FaTooltip: typeof import('./../ui/components/FaTooltip/index.vue')['default']
|
||||
const RouterLink: typeof import('vue-router')['RouterLink']
|
||||
const RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
Vendored
+10
-10
@@ -1,13 +1,5 @@
|
||||
interface ImportMetaEnv {
|
||||
// Auto generate by env-parse
|
||||
/**
|
||||
* 应用配置面板
|
||||
*/
|
||||
readonly VITE_APP_SETTING: boolean
|
||||
/**
|
||||
* 页面标题
|
||||
*/
|
||||
readonly VITE_APP_TITLE: string
|
||||
/**
|
||||
* 接口请求地址,会设置到 axios 的 baseURL 参数上
|
||||
*/
|
||||
@@ -21,11 +13,19 @@ interface ImportMetaEnv {
|
||||
*/
|
||||
readonly VITE_APP_DISABLE_DEVTOOL: boolean
|
||||
/**
|
||||
* 是否开启代理
|
||||
* 应用配置面板
|
||||
*/
|
||||
readonly VITE_OPEN_PROXY: boolean
|
||||
readonly VITE_APP_SETTING: boolean
|
||||
/**
|
||||
* 页面标题
|
||||
*/
|
||||
readonly VITE_APP_TITLE: string
|
||||
/**
|
||||
* 是否开启开发者工具
|
||||
*/
|
||||
readonly VITE_OPEN_DEVTOOLS: boolean
|
||||
/**
|
||||
* 是否开启代理
|
||||
*/
|
||||
readonly VITE_OPEN_PROXY: boolean
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ const settingsStore = useSettingsStore()
|
||||
<FaIcon name="i-ri:copyright-line" class="size-5" />
|
||||
<span v-if="settingsStore.settings.copyright.dates" class="px-1">{{ 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" class="px-1 text-center no-underline transition hover-text-secondary-foreground">{{ settingsStore.settings.copyright.company }}</a>
|
||||
<a v-if="settingsStore.settings.copyright.website" :href="settingsStore.settings.copyright.website" target="_blank" rel="noopener" class="px-1 text-center no-underline transition-colors hover-text-secondary-foreground">{{ settingsStore.settings.copyright.company }}</a>
|
||||
<span v-else class="px-1">{{ settingsStore.settings.copyright.company }}</span>
|
||||
</template>
|
||||
<a v-if="settingsStore.settings.copyright.beian" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" class="px-1 text-center no-underline transition hover-text-secondary-foreground">{{ settingsStore.settings.copyright.beian }}</a>
|
||||
<a v-if="settingsStore.settings.copyright.beian" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener" class="px-1 text-center no-underline transition-colors hover-text-secondary-foreground">{{ settingsStore.settings.copyright.beian }}</a>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
@@ -23,13 +23,14 @@ export interface DrawerProps {
|
||||
confirmButtonLoading?: boolean
|
||||
beforeClose?: (
|
||||
action: 'confirm' | 'cancel' | 'close',
|
||||
done: () => void
|
||||
done: () => void,
|
||||
) => void
|
||||
header?: boolean
|
||||
footer?: boolean
|
||||
closeOnClickOverlay?: boolean
|
||||
closeOnPressEscape?: boolean
|
||||
destroyOnClose?: boolean
|
||||
openAutoFocus?: boolean
|
||||
contentClass?: HTMLAttributes['class']
|
||||
headerClass?: HTMLAttributes['class']
|
||||
footerClass?: HTMLAttributes['class']
|
||||
@@ -93,7 +94,7 @@ export function useFaDrawer() {
|
||||
}
|
||||
|
||||
// 监听 visible 和 options 变化,自动重新渲染
|
||||
watch([visible, options], () => {
|
||||
watch([visible, options as object], () => {
|
||||
updateVNode()
|
||||
}, {
|
||||
immediate: true,
|
||||
|
||||
@@ -38,6 +38,7 @@ const props = withDefaults(
|
||||
closeOnClickOverlay: true,
|
||||
closeOnPressEscape: true,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: false,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -135,6 +136,13 @@ async function onCancel() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpenAutoFocus(e: Event) {
|
||||
if (!props.openAutoFocus) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
function handleFocusOutside(e: Event) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -179,7 +187,7 @@ function handleAnimationEnd() {
|
||||
})"
|
||||
:side="props.side"
|
||||
:force-mount="forceMount"
|
||||
@open-auto-focus="handleFocusOutside"
|
||||
@open-auto-focus="handleOpenAutoFocus"
|
||||
@close-auto-focus="handleFocusOutside"
|
||||
@focus-outside="handleFocusOutside"
|
||||
@pointer-down-outside="handleClickOutside"
|
||||
@@ -209,15 +217,11 @@ function handleAnimationEnd() {
|
||||
<SheetTitle />
|
||||
<SheetDescription />
|
||||
</VisuallyHidden>
|
||||
<div class="m-0 flex-1 of-y-hidden">
|
||||
<FaScrollArea v-if="!!slots.default" class="h-full">
|
||||
<div class="p-4">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
|
||||
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
|
||||
</div>
|
||||
</FaScrollArea>
|
||||
<div v-if="!!slots.default" class="relative m-0 flex-1 overflow-y-auto p-4">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
|
||||
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
|
||||
</div>
|
||||
<SheetFooter
|
||||
v-if="footer" :class="cn('p-3 gap-y-2', props.footerClass, {
|
||||
|
||||
@@ -80,7 +80,7 @@ function onSelectFile(files: FileList | File[] | null) {
|
||||
if (!files) {
|
||||
return
|
||||
}
|
||||
const selectedFiles = Array.from(files)
|
||||
const selectedFiles = [...files]
|
||||
// 数量限制
|
||||
const remain = props.max > 0 ? props.max - fileList.value.length : selectedFiles.length
|
||||
const filesToAdd: File[] = []
|
||||
@@ -175,10 +175,12 @@ function removeFile(idx: number) {
|
||||
@drop="handleDrop"
|
||||
@click="fileInputRef?.click()"
|
||||
>
|
||||
<FaIcon name="i-icon-park-outline:upload" class="mb-2 text-2xl text-card-foreground/50" />
|
||||
<div class="text-sm text-card-foreground/70">
|
||||
将文件拖到此处,或<span class="cursor-pointer text-primary font-bold">点击上传</span>
|
||||
</div>
|
||||
<slot>
|
||||
<FaIcon name="i-icon-park-outline:upload" class="mb-2 text-2xl text-card-foreground/50" />
|
||||
<div class="text-sm text-card-foreground/70">
|
||||
将文件拖到此处,或<span class="cursor-pointer text-primary font-bold">点击上传</span>
|
||||
</div>
|
||||
</slot>
|
||||
<input ref="fileInputRef" type="file" :multiple="props.multiple" :disabled="props.disabled" class="hidden" @change="e => onSelectFile((e.target as HTMLInputElement).files)">
|
||||
</button>
|
||||
<div v-if="!props.hideTips && !props.disabled" class="flex flex-wrap gap-1 text-xs text-card-foreground/50 empty:hidden">
|
||||
|
||||
@@ -21,6 +21,12 @@ const isLoading = ref(true)
|
||||
const isError = ref(false)
|
||||
const isOpen = ref(false)
|
||||
|
||||
// 监听 src 变化,重置加载状态
|
||||
watch(() => props.src, () => {
|
||||
isLoading.value = true
|
||||
isError.value = false
|
||||
}, { immediate: false })
|
||||
|
||||
function handleLoad() {
|
||||
isLoading.value = false
|
||||
emits('load')
|
||||
|
||||
@@ -54,7 +54,7 @@ function onSelectFile(e: Event) {
|
||||
if (!input.files) {
|
||||
return
|
||||
}
|
||||
const selectedFiles = Array.from(input.files)
|
||||
const selectedFiles = [...input.files]
|
||||
const remain = props.max === 0 ? selectedFiles.length : props.max - images.value.length
|
||||
const filesToAdd: File[] = []
|
||||
for (const file of selectedFiles.slice(0, remain)) {
|
||||
|
||||
@@ -27,13 +27,14 @@ export interface ModalProps {
|
||||
confirmButtonLoading?: boolean
|
||||
beforeClose?: (
|
||||
action: 'confirm' | 'cancel' | 'close',
|
||||
done: () => void
|
||||
done: () => void,
|
||||
) => void
|
||||
header?: boolean
|
||||
footer?: boolean
|
||||
closeOnClickOverlay?: boolean
|
||||
closeOnPressEscape?: boolean
|
||||
destroyOnClose?: boolean
|
||||
openAutoFocus?: boolean
|
||||
class?: HTMLAttributes['class']
|
||||
headerClass?: HTMLAttributes['class']
|
||||
contentClass?: HTMLAttributes['class']
|
||||
@@ -101,7 +102,7 @@ export function useFaModal() {
|
||||
}
|
||||
|
||||
// 监听 visible 和 options 变化,自动重新渲染
|
||||
watch([visible, options], () => {
|
||||
watch([visible, options as object], () => {
|
||||
updateVNode()
|
||||
}, {
|
||||
immediate: true,
|
||||
@@ -147,6 +148,7 @@ export function useFaModal() {
|
||||
alignCenter: true,
|
||||
closeOnClickOverlay: false,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: true,
|
||||
contentClass: 'py-0 min-h-auto',
|
||||
footerClass: 'p-4',
|
||||
}
|
||||
@@ -161,6 +163,7 @@ export function useFaModal() {
|
||||
alignCenter: true,
|
||||
closeOnClickOverlay: false,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: true,
|
||||
contentClass: 'py-0 min-h-auto',
|
||||
footerClass: 'p-4',
|
||||
}
|
||||
@@ -175,6 +178,7 @@ export function useFaModal() {
|
||||
alignCenter: true,
|
||||
closeOnClickOverlay: false,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: true,
|
||||
contentClass: 'py-0 min-h-auto',
|
||||
footerClass: 'p-4',
|
||||
}
|
||||
@@ -189,6 +193,7 @@ export function useFaModal() {
|
||||
alignCenter: true,
|
||||
closeOnClickOverlay: false,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: true,
|
||||
contentClass: 'py-0 min-h-auto',
|
||||
footerClass: 'p-4',
|
||||
}
|
||||
@@ -203,6 +208,7 @@ export function useFaModal() {
|
||||
showCancelButton: true,
|
||||
closeOnClickOverlay: false,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: true,
|
||||
contentClass: 'py-0 min-h-auto',
|
||||
footerClass: 'p-4',
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ const props = withDefaults(
|
||||
closeOnClickOverlay: true,
|
||||
closeOnPressEscape: true,
|
||||
destroyOnClose: true,
|
||||
openAutoFocus: false,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -55,13 +56,8 @@ const slots = defineSlots<{
|
||||
|
||||
const dialogContentRef = useTemplateRef('dialogContentRef')
|
||||
const dialogHeaderRef = ref()
|
||||
const dialogAreaRef = useTemplateRef('dialogAreaRef')
|
||||
const dialogRef = ref()
|
||||
|
||||
defineExpose({
|
||||
areaRef: dialogAreaRef,
|
||||
})
|
||||
|
||||
const modalId = shallowRef(props.id ?? useId())
|
||||
const isOpen = ref(props.modelValue)
|
||||
const isMaximize = ref(props.maximize)
|
||||
@@ -174,6 +170,13 @@ async function onCancel() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleOpenAutoFocus(e: Event) {
|
||||
if (!props.openAutoFocus) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
}
|
||||
|
||||
function handleFocusOutside(e: Event) {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -222,13 +225,13 @@ function handleAnimationEnd() {
|
||||
:maximize="isMaximize"
|
||||
:maximizable="props.maximizable"
|
||||
:force-mount="forceMount"
|
||||
:class="cn('left-0 right-0 top-0 md:top-[5vh] flex flex-col p-0 gap-0 mx-auto h-[calc-size(auto,size)] min-h-full md:min-h-auto max-h-full md:max-h-[90vh] translate-x-0 translate-y-0', props.class, {
|
||||
:class="cn('left-0 right-0 top-0 md:top-[5vh] flex flex-col p-0 gap-0 mx-auto h-[calc-size(auto,size)] min-h-full overflow-hidden md:min-h-auto max-h-full md:max-h-[90vh] translate-x-0 translate-y-0', props.class, {
|
||||
'md:top-0 size-full max-w-full max-h-full md:max-h-full': isMaximize,
|
||||
'md:top-1/2 md:-translate-y-1/2!': props.alignCenter,
|
||||
'duration-0': isDragging,
|
||||
'hidden': isClosed,
|
||||
})"
|
||||
@open-auto-focus="handleFocusOutside"
|
||||
@open-auto-focus="handleOpenAutoFocus"
|
||||
@close-auto-focus="handleFocusOutside"
|
||||
@focus-outside="handleFocusOutside"
|
||||
@pointer-down-outside="handleClickOutside"
|
||||
@@ -273,14 +276,12 @@ function handleAnimationEnd() {
|
||||
<DialogTitle />
|
||||
<DialogDescription />
|
||||
</VisuallyHidden>
|
||||
<FaScrollArea v-if="!!slots.default" ref="dialogAreaRef" class="flex-1">
|
||||
<div :class="cn('min-h-40 p-4', props.contentClass)">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
|
||||
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
|
||||
</div>
|
||||
</FaScrollArea>
|
||||
<div v-if="!!slots.default" :class="cn('relative flex-1 min-h-40 p-4 overflow-y-auto', props.contentClass)">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-show="props.loading" class="absolute inset-0 z-1000 size-full flex-center bg-popover/75">
|
||||
<FaIcon name="i-line-md:loading-twotone-loop" class="size-10" />
|
||||
</div>
|
||||
<DialogFooter
|
||||
v-if="footer" :class="cn('p-3 gap-y-2', props.footerClass, {
|
||||
'md:justify-center': props.center,
|
||||
|
||||
@@ -80,11 +80,11 @@ const state = ref<State>({
|
||||
function dispatch(action: Action) {
|
||||
switch (action.type) {
|
||||
case actionTypes.ADD_TOAST:
|
||||
state.value.toasts = [action.toast, ...state.value.toasts].slice(0, TOAST_LIMIT)
|
||||
state.value.toasts = [action.toast as any, ...state.value.toasts as any].slice(0, TOAST_LIMIT)
|
||||
break
|
||||
|
||||
case actionTypes.UPDATE_TOAST:
|
||||
state.value.toasts = state.value.toasts.map(t =>
|
||||
state.value.toasts = state.value.toasts.map((t: any) =>
|
||||
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
||||
)
|
||||
break
|
||||
@@ -101,7 +101,7 @@ function dispatch(action: Action) {
|
||||
})
|
||||
}
|
||||
|
||||
state.value.toasts = state.value.toasts.map(t =>
|
||||
state.value.toasts = state.value.toasts.map((t: any) =>
|
||||
t.id === toastId || toastId === undefined
|
||||
? {
|
||||
...t,
|
||||
@@ -116,7 +116,7 @@ function dispatch(action: Action) {
|
||||
if (action.toastId === undefined) {
|
||||
state.value.toasts = []
|
||||
}
|
||||
else { state.value.toasts = state.value.toasts.filter(t => t.id !== action.toastId) }
|
||||
else { state.value.toasts = state.value.toasts.filter((t: any) => t.id !== action.toastId) }
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ defineExpose({
|
||||
|
||||
<template>
|
||||
<ScrollAreaRoot v-bind="delegatedProps" :class="cn('relative overflow-hidden', props.class)">
|
||||
<ScrollAreaViewport ref="viewportRef" class="scroll-area-viewport h-full w-full overscroll-contain rounded-[inherit]" @scroll="onScroll" @wheel="onWheel">
|
||||
<ScrollAreaViewport ref="viewportRef" class="scroll-area-viewport h-full w-full rounded-[inherit]" @scroll="onScroll" @wheel="onWheel">
|
||||
<slot />
|
||||
</ScrollAreaViewport>
|
||||
<ScrollBar :class="{ 'opacity-0 pointer-events-none': !props.scrollbar }" />
|
||||
|
||||
@@ -29,10 +29,10 @@ const value = defineModel<AcceptableValue>()
|
||||
|
||||
const selectedOption = computed({
|
||||
get() {
|
||||
return props.options.find(option => option.value === value.value) ?? props.options[0]
|
||||
return props.options.length > 0 ? props.options.find(option => option.value === value.value) ?? props.options[0] : null
|
||||
},
|
||||
set(val) {
|
||||
value.value = val.value
|
||||
value.value = val?.value || null
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -40,7 +40,7 @@ const selectedOption = computed({
|
||||
<template>
|
||||
<Select v-model="value" :disabled>
|
||||
<SelectTrigger :class="cn('w-[200px]', props.class)">
|
||||
<SelectValue :placeholder="selectedOption.label" />
|
||||
<SelectValue :placeholder="selectedOption?.label" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
|
||||
@@ -30,7 +30,7 @@ if (isSupprotColorMix) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ElConfigProvider :locale="zhCN" :button="{ autoInsertSpace: true }">
|
||||
<ElConfigProvider :locale="zhCN as any" :button="{ autoInsertSpace: true }">
|
||||
<slot />
|
||||
</ElConfigProvider>
|
||||
</template>
|
||||
|
||||
@@ -92,6 +92,7 @@ function open(url: string) {
|
||||
[
|
||||
{ label: 'Github', icon: 'i-simple-icons:github', handle: () => open('https://github.com/fantastic-admin/basic') },
|
||||
{ label: 'Gitee', icon: 'i-simple-icons:gitee', handle: () => open('https://gitee.com/fantastic-admin/basic') },
|
||||
{ label: 'GitCode', icon: 'i-simple-icons:gitcode', handle: () => open('https://atomgit.com/fantastic-admin/basic') },
|
||||
],
|
||||
]"
|
||||
>
|
||||
|
||||
+1
-1
@@ -3,7 +3,6 @@
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"#/*": ["./src/types/*"]
|
||||
@@ -19,6 +18,7 @@
|
||||
"strict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"noUncheckedSideEffectImports": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"sourceMap": true,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
// 此处配置是为了满足 shadcn-vue CLI 的配置要求
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
|
||||
+15
-12
@@ -131,16 +131,18 @@ export default function createVitePlugins(mode: string, isBuild = false) {
|
||||
transform: (code, id) => {
|
||||
if (/src\/main.ts$/.test(id)) {
|
||||
if (viteEnv.VITE_APP_DEBUG_TOOL === 'eruda') {
|
||||
code = code.concat(`
|
||||
import eruda from 'eruda'
|
||||
eruda.init()
|
||||
`)
|
||||
code = `
|
||||
${code}
|
||||
import eruda from 'eruda'
|
||||
eruda.init()
|
||||
`
|
||||
}
|
||||
else if (viteEnv.VITE_APP_DEBUG_TOOL === 'vconsole') {
|
||||
code = code.concat(`
|
||||
import VConsole from 'vconsole'
|
||||
new VConsole()
|
||||
`)
|
||||
code = `
|
||||
${code}
|
||||
import VConsole from 'vconsole'
|
||||
new VConsole()
|
||||
`
|
||||
}
|
||||
return {
|
||||
code,
|
||||
@@ -156,10 +158,11 @@ export default function createVitePlugins(mode: string, isBuild = false) {
|
||||
transform: (code, id) => {
|
||||
if (/src\/main.ts$/.test(id)) {
|
||||
if (viteEnv.VITE_APP_DISABLE_DEVTOOL) {
|
||||
code = code.concat(`
|
||||
import DisableDevtool from 'disable-devtool'
|
||||
DisableDevtool()
|
||||
`)
|
||||
code = `
|
||||
${code}
|
||||
import DisableDevtool from 'disable-devtool'
|
||||
DisableDevtool()
|
||||
`
|
||||
}
|
||||
return {
|
||||
code,
|
||||
|
||||
Reference in New Issue
Block a user