mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-01-23 01:30:26 +08:00
chore: init project
This commit is contained in:
commit
399334ac57
4
.browserslistrc
Normal file
4
.browserslistrc
Normal file
@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
5
.changeset/README.md
Normal file
5
.changeset/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
15
.changeset/config.json
Normal file
15
.changeset/config.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [["@vben-core/*", "@vben/*"]],
|
||||
"snapshot": {
|
||||
"prereleaseTemplate": "{tag}-{datetime}"
|
||||
},
|
||||
"privatePackages": { "version": true, "tag": true },
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
1
.commitlintrc.mjs
Normal file
1
.commitlintrc.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@vben/commitlint-config';
|
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@ -0,0 +1,21 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
max_line_length = 100
|
||||
trim_trailing_whitespace = true
|
||||
quote_type = single
|
||||
|
||||
[*.{yml,yaml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
|
||||
|
||||
# Automatically normalize line endings (to LF) for all text-based files.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
|
2
.gitconfig
Normal file
2
.gitconfig
Normal file
@ -0,0 +1,2 @@
|
||||
[core]
|
||||
ignorecase = false
|
39
.github/ISSUE_TEMPLATE/1-bug.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/1-bug.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'bug: pending triage'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
抱歉,您遇到了一个错误。感谢您抽出宝贵的时间进行举报!
|
||||
|
||||
请尽可能填写以下模板。
|
||||
|
||||
Ouch, sorry you’ve run into a bug. Thank for taking the time to report it!
|
||||
|
||||
Please fill in as much of the template below as you’re able.
|
||||
|
||||
P.S. have you seen our support and contributing docs?
|
||||
-->
|
||||
|
||||
**⚠️ IMPORTANT ⚠️ Please check the following list before proceeding. If you ignore this issue template, your issue will be directly closed.**
|
||||
|
||||
- [ ] Read [the docs](https://anncwb.github.io/vue-vben-admin-doc/).
|
||||
- [ ] Make sure the code is up to date. (Some bugs have been fixed in the latest code)
|
||||
- [ ] This is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/anncwb/vue-vben-admin/discussions) or join our [Discord](https://discord.gg/8GuAdwDhj6) Chat Server.
|
||||
|
||||
### Describe the bug
|
||||
|
||||
A clear and concise description of what the bug is..
|
||||
|
||||
### Reproduction
|
||||
|
||||
Please describe the steps of the problem in detail to ensure that we can restore the correct problem
|
||||
|
||||
## System Info
|
||||
|
||||
- Operating System:
|
||||
- Node version:
|
||||
- Package manager (npm/yarn/pnpm) and version:
|
32
.github/ISSUE_TEMPLATE/2-feature.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/2-feature.md
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
name: 🚀 Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
感谢您提出使这个项目更好的想法!
|
||||
请尽可能填写以下模板。
|
||||
|
||||
Thank you for suggesting an idea to make this project better!
|
||||
Please fill in as much of the template below as you’re able.
|
||||
|
||||
-->
|
||||
|
||||
### Subject of the feature
|
||||
|
||||
Describe your issue here.
|
||||
|
||||
### Problem
|
||||
|
||||
If the feature requests relates to a problem, please describe the problem you are trying to solve here.
|
||||
|
||||
### Expected behaviour
|
||||
|
||||
What should happen? Please describe the desired behaviour.
|
||||
|
||||
### Alternatives
|
||||
|
||||
What are the alternative solutions? Please describe what else you have considered?
|
28
.github/ISSUE_TEMPLATE/3-bug-cn.md
vendored
Normal file
28
.github/ISSUE_TEMPLATE/3-bug-cn.md
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
name: 🐛 Bug 报告
|
||||
about: 向我们报告一个Bug以帮助我们改进
|
||||
title: ''
|
||||
labels: 'bug: pending triage'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
**⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭**
|
||||
|
||||
- [ ] 已阅读 [文档](https://anncwb.github.io/vue-vben-admin-doc/).
|
||||
- [ ] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复)
|
||||
- [ ] 已在 Issues 中搜索了相关的关键词
|
||||
- [ ] 不是 ant design vue 组件库的 Bug
|
||||
|
||||
### 描述 Bug
|
||||
|
||||
请清晰地描述此 Bug 的具体表现。
|
||||
|
||||
### 复现 Bug
|
||||
|
||||
请描述在演示页面中复现 Bug 的详细步骤,以确保我们可以理解并定位问题。部分 Bug 如果未在 Demo 中涉及,请务必提供关键代码
|
||||
|
||||
## 系统信息
|
||||
|
||||
- 操作系统:
|
||||
- Node 版本:
|
||||
- 包管理器 (npm/yarn/pnpm) 及其版本:
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord Chat
|
||||
url: https://discord.gg/8GuAdwDhj6
|
||||
about: Ask questions and discuss with other Vben users in real time.
|
||||
- name: Questions & Discussions
|
||||
url: https://github.com/anncwb/vue-vben-admin/discussions
|
||||
about: Use GitHub discussions for message-board style questions and discussions.
|
89
.github/commit-convention.md
vendored
Normal file
89
.github/commit-convention.md
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
## Git Commit Message Convention
|
||||
|
||||
> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
|
||||
|
||||
#### TL;DR:
|
||||
|
||||
Messages must be matched by the following regex:
|
||||
|
||||
```js
|
||||
/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|types|wip): .{1,50}/;
|
||||
```
|
||||
|
||||
#### Examples
|
||||
|
||||
Appears under "Features" header, `dev` subheader:
|
||||
|
||||
```
|
||||
feat(dev): add 'comments' option
|
||||
```
|
||||
|
||||
Appears under "Bug Fixes" header, `dev` subheader, with a link to issue #28:
|
||||
|
||||
```
|
||||
fix(dev): fix dev error
|
||||
|
||||
close #28
|
||||
```
|
||||
|
||||
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
||||
|
||||
```
|
||||
perf(build): remove 'foo' option
|
||||
|
||||
BREAKING CHANGE: The 'foo' option has been removed.
|
||||
```
|
||||
|
||||
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
||||
|
||||
```
|
||||
revert: feat(compiler): add 'comments' option
|
||||
|
||||
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||
```
|
||||
|
||||
### Full Message Format
|
||||
|
||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
### Revert
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
### Type
|
||||
|
||||
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
|
||||
|
||||
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
|
||||
|
||||
### Scope
|
||||
|
||||
The scope could be anything specifying the place of the commit change. For example `dev`, `build`, `workflow`, `cli` etc...
|
||||
|
||||
### Subject
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- don't capitalize the first letter
|
||||
- no dot (.) at the end
|
||||
|
||||
### Body
|
||||
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Footer
|
||||
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
5
.github/contributing.md
vendored
Normal file
5
.github/contributing.md
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Contributing Guide
|
||||
|
||||
1. Make sure you put things in the right category!
|
||||
2. Always add your items to the end of a list. To be fair, the order is first-come-first-serve.
|
||||
3. If you think something belongs in the wrong category, or think there needs to be a new category, feel free to edit things too.
|
34
.github/pull_request_template.md
vendored
Normal file
34
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
### `General`
|
||||
|
||||
> ✏️ Mark the necessary items without changing the structure of the PR template.
|
||||
|
||||
- [ ] Pull request template structure not broken
|
||||
|
||||
### `Type`
|
||||
|
||||
> ℹ️ What types of changes does your code introduce?
|
||||
|
||||
> 👉 _Put an `x` in the boxes that apply_
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
### `Checklist`
|
||||
|
||||
> ℹ️ Check all checkboxes - this will indicate that you have done everything in accordance with the rules in [CONTRIBUTING](contributing.md).
|
||||
|
||||
> 👉 _Put an `x` in the boxes that apply._
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] Is the code format correct
|
||||
- [ ] Is the git submission information standard?
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
- [ ] Any dependent changes have been merged and published in downstream modules
|
118
.github/workflows/deploy.yml
vendored
Normal file
118
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
name: deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
# push-to-ftp:
|
||||
# if: "contains(github.event.head_commit.message, '[deploy]')"
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout
|
||||
# uses: actions/checkout@v2
|
||||
|
||||
# - name: Sed Config Base
|
||||
# shell: bash
|
||||
# run: |
|
||||
# sed -i 's#VITE_PUBLIC_PATH\s*=.*#VITE_PUBLIC_PATH = /next/#g' ./.env.production
|
||||
# sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production
|
||||
# cat ./.env.production
|
||||
|
||||
# - name: use Node.js 14
|
||||
# uses: actions/setup-node@v2.1.2
|
||||
# with:
|
||||
# node-version: '14.x'
|
||||
|
||||
# - name: Get yarn cache
|
||||
# id: yarn-cache
|
||||
# run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
|
||||
# - name: Cache dependencies
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ${{ steps.yarn-cache.outputs.dir }}
|
||||
# key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-yarn-
|
||||
|
||||
# - name: Build
|
||||
# run: |
|
||||
# yarn install
|
||||
# yarn run build
|
||||
|
||||
# - name: Deploy
|
||||
# uses: SamKirkland/FTP-Deploy-Action@2.0.0
|
||||
# env:
|
||||
# FTP_SERVER: ${{ secrets.FTP_SERVER }}
|
||||
# FTP_USERNAME: ${{ secrets.FTP_USERNAME }}
|
||||
# FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
|
||||
# METHOD: sftp
|
||||
# PORT: ${{ secrets.FTP_PORT }}
|
||||
# LOCAL_DIR: dist
|
||||
# REMOTE_DIR: /srv/www/vben-admin
|
||||
# ARGS: --delete --verbose --parallel=80
|
||||
|
||||
push-to-gh-pages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s#VITE_BUILD_COMPRESS\s*=.*#VITE_BUILD_COMPRESS = 'gzip'#g" ./.env.production
|
||||
sed -i "s#VITE_DROP_CONSOLE\s*=.*#VITE_DROP_CONSOLE = true#g" ./.env.production
|
||||
cat ./.env.production
|
||||
|
||||
- name: use Node.js 16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "16.x"
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Set SSH Environment
|
||||
env:
|
||||
DOCS_DEPLOY_KEY: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh/
|
||||
echo "$ACTIONS_DEPLOY_KEY" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
ssh-keyscan github.com > ~/.ssh/known_hosts
|
||||
chmod 700 ~/.ssh && chmod 600 ~/.ssh/*
|
||||
git config --global user.email "vbenadmin@163.com"
|
||||
git config --global user.name "vbenAdmin"
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
run: |
|
||||
yarn install
|
||||
yarn run build
|
||||
touch dist/.nojekyll
|
||||
cp dist/index.html dist/404.html
|
||||
|
||||
- name: Delete gh-pages branch
|
||||
run: |
|
||||
git push origin --delete gh-pages
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3.9.0
|
||||
with:
|
||||
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: ./dist
|
||||
CNAME: vben.vvbin.cn
|
17
.github/workflows/issue-close-require.yml
vendored
Normal file
17
.github/workflows/issue-close-require.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: Issue Close Require
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: need reproduction
|
||||
uses: actions-cool/issues-helper@v2.1.1
|
||||
with:
|
||||
actions: "close-issues"
|
||||
token: ${{ secrets.OPER_TOKEN }}
|
||||
labels: "need reproduction"
|
||||
inactive-day: 3
|
29
.github/workflows/issue-labeled.yml
vendored
Normal file
29
.github/workflows/issue-labeled.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Issue Labeled
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
jobs:
|
||||
reply-labeled:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: remove pending
|
||||
if: github.event.label.name == 'enhancement' || github.event.label.name == 'bug'
|
||||
uses: actions-cool/issues-helper@v2.1.1
|
||||
with:
|
||||
actions: "remove-labels"
|
||||
token: ${{ secrets.OPER_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: "bug: pending triage"
|
||||
|
||||
- name: need reproduction
|
||||
if: github.event.label.name == 'need reproduction'
|
||||
uses: actions-cool/issues-helper@v2.1.1
|
||||
with:
|
||||
actions: "create-comment, remove-labels"
|
||||
token: ${{ secrets.OPER_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}. Please provide the complete reproduction steps and code. Issues labeled by `need reproduction` will be closed if no activities in 3 days.
|
||||
labels: "bug: pending triage"
|
24
.github/workflows/release.yml
vendored
Normal file
24
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: Create Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: Create Release for Tag
|
||||
id: release_tag
|
||||
uses: yyx990803/release-tag@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.OPER_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
body: |
|
||||
Please refer to [CHANGELOG.md](https://github.com/anncwb/vue-vben-admin/blob/main/CHANGELOG.md) for details.
|
38
.gitignore
vendored
Normal file
38
.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
.cache
|
||||
.turbo
|
||||
.stylelintcache
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
.VSCodeCounter
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
vite.config.mts.*
|
||||
vite.config.mjs.*
|
||||
vite.config.js.*
|
||||
vite.config.ts.*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@ -0,0 +1,6 @@
|
||||
ports:
|
||||
- port: 3344
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- init: corepack enable && pnpm install
|
||||
command: pnpm run dev
|
6
.husky/commit-msg
Executable file
6
.husky/commit-msg
Executable file
@ -0,0 +1,6 @@
|
||||
echo Start running commit-msg hook...
|
||||
|
||||
# Check whether the git commit information is standardized
|
||||
pnpm exec commitlint --edit "$1"
|
||||
|
||||
echo Run commit-msg hook done.
|
7
.husky/pre-commit
Executable file
7
.husky/pre-commit
Executable file
@ -0,0 +1,7 @@
|
||||
# update `.vscode/vben-admin.code-workspace` file
|
||||
pnpm vsh code-workspace --auto-commit
|
||||
|
||||
# Format and submit code according to lintstagedrc.js configuration
|
||||
pnpm exec lint-staged
|
||||
|
||||
echo Run pre-commit hook done.
|
1
.lintstagedrc.mjs
Normal file
1
.lintstagedrc.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@vben/lint-staged-config';
|
27
.ls-lint.yml
Normal file
27
.ls-lint.yml
Normal file
@ -0,0 +1,27 @@
|
||||
ls:
|
||||
.js: kebab-case | pointcase
|
||||
.vue: kebab-case | pointcase
|
||||
.ts: kebab-case | pointcase
|
||||
.tsx: kebab-case | pointcase
|
||||
.jsx: kebab-case | pointcase
|
||||
.css: kebab-case | pointcase
|
||||
.d.ts: kebab-case | pointcase
|
||||
# shadcn 自动生成文件为 PascalCase 格式
|
||||
packages/@vben-core/uikit/shadcn-ui/src/components/ui:
|
||||
.vue: PascalCase
|
||||
|
||||
ignore:
|
||||
- "**/*.png"
|
||||
- "**/*.jpg"
|
||||
- "**/*.jpeg"
|
||||
- "**/*.jpeg"
|
||||
- "**/*.gif"
|
||||
- "**/_util.ts"
|
||||
- "**/deps/**"
|
||||
- "**/dist/**"
|
||||
- "**/node_modules/**"
|
||||
- "**/.turbo/**"
|
||||
- .git
|
||||
- .vscode
|
||||
- .idea
|
||||
- node_modules
|
1
.node-version
Normal file
1
.node-version
Normal file
@ -0,0 +1 @@
|
||||
20
|
12
.npmrc
Normal file
12
.npmrc
Normal file
@ -0,0 +1,12 @@
|
||||
public-hoist-pattern[]=husky
|
||||
public-hoist-pattern[]=eslint
|
||||
public-hoist-pattern[]=prettier
|
||||
public-hoist-pattern[]=prettier-plugin-tailwindcss
|
||||
public-hoist-pattern[]=lint-staged
|
||||
public-hoist-pattern[]=stylelint
|
||||
public-hoist-pattern[]=@commitlint/*
|
||||
public-hoist-pattern[]=czg
|
||||
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
dedupe-peer-dependents=true
|
13
.prettierignore
Normal file
13
.prettierignore
Normal file
@ -0,0 +1,13 @@
|
||||
dist
|
||||
.local
|
||||
.output.js
|
||||
node_modules
|
||||
.nvmrc
|
||||
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
public
|
||||
.npmrc
|
||||
*-lock.yaml
|
1
.prettierrc.mjs
Normal file
1
.prettierrc.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@vben/prettier-config';
|
3
.stylelintignore
Normal file
3
.stylelintignore
Normal file
@ -0,0 +1,3 @@
|
||||
dist
|
||||
public
|
||||
__tests__
|
3
.tazerc.json
Normal file
3
.tazerc.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"exclude": ["zx", "eslint"]
|
||||
}
|
34
.vscode/extensions.json
vendored
Normal file
34
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"recommendations": [
|
||||
// Vue 3 的语言支持
|
||||
"Vue.volar",
|
||||
// 用于 TypeScript 服务器的 Vue 插件
|
||||
"Vue.vscode-typescript-vue-plugin",
|
||||
// 将 ESLint JavaScript 集成到 VS Code 中。
|
||||
"dbaeumer.vscode-eslint",
|
||||
// Visual Studio Code 的官方 Stylelint 扩展
|
||||
"stylelint.vscode-stylelint",
|
||||
// 使用 Prettier 的代码格式化程序
|
||||
"esbenp.prettier-vscode",
|
||||
// 支持 dotenv 文件语法
|
||||
"mikestead.dotenv",
|
||||
// 获取每个 CSS 属性的初始值。
|
||||
"dzhavat.css-initial-value",
|
||||
// 使 VSCode 中的 TypeScript 错误更漂亮、更易于理解
|
||||
"yoavbls.pretty-ts-errors",
|
||||
// 源代码的拼写检查器
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
// Tailwind CSS 的官方 VS Code 插件
|
||||
"bradlc.vscode-tailwindcss",
|
||||
// iconify 图标插件
|
||||
"antfu.iconify",
|
||||
// i18n 插件
|
||||
"Lokalise.i18n-ally",
|
||||
// CSS 变量提示
|
||||
"vunguyentuan.vscode-css-variables"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
// 和 volar 冲突
|
||||
"octref.vetur"
|
||||
]
|
||||
}
|
37
.vscode/global.code-snippets
vendored
Normal file
37
.vscode/global.code-snippets
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"import": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "im",
|
||||
"body": ["import { $2 } from '$1';"],
|
||||
"description": "Import a module",
|
||||
},
|
||||
"export-all": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "ex",
|
||||
"body": ["export * from '$1';"],
|
||||
"description": "Export a module",
|
||||
},
|
||||
"vue-script-setup": {
|
||||
"scope": "vue",
|
||||
"prefix": "<sc",
|
||||
"body": [
|
||||
"<script setup lang=\"ts\">",
|
||||
"const props = defineProps<{",
|
||||
" modelValue?: boolean,",
|
||||
"}>()",
|
||||
"$1",
|
||||
"</script>",
|
||||
"",
|
||||
"<template>",
|
||||
" <div>",
|
||||
" <slot/>",
|
||||
" </div>",
|
||||
"</template>",
|
||||
],
|
||||
},
|
||||
"vue-computed": {
|
||||
"scope": "javascript,typescript,vue",
|
||||
"prefix": "com",
|
||||
"body": ["computed(() => { $1 })"],
|
||||
},
|
||||
}
|
186
.vscode/settings.json
vendored
Normal file
186
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
{
|
||||
"tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
|
||||
// workbench
|
||||
"workbench.list.smoothScrolling": true,
|
||||
"workbench.startupEditor": "newUntitledFile",
|
||||
"workbench.tree.indent": 10,
|
||||
"workbench.editor.highlightModifiedTabs": true,
|
||||
"workbench.editor.closeOnFileDelete": true,
|
||||
"workbench.editor.limit.enabled": true,
|
||||
"workbench.editor.limit.perEditorGroup": true,
|
||||
"workbench.editor.limit.value": 5,
|
||||
|
||||
// editor
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.fontFamily": "Input Mono, FiraCode-Retina, monospace",
|
||||
"editor.fontLigatures": true,
|
||||
"editor.largeFileOptimizations": false,
|
||||
"editor.accessibilitySupport": "off",
|
||||
"editor.cursorSmoothCaretAnimation": "on",
|
||||
"editor.guides.bracketPairs": "active",
|
||||
"editor.inlineSuggest.enabled": true,
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.stickyScroll.enabled": true,
|
||||
"editor.hover.sticky": true,
|
||||
"editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// extensions
|
||||
"extensions.ignoreRecommendations": true,
|
||||
|
||||
// terminal
|
||||
"terminal.integrated.cursorBlinking": true,
|
||||
"terminal.integrated.persistentSessionReviveProcess": "never",
|
||||
"terminal.integrated.tabs.enabled": true,
|
||||
"terminal.integrated.scrollback": 10000,
|
||||
|
||||
// files
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.simpleDialog.enable": true,
|
||||
"files.associations": {
|
||||
"*.ejs": "html",
|
||||
"*.art": "html",
|
||||
"**/tsconfig.json": "jsonc"
|
||||
// "*.json": "jsonc"
|
||||
},
|
||||
|
||||
"files.exclude": {
|
||||
"**/.editorconfig": true,
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.turbo": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.stylelintcache": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/vite.config.mts.*": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.vscode/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/tmp/**": true,
|
||||
"**/bower_components/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
|
||||
// search
|
||||
"search.followSymlinks": false,
|
||||
// 在使用搜索功能时,将这些文件夹/文件排除在外
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/bower_components": true,
|
||||
"**/dist": true,
|
||||
"**/elehukouben": true,
|
||||
"**/.git": true,
|
||||
"**/.github": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/.yarn": true,
|
||||
"**/tmp": true,
|
||||
"*.xml": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"CHANGELOG.md": true,
|
||||
"**/pnpm-lock.yaml": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
|
||||
"debug.onTaskErrors": "debugAnyway",
|
||||
"diffEditor.ignoreTrimWhitespace": false,
|
||||
"npm.packageManager": "pnpm",
|
||||
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"scss.validate": false,
|
||||
|
||||
// extension
|
||||
"emmet.showSuggestionsAsSnippets": true,
|
||||
"emmet.triggerExpansionOnTab": false,
|
||||
|
||||
"errorLens.enabledDiagnosticLevels": ["warning", "error"],
|
||||
"errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"],
|
||||
|
||||
"stylelint.enable": true,
|
||||
"stylelint.packageManager": "pnpm",
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
|
||||
|
||||
// Enable the ESlint flat config support
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"typescript",
|
||||
"javascriptreact",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"json5"
|
||||
],
|
||||
|
||||
"github.copilot.enable": {
|
||||
"*": true,
|
||||
"markdown": true,
|
||||
"plaintext": false,
|
||||
"yaml": false
|
||||
},
|
||||
|
||||
"cssVariables.lookupFiles": [
|
||||
"packages/@vben-core/shared/design-tokens/src/**/*.css"
|
||||
],
|
||||
|
||||
"cSpell.words": [
|
||||
"vben",
|
||||
"iconify",
|
||||
"pinia",
|
||||
"nprogress",
|
||||
"shadcn",
|
||||
"antd",
|
||||
"qrcode",
|
||||
"vueuse",
|
||||
"brotli"
|
||||
],
|
||||
"cSpell.allowCompoundWords": true,
|
||||
"cSpell.language": "en,en-US",
|
||||
|
||||
"i18n-ally.localesPaths": ["packages/locales/src/langs"],
|
||||
"i18n-ally.enabledParsers": ["json", "ts", "js", "yaml"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
|
||||
// 控制相关文件嵌套展示
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.env": "$(capture).env.*",
|
||||
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
||||
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,.ls-lint*",
|
||||
"tailwind.config.mjs": "postcss.*"
|
||||
},
|
||||
"commentTranslate.hover.enabled": true,
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024-present, Vben Admin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
165
README.md
Normal file
165
README.md
Normal file
@ -0,0 +1,165 @@
|
||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="200" height="200" src="https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.0/vben-admin/logo.png"> </a> <br> <br>
|
||||
|
||||
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
|
||||
|
||||
<h1>Vue vben admin</h1>
|
||||
</div>
|
||||
|
||||
**English** | [中文](./README.zh-CN.md)
|
||||
|
||||
## Introduction
|
||||
|
||||
Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite2`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||
|
||||
## Feature
|
||||
|
||||
- **State of The Art Development**:Use front-end front-end technology development such as Vue3/vite2
|
||||
- **TypeScript**: Application-level JavaScript language
|
||||
- **Theming**: Configurable themes
|
||||
- **International**:Built-in complete internationalization program
|
||||
- **Mock Server** Built-in mock data scheme
|
||||
- **Authority** Built-in complete dynamic routing permission generation scheme.
|
||||
- **Component** Multiple commonly used components are encapsulated twice
|
||||
|
||||
## Preview
|
||||
|
||||
- [vue-vben-admin](https://vben.vvbin.cn/) - Full version Chinese site
|
||||
- [vue-vben-admin-gh-pages](https://anncwb.github.io/vue-vben-admin/) - Full version of the github site
|
||||
- [vben-admin-thin-next](https://vben.vvbin.cn/thin/next/) - Simplified Chinese site
|
||||
- [vben-admin-thin-gh-pages](https://anncwb.github.io/vben-admin-thin-next/) -Simplified github site
|
||||
|
||||
Test account: vben/123456
|
||||
|
||||
<p align="center">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||
</p>
|
||||
|
||||
### Use Gitpod
|
||||
|
||||
Open the project in Gitpod (free online dev environment for GitHub) and start coding immediately.
|
||||
|
||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/anncwb/vue-vben-admin)
|
||||
|
||||
## Documentation
|
||||
|
||||
[Document](https://doc.vvbin.cn/)
|
||||
|
||||
## Preparation
|
||||
|
||||
- [node](http://nodejs.org/) and [git](https://git-scm.com/) - Project development environment
|
||||
- [Vite](https://vitejs.dev/) - Familiar with vite features
|
||||
- [Vue3](https://v3.vuejs.org/) - Familiar with Vue basic syntax
|
||||
- [TypeScript](https://www.typescriptlang.org/) - Familiar with the basic syntax of `TypeScript`
|
||||
- [Es6+](http://es6.ruanyifeng.com/) - Familiar with es6 basic syntax
|
||||
- [Vue-Router-Next](https://next.router.vuejs.org/) - Familiar with the basic use of vue-router
|
||||
- [Ant-Design-Vue](https://2x.antdv.com/docs/vue/introduce-cn/) - ui basic use
|
||||
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs basic syntax
|
||||
|
||||
## Install and use
|
||||
|
||||
- Get the project code
|
||||
|
||||
```bash
|
||||
git clone https://github.com/anncwb/vue-vben-admin.git
|
||||
```
|
||||
|
||||
- Installation dependencies
|
||||
|
||||
```bash
|
||||
cd vue-vben-admin
|
||||
|
||||
pnpm install
|
||||
|
||||
```
|
||||
|
||||
- run
|
||||
|
||||
```bash
|
||||
pnpm serve
|
||||
```
|
||||
|
||||
- build
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## Change Log
|
||||
|
||||
[CHANGELOG](./CHANGELOG.zh_CN.md)
|
||||
|
||||
## Project
|
||||
|
||||
- [vue-vben-admin](https://github.com/anncwb/vue-vben-admin) - full version
|
||||
- [vue-vben-admin-thin-next](https://github.com/anncwb/vben-admin-thin-next) - Simplified version
|
||||
|
||||
## How to contribute
|
||||
|
||||
You are very welcome to join![Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) Or submit a Pull Request。
|
||||
|
||||
**Pull Request:**
|
||||
|
||||
1. Fork code!
|
||||
2. Create your own branch: `git checkout -b feat/xxxx`
|
||||
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. Push your branch: `git push origin feat/xxxx`
|
||||
5. submit`pull request`
|
||||
|
||||
## Git Contribution submission specification
|
||||
|
||||
- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||
|
||||
- `feat` Add new features
|
||||
- `fix` Fix the problem/BUG
|
||||
- `style` The code style is related and does not affect the running result
|
||||
- `perf` Optimization/performance improvement
|
||||
- `refactor` Refactor
|
||||
- `revert` Undo edit
|
||||
- `test` Test related
|
||||
- `docs` Documentation/notes
|
||||
- `chore` Dependency update/scaffolding configuration modification etc.
|
||||
- `workflow` Workflow improvements
|
||||
- `ci` Continuous integration
|
||||
- `types` Type definition file changes
|
||||
- `wip` In development
|
||||
|
||||
## Related warehouse
|
||||
|
||||
If these plugins are helpful to you, you can give a star support
|
||||
|
||||
- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) - Used for local and development environment data mock
|
||||
- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) - Used for html template conversion and compression
|
||||
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - Used to pack input .gz|.brotil files
|
||||
|
||||
## Browser support
|
||||
|
||||
The `Chrome 80+` browser is recommended for local development
|
||||
|
||||
Support modern browsers, not IE
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## Maintainer
|
||||
|
||||
[@Vben](https://github.com/anncwb)
|
||||
|
||||
## Donate
|
||||
|
||||
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
|
||||
|
||||
![donate](https://anncwb.github.io/anncwb/images/sponsor.png)
|
||||
|
||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||
|
||||
## Discord
|
||||
|
||||
- [github discussions](https://github.com/anncwb/vue-vben-admin/discussions)
|
||||
- [Discord](https://discord.gg/8GuAdwDhj6)
|
||||
|
||||
## License
|
||||
|
||||
[MIT © Vben-2020](./LICENSE)
|
171
README.zh-CN.md
Normal file
171
README.zh-CN.md
Normal file
@ -0,0 +1,171 @@
|
||||
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="200" height="200" src="https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.0/vben-admin/logo.png"> </a> <br> <br>
|
||||
|
||||
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
|
||||
|
||||
<h1>Vue vben admin</h1>
|
||||
</div>
|
||||
|
||||
**中文** | [English](./README.md)
|
||||
|
||||
## 简介
|
||||
|
||||
Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite2`,`TypeScript`等主流技术开发,开箱即用的中后台前端解决方案,也可用于学习参考。
|
||||
|
||||
## 特性
|
||||
|
||||
- **最新技术栈**:使用 Vue3/vite2 等前端前沿技术开发
|
||||
- **TypeScript**: 应用程序级 JavaScript 的语言
|
||||
- **主题**:可配置的主题
|
||||
- **国际化**:内置完善的国际化方案
|
||||
- **Mock 数据** 内置 Mock 数据方案
|
||||
- **权限** 内置完善的动态路由权限生成方案
|
||||
- **组件** 二次封装了多个常用的组件
|
||||
|
||||
## 预览
|
||||
|
||||
- [vue-vben-admin](https://vben.vvbin.cn/) - 完整版中文站点
|
||||
- [vue-vben-admin-gh-pages](https://anncwb.github.io/vue-vben-admin/) - 完整版 github 站点
|
||||
- [vben-admin-thin-next](https://vben.vvbin.cn/thin/next/) - 简化版中文站点
|
||||
- [vben-admin-thin-gh-pages](https://anncwb.github.io/vben-admin-thin-next/) - 简化版 github 站点
|
||||
|
||||
测试账号: vben/123456
|
||||
|
||||
<p align="center">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||
</p>
|
||||
|
||||
### 使用 Gitpod
|
||||
|
||||
在 Gitpod(适用于 GitHub 的免费在线开发环境)中打开项目,并立即开始编码.
|
||||
|
||||
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/anncwb/vue-vben-admin)
|
||||
|
||||
## 文档
|
||||
|
||||
[文档地址](https://doc.vvbin.cn/)
|
||||
|
||||
## 准备
|
||||
|
||||
- [node](http://nodejs.org/) 和 [git](https://git-scm.com/) -项目开发环境
|
||||
- [Vite](https://vitejs.dev/) - 熟悉 vite 特性
|
||||
- [Vue3](https://v3.vuejs.org/) - 熟悉 Vue 基础语法
|
||||
- [TypeScript](https://www.typescriptlang.org/) - 熟悉`TypeScript`基本语法
|
||||
- [Es6+](http://es6.ruanyifeng.com/) - 熟悉 es6 基本语法
|
||||
- [Vue-Router-Next](https://next.router.vuejs.org/) - 熟悉 vue-router 基本使用
|
||||
- [Ant-Design-Vue](https://2x.antdv.com/docs/vue/introduce-cn/) - ui 基本使用
|
||||
- [Mock.js](https://github.com/nuysoft/Mock) - mockjs 基本语法
|
||||
|
||||
## 安装使用
|
||||
|
||||
- 获取项目代码
|
||||
|
||||
```bash
|
||||
git clone https://github.com/anncwb/vue-vben-admin.git
|
||||
```
|
||||
|
||||
- 安装依赖
|
||||
|
||||
```bash
|
||||
cd vue-vben-admin
|
||||
|
||||
pnpm install
|
||||
|
||||
```
|
||||
|
||||
- 运行
|
||||
|
||||
```bash
|
||||
pnpm serve
|
||||
```
|
||||
|
||||
- 打包
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
|
||||
[CHANGELOG](./CHANGELOG.zh_CN.md)
|
||||
|
||||
## 项目地址
|
||||
|
||||
- [vue-vben-admin](https://github.com/anncwb/vue-vben-admin) - 完整版
|
||||
- [vue-vben-admin-thin-next](https://github.com/anncwb/vben-admin-thin-next) - 简化版
|
||||
|
||||
## 如何贡献
|
||||
|
||||
非常欢迎你的加入![提一个 Issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 或者提交一个 Pull Request。
|
||||
|
||||
**Pull Request:**
|
||||
|
||||
1. Fork 代码!
|
||||
2. 创建自己的分支: `git checkout -b feat/xxxx`
|
||||
3. 提交你的修改: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. 推送您的分支: `git push origin feat/xxxx`
|
||||
5. 提交`pull request`
|
||||
|
||||
## Git 贡献提交规范
|
||||
|
||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||
|
||||
- `feat` 增加新功能
|
||||
- `fix` 修复问题/BUG
|
||||
- `style` 代码风格相关无影响运行结果的
|
||||
- `perf` 优化/性能提升
|
||||
- `refactor` 重构
|
||||
- `revert` 撤销修改
|
||||
- `test` 测试相关
|
||||
- `docs` 文档/注释
|
||||
- `chore` 依赖更新/脚手架配置修改等
|
||||
- `workflow` 工作流改进
|
||||
- `ci` 持续集成
|
||||
- `types` 类型定义文件更改
|
||||
- `wip` 开发中
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
本地开发推荐使用`Chrome 80+` 浏览器
|
||||
|
||||
支持现代浏览器, 不支持 IE
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 相关仓库
|
||||
|
||||
如果这些插件对你有帮助,可以给一个 star 支持下
|
||||
|
||||
- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) - 用于本地及开发环境数据 mock
|
||||
- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) - 用于 html 模版转换及压缩
|
||||
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - 用于打包输出.gz|.brotil 文件
|
||||
|
||||
## 后台整合示例
|
||||
|
||||
- [lamp-cloud](https://github.com/zuihou/lamp-cloud) - 基于 SpringCloud Alibaba 的微服务中后台快速开发平台
|
||||
- [matecloud](https://github.com/matevip/matecloud) - MateCloud 微服务脚手架,基于 Spring Cloud 2020.0.3、SpringBoot 2.5.3 的全开源平台
|
||||
|
||||
## 维护者
|
||||
|
||||
[@Vben](https://github.com/anncwb)
|
||||
|
||||
## 捐赠
|
||||
|
||||
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
||||
|
||||
![donate](https://anncwb.github.io/anncwb/images/sponsor.png)
|
||||
|
||||
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||
|
||||
## 交流
|
||||
|
||||
`Vue-vben-Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供 QQ 交流群使用问题欢迎在群内提问。
|
||||
|
||||
- QQ 群 `569291866`
|
||||
|
||||
## License
|
||||
|
||||
[MIT © Vben-2020](./LICENSE)
|
2
apps/antd-view/.env
Normal file
2
apps/antd-view/.env
Normal file
@ -0,0 +1,2 @@
|
||||
# spa-title
|
||||
VITE_GLOB_APP_TITLE = Vben Admin Pro
|
6
apps/antd-view/.env.analyze
Normal file
6
apps/antd-view/.env.analyze
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/vben-api
|
3
apps/antd-view/.env.development
Normal file
3
apps/antd-view/.env.development
Normal file
@ -0,0 +1,3 @@
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
VITE_GLOB_API_URL=/vben-api
|
5
apps/antd-view/.env.production
Normal file
5
apps/antd-view/.env.production
Normal file
@ -0,0 +1,5 @@
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/vben-api
|
22
apps/antd-view/index.html
Normal file
22
apps/antd-view/index.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta name="description" content="A Modern Back-end Management System" />
|
||||
<meta name="keywords" content="Vben Admin Pro Vue3 Vite" />
|
||||
<meta name="author" content="Vben" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||
/>
|
||||
<!-- 由 vite 注入 VITE_GLOB_APP_TITLE 变量,在 . env 内配置 -->
|
||||
<title><%= VITE_GLOB_APP_TITLE %></title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
33
apps/antd-view/mock/_util.ts
Normal file
33
apps/antd-view/mock/_util.ts
Normal file
@ -0,0 +1,33 @@
|
||||
function resultSuccess<T = Record<string, any>>(
|
||||
result: T,
|
||||
{ message = 'ok' } = {},
|
||||
) {
|
||||
return {
|
||||
code: 0,
|
||||
message,
|
||||
result,
|
||||
type: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
function resultError(
|
||||
message = 'Request failed',
|
||||
{ code = -1, result = null } = {},
|
||||
) {
|
||||
return {
|
||||
code,
|
||||
message,
|
||||
result,
|
||||
type: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh_CN 本函数用于从request数据中获取token,请根据项目的实际情况修改
|
||||
*
|
||||
*/
|
||||
function getRequestToken({ headers }: any): string | undefined {
|
||||
return headers?.authorization;
|
||||
}
|
||||
|
||||
export { getRequestToken, resultError, resultSuccess };
|
101
apps/antd-view/mock/user.ts
Normal file
101
apps/antd-view/mock/user.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { getRequestToken, resultError, resultSuccess } from './_util';
|
||||
|
||||
const fakeUserList = [
|
||||
{
|
||||
accessToken: 'fakeAdminToken',
|
||||
avatar: '',
|
||||
desc: 'manager',
|
||||
homePath: '/welcome',
|
||||
password: '123456',
|
||||
realName: 'Vben Admin',
|
||||
roles: [
|
||||
{
|
||||
roleName: 'Super Admin',
|
||||
value: 'super',
|
||||
},
|
||||
],
|
||||
userId: '1',
|
||||
username: 'vben',
|
||||
},
|
||||
{
|
||||
accessToken: 'fakeTestToken',
|
||||
avatar: '',
|
||||
desc: 'tester',
|
||||
homePath: '/welcome',
|
||||
password: '123456',
|
||||
realName: 'test user',
|
||||
roles: [
|
||||
{
|
||||
roleName: 'Tester',
|
||||
value: 'test',
|
||||
},
|
||||
],
|
||||
userId: '2',
|
||||
username: 'test',
|
||||
},
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
method: 'post',
|
||||
response: ({ body }: any) => {
|
||||
const { password, username } = body;
|
||||
const checkUser = fakeUserList.find(
|
||||
(item) => item.username === username && password === item.password,
|
||||
);
|
||||
if (!checkUser) {
|
||||
return resultError('Incorrect account or password!');
|
||||
}
|
||||
const {
|
||||
accessToken,
|
||||
desc,
|
||||
realName,
|
||||
roles,
|
||||
userId,
|
||||
username: _username,
|
||||
} = checkUser;
|
||||
return resultSuccess({
|
||||
accessToken,
|
||||
desc,
|
||||
realName,
|
||||
roles,
|
||||
userId,
|
||||
username: _username,
|
||||
});
|
||||
},
|
||||
timeout: 200,
|
||||
url: '/vben-api/login',
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
response: (request: any) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) return resultError('Invalid token');
|
||||
const checkUser = fakeUserList.find((item) => item.accessToken === token);
|
||||
if (!checkUser) {
|
||||
return resultError(
|
||||
'The corresponding user information was not obtained!',
|
||||
);
|
||||
}
|
||||
const { accessToken: _token, password: _pwd, ...rest } = checkUser;
|
||||
return resultSuccess(rest);
|
||||
},
|
||||
url: '/vben-api/getUserInfo',
|
||||
},
|
||||
{
|
||||
method: 'get',
|
||||
response: (request: any) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) return resultError('Invalid token');
|
||||
const checkUser = fakeUserList.find((item) => item.accessToken === token);
|
||||
if (!checkUser) {
|
||||
return resultError('Invalid token!');
|
||||
}
|
||||
return resultSuccess(undefined, {
|
||||
message: 'Token has been destroyed',
|
||||
});
|
||||
},
|
||||
timeout: 200,
|
||||
url: '/vben-api/logout',
|
||||
},
|
||||
];
|
47
apps/antd-view/package.json
Normal file
47
apps/antd-view/package.json
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "@vben/antd-view",
|
||||
"version": "4.0.0-alpha.1",
|
||||
"type": "module",
|
||||
"author": {
|
||||
"name": "vben",
|
||||
"email": "anncwb@126.com",
|
||||
"url": "https://github.com/anncwb"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "apps/vben-admin"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm vite build",
|
||||
"dev": "pnpm vite",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/design": "workspace:*",
|
||||
"@vben-core/design-tokens": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vben/common-ui": "workspace:*",
|
||||
"@vben/hooks": "workspace:*",
|
||||
"@vben/icons": "workspace:*",
|
||||
"@vben/layouts": "workspace:*",
|
||||
"@vben/locales": "workspace:*",
|
||||
"@vben/preference": "workspace:*",
|
||||
"@vben/stores": "workspace:*",
|
||||
"ant-design-vue": "^4.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"dayjs": "^1.11.11",
|
||||
"vue": "^3.4.27",
|
||||
"vue-router": "^4.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite-plugin-mock": "^3.0.2"
|
||||
}
|
||||
}
|
1
apps/antd-view/postcss.config.mjs
Normal file
1
apps/antd-view/postcss.config.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config/postcss';
|
BIN
apps/antd-view/public/favicon.ico
Normal file
BIN
apps/antd-view/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 894 B |
40
apps/antd-view/src/app.vue
Normal file
40
apps/antd-view/src/app.vue
Normal file
@ -0,0 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import 'dayjs/locale/zh-cn';
|
||||
|
||||
import { GlobalProvider } from '@vben/common-ui';
|
||||
import { preference, usePreference } from '@vben/preference';
|
||||
import { ConfigProvider, theme } from 'ant-design-vue';
|
||||
import zhCN from 'ant-design-vue/es/locale/zh_CN';
|
||||
import dayjs from 'dayjs';
|
||||
import { computed } from 'vue';
|
||||
|
||||
defineOptions({ name: 'App' });
|
||||
|
||||
dayjs.locale(zhCN.locale);
|
||||
|
||||
const { isDark } = usePreference();
|
||||
|
||||
const tokenTheme = computed(() => {
|
||||
const { colorPrimary, compact } = preference;
|
||||
const algorithms = isDark.value
|
||||
? [theme.darkAlgorithm]
|
||||
: [theme.defaultAlgorithm];
|
||||
|
||||
// antd 紧凑模式算法
|
||||
if (compact) {
|
||||
algorithms.push(theme.compactAlgorithm);
|
||||
}
|
||||
return {
|
||||
algorithms,
|
||||
token: { colorPrimary },
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<GlobalProvider>
|
||||
<ConfigProvider :locale="zhCN" :theme="tokenTheme">
|
||||
<RouterView />
|
||||
</ConfigProvider>
|
||||
</GlobalProvider>
|
||||
</template>
|
127
apps/antd-view/src/layout.vue
Normal file
127
apps/antd-view/src/layout.vue
Normal file
@ -0,0 +1,127 @@
|
||||
<script lang="ts" setup>
|
||||
import type { NotificationItem } from '@vben/common-ui';
|
||||
|
||||
import { openWindow } from '@vben-core/toolkit';
|
||||
|
||||
import { Notification, UserDropdown } from '@vben/common-ui';
|
||||
import {
|
||||
IcRoundCreditScore,
|
||||
IcRoundSettingsSuggest,
|
||||
MdiDriveDocument,
|
||||
MdiGithub,
|
||||
} from '@vben/icons';
|
||||
import { BasicLayout } from '@vben/layouts';
|
||||
import { $t } from '@vben/locales';
|
||||
import { preference } from '@vben/preference';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
// https://avatar.vercel.sh/vercel.svg?text=Vaa
|
||||
// https://avatar.vercel.sh/1
|
||||
// https://avatar.vercel.sh/nextjs
|
||||
// https://avatar.vercel.sh/satori
|
||||
const notifications = ref<NotificationItem[]>([
|
||||
{
|
||||
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
|
||||
date: '3小时前',
|
||||
isRead: true,
|
||||
message: '描述信息描述信息描述信息',
|
||||
title: '收到了 14 份新周报',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatar.vercel.sh/1',
|
||||
date: '刚刚',
|
||||
isRead: false,
|
||||
message: '描述信息描述信息描述信息',
|
||||
title: '朱偏右 回复了你',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatar.vercel.sh/1',
|
||||
date: '2024-01-01',
|
||||
isRead: false,
|
||||
message: '描述信息描述信息描述信息',
|
||||
title: '曲丽丽 评论了你',
|
||||
},
|
||||
{
|
||||
avatar: 'https://avatar.vercel.sh/satori',
|
||||
date: '1天前',
|
||||
isRead: false,
|
||||
message: '描述信息描述信息描述信息',
|
||||
title: '代办提醒',
|
||||
},
|
||||
]);
|
||||
|
||||
const menus = computed(() => [
|
||||
{
|
||||
handler: () => {
|
||||
openWindow('https://github.com/vbenjs/vue-vben-admin', {
|
||||
target: '_blank',
|
||||
});
|
||||
},
|
||||
icon: MdiDriveDocument,
|
||||
text: $t('widgets.document'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow('https://github.com/vbenjs/vue-vben-admin', {
|
||||
target: '_blank',
|
||||
});
|
||||
},
|
||||
icon: MdiGithub,
|
||||
text: 'GitHub',
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
openWindow('https://github.com/vbenjs/vue-vben-admin/issues', {
|
||||
target: '_blank',
|
||||
});
|
||||
},
|
||||
icon: IcRoundCreditScore,
|
||||
text: $t('widgets.qa'),
|
||||
},
|
||||
{
|
||||
handler: () => {
|
||||
// openWindow('https://github.com/vbenjs/vue-vben-admin', {
|
||||
// target: '_blank',
|
||||
// });
|
||||
},
|
||||
icon: IcRoundSettingsSuggest,
|
||||
text: $t('widgets.setting'),
|
||||
},
|
||||
]);
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
const router = useRouter();
|
||||
|
||||
function handleLogout() {
|
||||
accessStore.$reset();
|
||||
router.replace('/auth/login');
|
||||
}
|
||||
|
||||
function handleNoticeClear() {
|
||||
notifications.value = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicLayout>
|
||||
<template #user-dropdown>
|
||||
<UserDropdown
|
||||
:avatar="preference.defaultAvatar"
|
||||
:menus="menus"
|
||||
text="Vben Admin"
|
||||
description="ann.vben@gmail.com"
|
||||
tag-text="Pro"
|
||||
@logout="handleLogout"
|
||||
/>
|
||||
</template>
|
||||
<template #notification>
|
||||
<Notification
|
||||
dot
|
||||
:notifications="notifications"
|
||||
@clear="handleNoticeClear"
|
||||
/>
|
||||
</template>
|
||||
</BasicLayout>
|
||||
</template>
|
43
apps/antd-view/src/main.ts
Normal file
43
apps/antd-view/src/main.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import '@vben-core/design/tailwind';
|
||||
|
||||
import '@vben-core/design';
|
||||
import '@vben-core/design-tokens';
|
||||
|
||||
import { setupI18n } from '@vben/locales';
|
||||
import { preference, setupPreference } from '@vben/preference';
|
||||
import { setupStore } from '@vben/stores';
|
||||
import { createApp } from 'vue';
|
||||
|
||||
import App from './app.vue';
|
||||
import { overridesPreference } from './preference';
|
||||
import { router } from './router';
|
||||
|
||||
async function bootstrap(cachePrefix: string) {
|
||||
// app偏好设置
|
||||
await setupPreference({
|
||||
cachePrefix,
|
||||
overrides: overridesPreference,
|
||||
});
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 国际化 i18n 配置
|
||||
await setupI18n(app, { defaultLocale: preference.locale });
|
||||
|
||||
// 配置 pinia-store
|
||||
await setupStore(app, { cachePrefix });
|
||||
|
||||
// 配置路由及路由守卫
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
// production mock server
|
||||
if (import.meta.env.PROD) {
|
||||
import('./mock-prod-server').then(({ setupProdMockServer }) => {
|
||||
setupProdMockServer();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bootstrap('vben-admin-pro-antd');
|
10
apps/antd-view/src/mock-prod-server.ts
Normal file
10
apps/antd-view/src/mock-prod-server.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { createProdMockServer } from 'vite-plugin-mock/client';
|
||||
|
||||
// 逐一导入您的mock.ts文件
|
||||
// 如果使用vite.mock.config.ts,只需直接导入文件
|
||||
// 可以使用 import.meta.glob功能来进行全部导入
|
||||
import userModule from '../mock/user';
|
||||
|
||||
export function setupProdMockServer() {
|
||||
createProdMockServer([...userModule]);
|
||||
}
|
7
apps/antd-view/src/preference.ts
Normal file
7
apps/antd-view/src/preference.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import type { Preference } from '@vben-core/typings';
|
||||
|
||||
/**
|
||||
* @description 项目配置文件
|
||||
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
|
||||
*/
|
||||
export const overridesPreference: Partial<Preference> = {};
|
183
apps/antd-view/src/router/guard/access.ts
Normal file
183
apps/antd-view/src/router/guard/access.ts
Normal file
@ -0,0 +1,183 @@
|
||||
import type { ExRouteRecordRaw, MenuRecordRaw } from '@vben-core/typings';
|
||||
|
||||
import { filterTree, mapTree, traverseTreeValues } from '@vben-core/toolkit';
|
||||
import type { RouteRecordRaw, Router } from 'vue-router';
|
||||
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { dynamicRoutes } from '../routes';
|
||||
|
||||
// 登录页面路由 path
|
||||
const LOGIN_ROUTE_PATH = '/auth/login';
|
||||
// 不需要权限的页面白名单
|
||||
const WHITE_ROUTE_NAMES = new Set<string>([]);
|
||||
|
||||
/**
|
||||
* 权限访问守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function configAccessGuard(router: Router) {
|
||||
router.beforeEach(async (to, from) => {
|
||||
const accessStore = useAccessStore();
|
||||
const accessToken = accessStore.getAccessToken;
|
||||
|
||||
// accessToken 检查
|
||||
if (!accessToken) {
|
||||
// 明确声明忽略权限访问权限,则可以访问
|
||||
if (to.meta.ignoreAccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 白名单路由列表检查
|
||||
if (WHITE_ROUTE_NAMES.has(to.name as string)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 没有访问权限,跳转登录页面
|
||||
if (to.fullPath !== LOGIN_ROUTE_PATH) {
|
||||
return {
|
||||
path: LOGIN_ROUTE_PATH,
|
||||
// 如不需要,直接删除 query
|
||||
query: { redirect: encodeURIComponent(to.fullPath) },
|
||||
// 携带当前跳转的页面,登录后重新跳转该页面
|
||||
replace: true,
|
||||
};
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
const accessRoutes = accessStore.getAccessRoutes;
|
||||
|
||||
// 是否已经生成过动态路由
|
||||
if (accessRoutes && accessRoutes.length > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 生成路由表
|
||||
// 当前登录用户拥有的角色标识列表
|
||||
const userRoles = accessStore.getUserRoles;
|
||||
const routes = await generatorRoutes(userRoles);
|
||||
// 动态添加到router实例内
|
||||
routes.forEach((route) => router.addRoute(route));
|
||||
|
||||
const menus = await generatorMenus(routes, router);
|
||||
|
||||
// 保存菜单信息和路由信息
|
||||
accessStore.setAccessMenus(menus);
|
||||
accessStore.setAccessRoutes(routes);
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
const redirect = decodeURIComponent(redirectPath);
|
||||
|
||||
return {
|
||||
path: redirect,
|
||||
replace: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态生成路由
|
||||
*/
|
||||
async function generatorRoutes(roles: string[]): Promise<RouteRecordRaw[]> {
|
||||
// 根据角色标识过滤路由表,判断当前用户是否拥有指定权限
|
||||
return filterTree(dynamicRoutes, (route) => {
|
||||
return hasVisible(route) && hasAuthority(route, roles);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 routes 生成菜单列表
|
||||
* @param routes
|
||||
*/
|
||||
async function generatorMenus(
|
||||
routes: RouteRecordRaw[],
|
||||
router: Router,
|
||||
): Promise<MenuRecordRaw[]> {
|
||||
// 获取所有router最终的path及name
|
||||
const finalRoutes = traverseTreeValues(
|
||||
router.getRoutes(),
|
||||
({ name, path }) => {
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const menus = mapTree<ExRouteRecordRaw, MenuRecordRaw>(routes, (route) => {
|
||||
// 路由表的路径写法有多种,这里从router获取到最终的path并赋值
|
||||
const matchRoute = finalRoutes.find(
|
||||
(finalRoute) => finalRoute.name === route.name,
|
||||
);
|
||||
|
||||
// 转换为菜单结构
|
||||
const path = matchRoute?.path ?? route.path;
|
||||
const { meta, name: routeName, redirect, children } = route;
|
||||
const {
|
||||
badge,
|
||||
badgeType,
|
||||
badgeVariants,
|
||||
hideChildrenInMenu = false,
|
||||
icon,
|
||||
orderNo,
|
||||
title = '',
|
||||
} = meta || {};
|
||||
|
||||
const name = (title || routeName || '') as string;
|
||||
|
||||
// 隐藏子菜单
|
||||
const resultChildren = hideChildrenInMenu
|
||||
? []
|
||||
: (children as MenuRecordRaw[]);
|
||||
|
||||
// 将菜单的所有父级和父级菜单记录到菜单项内
|
||||
if (resultChildren && resultChildren.length > 0) {
|
||||
resultChildren.forEach((child) => {
|
||||
child.parents = [...(route.parents || []), path];
|
||||
child.parent = path;
|
||||
});
|
||||
}
|
||||
// 隐藏子菜单
|
||||
const resultPath = hideChildrenInMenu ? redirect : path;
|
||||
return {
|
||||
badge,
|
||||
badgeType,
|
||||
badgeVariants,
|
||||
icon,
|
||||
name,
|
||||
orderNo,
|
||||
parent: route.parent,
|
||||
parents: route.parents,
|
||||
path: resultPath,
|
||||
children: resultChildren,
|
||||
};
|
||||
});
|
||||
|
||||
return menus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断路由是否有权限访问
|
||||
* @param route
|
||||
* @param access
|
||||
*/
|
||||
function hasAuthority(route: RouteRecordRaw, access: string[]) {
|
||||
const authority = route.meta?.authority;
|
||||
if (!authority) {
|
||||
return true;
|
||||
}
|
||||
const authSet = new Set(authority);
|
||||
return access.some((value) => {
|
||||
return authSet.has(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断路由是否需要在菜单中显示
|
||||
* @param route
|
||||
*/
|
||||
function hasVisible(route: RouteRecordRaw) {
|
||||
return !route.meta?.hideInMenu;
|
||||
}
|
||||
|
||||
export { configAccessGuard };
|
43
apps/antd-view/src/router/guard/index.ts
Normal file
43
apps/antd-view/src/router/guard/index.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { startProgress, stopProgress } from '@vben-core/toolkit';
|
||||
import type { Router } from 'vue-router';
|
||||
|
||||
import { preference } from '@vben/preference';
|
||||
|
||||
import { configAccessGuard } from './access';
|
||||
|
||||
/**
|
||||
* 通用守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function configCommonGuard(router: Router) {
|
||||
const loadedPaths = new Set<string>();
|
||||
|
||||
router.beforeEach(async (to) => {
|
||||
if (preference.pageProgress) {
|
||||
startProgress();
|
||||
}
|
||||
to.meta.loaded = loadedPaths.has(to.path);
|
||||
return true;
|
||||
});
|
||||
|
||||
router.afterEach((to) => {
|
||||
// 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行
|
||||
loadedPaths.add(to.path);
|
||||
if (preference.pageProgress) {
|
||||
stopProgress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目守卫配置
|
||||
* @param router
|
||||
*/
|
||||
function createRouteGuard(router: Router) {
|
||||
/** 通用 */
|
||||
configCommonGuard(router);
|
||||
/** 权限访问 */
|
||||
configAccessGuard(router);
|
||||
}
|
||||
|
||||
export { createRouteGuard };
|
59
apps/antd-view/src/router/index.ts
Normal file
59
apps/antd-view/src/router/index.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { traverseTreeValues } from '@vben-core/toolkit';
|
||||
import type { RouteRecordName, RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { createRouter, createWebHashHistory } from 'vue-router';
|
||||
|
||||
import { createRouteGuard } from './guard';
|
||||
import { staticRoutes } from './routes';
|
||||
|
||||
/**
|
||||
* @zh_CN 创建vue-router实例
|
||||
*/
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
|
||||
// 应该添加到路由的初始路由列表。
|
||||
routes: staticRoutes,
|
||||
scrollBehavior: (to, from, savedPosition) => {
|
||||
if (to.path !== from.path) {
|
||||
setTimeout(() => {
|
||||
const app = document.querySelector('#app');
|
||||
if (app) {
|
||||
app.scrollTop = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
return savedPosition || { left: 0, top: 0 };
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @zh_CN 重置所有路由,如有指定白名单除外
|
||||
*/
|
||||
function resetRoutes() {
|
||||
// 获取静态路由所有节点包含子节点的 name,并排除不存在 name 字段的路由
|
||||
const staticRouteNames = traverseTreeValues<
|
||||
RouteRecordRaw,
|
||||
RouteRecordName | undefined
|
||||
>(staticRoutes, (route) => {
|
||||
// 这些路由需要指定 name,防止在路由重置时,不能删除没有指定 name 的路由
|
||||
if (!route.name) {
|
||||
console.warn(
|
||||
`The route with the path ${route.path} needs to specify the field name.`,
|
||||
);
|
||||
}
|
||||
return route.name;
|
||||
});
|
||||
|
||||
const { getRoutes, hasRoute, removeRoute } = router;
|
||||
const routes = getRoutes();
|
||||
routes.forEach(({ name }) => {
|
||||
// 存在于路由表且非白名单才需要删除
|
||||
if (name && !staticRouteNames.includes(name) && hasRoute(name)) {
|
||||
removeRoute(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 创建路由守卫
|
||||
createRouteGuard(router);
|
||||
|
||||
export { resetRoutes, router };
|
78
apps/antd-view/src/router/routes/builtin.ts
Normal file
78
apps/antd-view/src/router/routes/builtin.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { Fallback } from '@vben/common-ui';
|
||||
|
||||
import { AuthPageLayout } from './layout';
|
||||
|
||||
/** 静态路由列表,访问这些页面可以不需要权限 */
|
||||
const builtinRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: AuthPageLayout,
|
||||
meta: {
|
||||
title: 'Authentication',
|
||||
},
|
||||
name: 'Authentication',
|
||||
path: '/auth',
|
||||
children: [
|
||||
{
|
||||
name: 'Login',
|
||||
path: 'login',
|
||||
component: () => import('@/views/authentication/login.vue'),
|
||||
meta: {
|
||||
ignoreAccess: true,
|
||||
title: 'Login',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'CodeLogin',
|
||||
path: 'code-login',
|
||||
component: () => import('@/views/authentication/code-login.vue'),
|
||||
meta: {
|
||||
ignoreAccess: true,
|
||||
title: 'CodeLogin',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'QrCodeLogin',
|
||||
path: 'qrcode-login',
|
||||
component: () => import('@/views/authentication/qrcode-login.vue'),
|
||||
meta: {
|
||||
ignoreAccess: true,
|
||||
title: 'QrCodeLogin',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ForgetPassword',
|
||||
path: 'forget-password',
|
||||
component: () => import('@/views/authentication/forget-password.vue'),
|
||||
meta: {
|
||||
ignoreAccess: true,
|
||||
title: 'ForgetPassword',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Register',
|
||||
path: 'register',
|
||||
component: () => import('@/views/authentication/register.vue'),
|
||||
meta: {
|
||||
ignoreAccess: true,
|
||||
title: 'Register',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// 错误页
|
||||
{
|
||||
component: Fallback,
|
||||
meta: {
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
title: 'Fallback',
|
||||
},
|
||||
name: 'Fallback',
|
||||
path: '/:path(.*)*',
|
||||
},
|
||||
];
|
||||
|
||||
export { builtinRoutes };
|
66
apps/antd-view/src/router/routes/index.ts
Normal file
66
apps/antd-view/src/router/routes/index.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { builtinRoutes } from './builtin';
|
||||
import { Layout } from './layout';
|
||||
import { nestedRoutes } from './modules/nested';
|
||||
import { outsideRoutes } from './modules/outside';
|
||||
|
||||
/** 动态路由 */
|
||||
const dynamicRoutes: RouteRecordRaw[] = [
|
||||
// 根路由
|
||||
{
|
||||
component: Layout,
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
title: '首页',
|
||||
},
|
||||
name: 'Home',
|
||||
path: '/',
|
||||
redirect: '/welcome',
|
||||
children: [
|
||||
{
|
||||
name: 'Welcome',
|
||||
path: '/welcome',
|
||||
component: () => import('@/views/dashboard/index.vue'),
|
||||
meta: {
|
||||
affixTab: true,
|
||||
title: 'Welcome',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
...nestedRoutes,
|
||||
...outsideRoutes,
|
||||
// 关于
|
||||
{
|
||||
component: Layout,
|
||||
meta: {
|
||||
hideChildrenInMenu: true,
|
||||
icon: 'https://cdn.jsdelivr.net/gh/vbenjs/vben-cdn-static@0.1.2/vben-admin/admin-logo.png',
|
||||
keepAlive: false,
|
||||
title: '关于',
|
||||
},
|
||||
name: 'AboutLayout',
|
||||
path: '/about',
|
||||
redirect: '/about/index',
|
||||
children: [
|
||||
{
|
||||
name: 'About',
|
||||
path: 'index',
|
||||
component: () => import('@/views/about/index.vue'),
|
||||
meta: {
|
||||
keepAlive: false,
|
||||
title: '关于',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/** 排除在主框架外的路由,这些路由没有菜单和顶部及其他框架内容 */
|
||||
const externalRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
/** 静态路由列表,访问这些页面可以不需要权限 */
|
||||
const staticRoutes: RouteRecordRaw[] = [...builtinRoutes];
|
||||
|
||||
export { dynamicRoutes, externalRoutes, staticRoutes };
|
8
apps/antd-view/src/router/routes/layout.ts
Normal file
8
apps/antd-view/src/router/routes/layout.ts
Normal file
@ -0,0 +1,8 @@
|
||||
const Layout = () => import('@/layout.vue');
|
||||
|
||||
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||
|
||||
const AuthPageLayout = () =>
|
||||
import('@vben/layouts').then((m) => m.AuthPageLayout);
|
||||
|
||||
export { AuthPageLayout, IFrameView, Layout };
|
71
apps/antd-view/src/router/routes/modules/nested.ts
Normal file
71
apps/antd-view/src/router/routes/modules/nested.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { Layout } from '../layout';
|
||||
|
||||
export const nestedRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: Layout,
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '多级菜单',
|
||||
},
|
||||
name: 'Nested',
|
||||
path: '/nested',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu1',
|
||||
path: 'menu1',
|
||||
component: () => import('@/views/nested/menu-1.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '菜单1',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu2',
|
||||
path: 'menu2',
|
||||
component: () => import('@/views/nested/menu-2.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '菜单2',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu3',
|
||||
path: 'menu3',
|
||||
meta: {
|
||||
title: '菜单3',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Menu31',
|
||||
path: 'menu3-1',
|
||||
component: () => import('@/views/nested/menu-3-1.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '菜单3-1',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu32',
|
||||
path: 'menu3-2',
|
||||
meta: {
|
||||
title: '菜单3-2',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Menu321',
|
||||
path: 'menu3-2-1',
|
||||
component: () => import('@/views/nested/menu-3-2-1.vue'),
|
||||
meta: {
|
||||
keepAlive: true,
|
||||
title: '菜单3-2-1',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
37
apps/antd-view/src/router/routes/modules/outside.ts
Normal file
37
apps/antd-view/src/router/routes/modules/outside.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { IFrameView, Layout } from '../layout';
|
||||
|
||||
export const outsideRoutes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '外部页面',
|
||||
},
|
||||
name: 'Outside',
|
||||
path: '/outside',
|
||||
redirect: '/outside/document',
|
||||
children: [
|
||||
{
|
||||
name: 'Document',
|
||||
path: 'document',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
iframeSrc: 'https://doc.vvbin.cn/',
|
||||
// keepAlive: true,
|
||||
title: '项目文档',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'IFrameView',
|
||||
path: 'vue-document',
|
||||
component: IFrameView,
|
||||
meta: {
|
||||
iframeSrc: 'https://cn.vuejs.org/',
|
||||
keepAlive: true,
|
||||
title: 'Vue 文档(缓存)',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
1
apps/antd-view/src/services/index.ts
Normal file
1
apps/antd-view/src/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './modules/user';
|
23
apps/antd-view/src/services/modules/user/index.ts
Normal file
23
apps/antd-view/src/services/modules/user/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { UserInfo } from '@vben-core/typings';
|
||||
|
||||
import { request } from '@/services/request';
|
||||
|
||||
import type { UserApi } from './typing';
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
async function userLogin(data: UserApi.LoginParams) {
|
||||
return request<UserApi.LoginResult>('/login', { data, method: 'post' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
async function getUserInfo() {
|
||||
return request<UserInfo>('/getUserInfo', { method: 'get' });
|
||||
}
|
||||
|
||||
export { getUserInfo, userLogin };
|
||||
|
||||
export type { UserApi } from './typing';
|
18
apps/antd-view/src/services/modules/user/typing.ts
Normal file
18
apps/antd-view/src/services/modules/user/typing.ts
Normal file
@ -0,0 +1,18 @@
|
||||
namespace UserApi {
|
||||
/** 登录接口参数 */
|
||||
export interface LoginParams {
|
||||
password: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
/** 登录接口返回值 */
|
||||
export interface LoginResult {
|
||||
accessToken: string;
|
||||
desc: string;
|
||||
realName: string;
|
||||
userId: string;
|
||||
username: string;
|
||||
}
|
||||
}
|
||||
|
||||
export type { UserApi };
|
152
apps/antd-view/src/services/request.ts
Normal file
152
apps/antd-view/src/services/request.ts
Normal file
@ -0,0 +1,152 @@
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { message } from 'ant-design-vue';
|
||||
import axios, {
|
||||
AxiosError,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
InternalAxiosRequestConfig,
|
||||
} from 'axios';
|
||||
|
||||
// 后端需要的 token 存放在header内的key字段
|
||||
// 可以根据自己的需要修改
|
||||
const REQUEST_HEADER_TOKEN_KEY = 'Authorization';
|
||||
|
||||
type HttpConfig = InternalAxiosRequestConfig;
|
||||
|
||||
interface HttpResponse<T = any> {
|
||||
code: number;
|
||||
message: string;
|
||||
result: T;
|
||||
}
|
||||
|
||||
// 用于存储每个请求的标识和取消函数
|
||||
const pendingMap = new Map<string, AbortController>();
|
||||
|
||||
const getPendingUrl = (config: AxiosRequestConfig): string => {
|
||||
return [config.method, config.url].join('&');
|
||||
};
|
||||
|
||||
/**
|
||||
* 添加请求
|
||||
* @param config 请求配置
|
||||
*/
|
||||
function addRequestSignal(config: AxiosRequestConfig): void {
|
||||
abortRequest(config);
|
||||
const url = getPendingUrl(config);
|
||||
const controller = new AbortController();
|
||||
config.signal = config.signal || controller.signal;
|
||||
if (!pendingMap.has(url)) {
|
||||
// 如果当前请求不在等待中,将其添加到等待中
|
||||
pendingMap.set(url, controller);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有等待中的请求
|
||||
*/
|
||||
function abortAllRequest() {
|
||||
pendingMap.forEach((abortController) => {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
}
|
||||
});
|
||||
pendingMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除请求
|
||||
* @param config 请求配置
|
||||
*/
|
||||
function abortRequest(config: AxiosRequestConfig): void {
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const url = getPendingUrl(config);
|
||||
if (pendingMap.has(url)) {
|
||||
// 如果当前请求在等待中,取消它并将其从等待中移除
|
||||
const abortController = pendingMap.get(url);
|
||||
if (abortController) {
|
||||
abortController.abort(url);
|
||||
}
|
||||
pendingMap.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
// .env 环境获取请求地址
|
||||
baseURL: import.meta.env.VITE_GLOB_API_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
},
|
||||
timeout: 10 * 1000,
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
axiosInstance.interceptors.request.use(
|
||||
(config: HttpConfig) => {
|
||||
addRequestSignal(config);
|
||||
|
||||
// 携带 getAccessToken 在请求头
|
||||
const accessStore = useAccessStore();
|
||||
const getAccessToken = accessStore.getAccessToken;
|
||||
|
||||
if (getAccessToken) {
|
||||
config.headers[REQUEST_HEADER_TOKEN_KEY] = getAccessToken;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error: AxiosError) => {
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// 添加响应拦截器
|
||||
axiosInstance.interceptors.response.use(
|
||||
(response: AxiosResponse<HttpResponse>) => {
|
||||
const { data: responseData, status } = response;
|
||||
const { code, message: msg, result } = responseData;
|
||||
abortRequest(response.config);
|
||||
|
||||
if (status === 200 && code === 0) {
|
||||
return result;
|
||||
} else {
|
||||
message.error(msg);
|
||||
throw new Error(msg);
|
||||
}
|
||||
},
|
||||
(error: any) => {
|
||||
if (axios.isCancel(error)) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const err: string = error?.toString?.() ?? '';
|
||||
let errMsg = '';
|
||||
if (err?.includes('Network Error')) {
|
||||
errMsg = '网络错误。';
|
||||
} else if (error?.message?.includes?.('timeout')) {
|
||||
errMsg = '请求超时。';
|
||||
} else {
|
||||
errMsg = error?.response?.data?.error?.message ?? '';
|
||||
}
|
||||
message.error(errMsg);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
async function request<T>(url: string, config: AxiosRequestConfig): Promise<T> {
|
||||
try {
|
||||
const response: AxiosResponse<T> = await axiosInstance({
|
||||
url,
|
||||
...config,
|
||||
});
|
||||
return response as T;
|
||||
} catch (error: any) {
|
||||
throw error.response ? error.response.data : error;
|
||||
}
|
||||
}
|
||||
|
||||
export { abortAllRequest, request };
|
16
apps/antd-view/src/views/about/index.vue
Normal file
16
apps/antd-view/src/views/about/index.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'About' });
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('About');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
about
|
||||
<input class="bg-background border-border" />
|
||||
</div>
|
||||
</template>
|
24
apps/antd-view/src/views/authentication/code-login.vue
Normal file
24
apps/antd-view/src/views/authentication/code-login.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LoginCodeParams } from '@vben/common-ui';
|
||||
|
||||
import { AuthenticationCodeLogin } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
defineOptions({ name: 'CodeLogin' });
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
/**
|
||||
* 异步处理登录操作
|
||||
* Asynchronously handle the login process
|
||||
* @param values 登录表单数据
|
||||
*/
|
||||
async function handleLogin(values: LoginCodeParams) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(values);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticationCodeLogin :loading="loading" @submit="handleLogin" />
|
||||
</template>
|
17
apps/antd-view/src/views/authentication/forget-password.vue
Normal file
17
apps/antd-view/src/views/authentication/forget-password.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { AuthenticationForgetPassword } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
defineOptions({ name: 'ForgetPassword' });
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
function handleSubmit(value: string) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('reset email:', value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticationForgetPassword :loading="loading" @submit="handleSubmit" />
|
||||
</template>
|
69
apps/antd-view/src/views/authentication/login.vue
Normal file
69
apps/antd-view/src/views/authentication/login.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
||||
|
||||
import { getUserInfo, userLogin } from '@/services';
|
||||
import { AuthenticationLogin } from '@vben/common-ui';
|
||||
import { useRequest } from '@vben/hooks';
|
||||
import { $t } from '@vben/locales';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
import { notification } from 'ant-design-vue';
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
defineOptions({ name: 'Login' });
|
||||
|
||||
const router = useRouter();
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
const { loading, runAsync: runUserLogin } = useRequest(userLogin, {
|
||||
manual: true,
|
||||
});
|
||||
|
||||
const { loading: userInfoLoading, runAsync: runGetUserInfo } = useRequest(
|
||||
getUserInfo,
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* 异步处理登录操作
|
||||
* Asynchronously handle the login process
|
||||
* @param values 登录表单数据
|
||||
*/
|
||||
async function handleLogin(values: LoginAndRegisterParams) {
|
||||
// 异步处理用户登录操作并获取 accessToken
|
||||
// Asynchronously handle the user login operation and obtain the accessToken
|
||||
const { accessToken } = await runUserLogin(values);
|
||||
|
||||
// 如果成功获取到 accessToken
|
||||
// If accessToken is successfully obtained
|
||||
if (accessToken) {
|
||||
// 将 accessToken 存储到 accessStore 中
|
||||
// Store the accessToken in accessStore
|
||||
accessStore.setAccessToken(accessToken);
|
||||
|
||||
// 获取用户信息并存储到 accessStore 中
|
||||
// Get user information and store it in accessStore
|
||||
const userInfo = await runGetUserInfo();
|
||||
accessStore.setUserInfo(userInfo);
|
||||
|
||||
// 跳转到用户信息中定义的 homePath 路径
|
||||
// Redirect to the homePath defined in the user information
|
||||
await router.push(userInfo.homePath);
|
||||
notification.success({
|
||||
description: `${$t('authentication.login-success-desc')}:${userInfo.realName}`,
|
||||
duration: 3,
|
||||
message: $t('authentication.login-success'),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const loginLoading = computed(() => {
|
||||
return loading.value || userInfoLoading.value;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticationLogin :loading="loginLoading" @submit="handleLogin" />
|
||||
</template>
|
9
apps/antd-view/src/views/authentication/qrcode-login.vue
Normal file
9
apps/antd-view/src/views/authentication/qrcode-login.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
import { AuthenticationQrCodeLogin } from '@vben/common-ui';
|
||||
|
||||
defineOptions({ name: 'QrCodeLogin' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticationQrCodeLogin />
|
||||
</template>
|
19
apps/antd-view/src/views/authentication/register.vue
Normal file
19
apps/antd-view/src/views/authentication/register.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import type { LoginAndRegisterParams } from '@vben/common-ui';
|
||||
|
||||
import { AuthenticationRegister } from '@vben/common-ui';
|
||||
import { ref } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Register' });
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
function handleSubmit(value: LoginAndRegisterParams) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('register submit:', value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticationRegister :loading="loading" @submit="handleSubmit" />
|
||||
</template>
|
7
apps/antd-view/src/views/dashboard/index.vue
Normal file
7
apps/antd-view/src/views/dashboard/index.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'WelCome' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>dashboard</div>
|
||||
</template>
|
16
apps/antd-view/src/views/nested/menu-1.vue
Normal file
16
apps/antd-view/src/views/nested/menu-1.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Menu1' });
|
||||
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Menu1');
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-5">
|
||||
menu1
|
||||
<input class="bg-background border-border" />
|
||||
</div>
|
||||
</template>
|
16
apps/antd-view/src/views/nested/menu-2.vue
Normal file
16
apps/antd-view/src/views/nested/menu-2.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Menu2' });
|
||||
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Menu2');
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-5">
|
||||
menu2
|
||||
<input class="bg-background border-border" />
|
||||
</div>
|
||||
</template>
|
15
apps/antd-view/src/views/nested/menu-3-1.vue
Normal file
15
apps/antd-view/src/views/nested/menu-3-1.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Menu31' });
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Menu3-1');
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-5">
|
||||
Menu3-1
|
||||
<input class="bg-background border-border" />
|
||||
</div>
|
||||
</template>
|
15
apps/antd-view/src/views/nested/menu-3-2-1.vue
Normal file
15
apps/antd-view/src/views/nested/menu-3-2-1.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Menu321' });
|
||||
onMounted(() => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Menu3-2-1');
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="p-5">
|
||||
menu-3-2-1
|
||||
<input class="bg-background border-border" />
|
||||
</div>
|
||||
</template>
|
1
apps/antd-view/tailwind.config.mjs
Normal file
1
apps/antd-view/tailwind.config.mjs
Normal file
@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config';
|
12
apps/antd-view/tsconfig.json
Normal file
12
apps/antd-view/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web-app.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
9
apps/antd-view/tsconfig.node.json
Normal file
9
apps/antd-view/tsconfig.node.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/node.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"noEmit": false
|
||||
},
|
||||
"include": ["vite.config.mts"]
|
||||
}
|
35
apps/antd-view/vite.config.mts
Normal file
35
apps/antd-view/vite.config.mts
Normal file
@ -0,0 +1,35 @@
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig({
|
||||
appcation: {
|
||||
compress: false,
|
||||
compressTypes: ['brotli', 'gzip'],
|
||||
importmap: false,
|
||||
importmapOptions: {
|
||||
// 通过 Importmap CDN 方式引入,
|
||||
// 目前只有esm.sh源兼容性好一点,jspm.io对于 esm 入口要求高
|
||||
defaultProvider: 'esm.sh',
|
||||
importmap: [
|
||||
{ name: 'vue' },
|
||||
{ name: 'pinia' },
|
||||
{ name: 'vue-router' },
|
||||
{ name: 'vue-i18n' },
|
||||
{ name: 'dayjs' },
|
||||
{ name: 'vue-demi' },
|
||||
],
|
||||
},
|
||||
visualizer: false,
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
proxy: {
|
||||
'/vben-api': {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/vben-api/, ''),
|
||||
target: 'http://localhost:3000',
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
5
eslint.config.mjs
Normal file
5
eslint.config.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
import { defineConfig } from '@vben/eslint-config';
|
||||
|
||||
export default defineConfig();
|
7
internal/lint-configs/commitlint-config/build.config.ts
Normal file
7
internal/lint-configs/commitlint-config/build.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index'],
|
||||
});
|
42
internal/lint-configs/commitlint-config/package.json
Normal file
42
internal/lint-configs/commitlint-config/package.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@vben/commitlint-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/lint-configs/commitlint-config"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"imports": {
|
||||
"#*": "./src/*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@vben/node-utils": "workspace:*",
|
||||
"cz-git": "^1.9.1",
|
||||
"czg": "^1.9.1"
|
||||
}
|
||||
}
|
145
internal/lint-configs/commitlint-config/src/index.ts
Normal file
145
internal/lint-configs/commitlint-config/src/index.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import type { UserConfig } from 'cz-git';
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
import { getPackagesSync } from '@vben/node-utils';
|
||||
|
||||
const { packages } = getPackagesSync();
|
||||
|
||||
const pkgs = packages.map((pkg) => {
|
||||
return pkg.packageJson.name?.replace('@vben-core/', '');
|
||||
});
|
||||
|
||||
const scopes = [
|
||||
...pkgs,
|
||||
'project',
|
||||
'style',
|
||||
'lint',
|
||||
'ci',
|
||||
'dev',
|
||||
'deploy',
|
||||
'other',
|
||||
];
|
||||
|
||||
// precomputed scope
|
||||
const scopeComplete = execSync('git status --porcelain || true')
|
||||
.toString()
|
||||
.trim()
|
||||
.split('\n')
|
||||
.find((r) => ~r.indexOf('M src'))
|
||||
?.replace(/(\/)/g, '%%')
|
||||
?.match(/src%%((\w|-)*)/)?.[1]
|
||||
?.replace(/s$/, '');
|
||||
|
||||
const userConfig: UserConfig = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
prompt: {
|
||||
/** @use `pnpm commit :f` */
|
||||
alias: {
|
||||
b: 'build: bump dependencies',
|
||||
c: 'chore: update config',
|
||||
f: 'docs: fix typos',
|
||||
r: 'docs: update README',
|
||||
s: 'style: update code format',
|
||||
},
|
||||
allowCustomIssuePrefixs: false,
|
||||
// scopes: [...scopes, 'mock'],
|
||||
allowEmptyIssuePrefixs: false,
|
||||
customScopesAlign: scopeComplete ? 'bottom' : 'top',
|
||||
defaultScope: scopeComplete,
|
||||
// English
|
||||
typesAppend: [
|
||||
{ name: 'wip: work in process', value: 'wip' },
|
||||
{ name: 'workflow: workflow improvements', value: 'workflow' },
|
||||
{ name: 'types: type definition file changes', value: 'types' },
|
||||
],
|
||||
|
||||
// 中英文对照版
|
||||
// messages: {
|
||||
// type: '选择你要提交的类型 :',
|
||||
// scope: '选择一个提交范围 (可选):',
|
||||
// customScope: '请输入自定义的提交范围 :',
|
||||
// subject: '填写简短精炼的变更描述 :\n',
|
||||
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
|
||||
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
|
||||
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
|
||||
// customFooterPrefixs: '输入自定义issue前缀 :',
|
||||
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
|
||||
// confirmCommit: '是否提交或修改commit ?',
|
||||
// },
|
||||
// types: [
|
||||
// { value: 'feat', name: 'feat: 新增功能' },
|
||||
// { value: 'fix', name: 'fix: 修复缺陷' },
|
||||
// { value: 'docs', name: 'docs: 文档变更' },
|
||||
// { value: 'style', name: 'style: 代码格式' },
|
||||
// { value: 'refactor', name: 'refactor: 代码重构' },
|
||||
// { value: 'perf', name: 'perf: 性能优化' },
|
||||
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
|
||||
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
|
||||
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
|
||||
// { value: 'revert', name: 'revert: 回滚 commit' },
|
||||
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
|
||||
// { value: 'wip', name: 'wip: 正在开发中' },
|
||||
// { value: 'workflow', name: 'workflow: 工作流程改进' },
|
||||
// { value: 'types', name: 'types: 类型定义文件修改' },
|
||||
// ],
|
||||
// emptyScopesAlias: 'empty: 不填写',
|
||||
// customScopesAlias: 'custom: 自定义',
|
||||
},
|
||||
rules: {
|
||||
/**
|
||||
* type[scope]: [function] description
|
||||
*
|
||||
* ^^^^^^^^^^^^^^ empty line.
|
||||
* - Something here
|
||||
*/
|
||||
'body-leading-blank': [2, 'always'],
|
||||
/**
|
||||
* type[scope]: [function] description
|
||||
*
|
||||
* - something here
|
||||
*
|
||||
* ^^^^^^^^^^^^^^
|
||||
*/
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
/**
|
||||
* type[scope]: [function] description [No more than 108 characters]
|
||||
* ^^^^^
|
||||
*/
|
||||
'header-max-length': [2, 'always', 108],
|
||||
/**
|
||||
* type[scope]: [function] description
|
||||
* ^^^^^
|
||||
*/
|
||||
'scope-enum': [2, 'always', scopes],
|
||||
'subject-case': [0],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
/**
|
||||
* type[scope]: [function] description
|
||||
* ^^^^
|
||||
*/
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'style',
|
||||
'docs',
|
||||
'test',
|
||||
'refactor',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'types',
|
||||
'release',
|
||||
'improvement',
|
||||
],
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default userConfig;
|
5
internal/lint-configs/commitlint-config/tsconfig.json
Normal file
5
internal/lint-configs/commitlint-config/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/node.json",
|
||||
"include": ["src"]
|
||||
}
|
7
internal/lint-configs/eslint-config/build.config.ts
Normal file
7
internal/lint-configs/eslint-config/build.config.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index'],
|
||||
});
|
61
internal/lint-configs/eslint-config/package.json
Normal file
61
internal/lint-configs/eslint-config/package.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "@vben/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/lint-configs/eslint-config"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"imports": {
|
||||
"#*": "./src/*"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-plugin-command": "^0.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.3.0",
|
||||
"@types/eslint": "^8.56.10",
|
||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||
"@typescript-eslint/parser": "^7.9.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-i": "^2.29.1",
|
||||
"eslint-plugin-jsdoc": "^48.2.5",
|
||||
"eslint-plugin-jsonc": "^2.15.1",
|
||||
"eslint-plugin-n": "^17.7.0",
|
||||
"eslint-plugin-no-only-tests": "^3.1.0",
|
||||
"eslint-plugin-perfectionist": "^2.10.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-regexp": "^2.5.0",
|
||||
"eslint-plugin-unicorn": "^53.0.0",
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"eslint-plugin-vitest": "^0.5.4",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"globals": "^15.2.0",
|
||||
"jsonc-eslint-parser": "^2.4.0",
|
||||
"vue-eslint-parser": "^9.4.2"
|
||||
}
|
||||
}
|
10
internal/lint-configs/eslint-config/src/configs/command.ts
Normal file
10
internal/lint-configs/eslint-config/src/configs/command.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import createCommand from 'eslint-plugin-command/config';
|
||||
|
||||
export async function command() {
|
||||
return [
|
||||
{
|
||||
// @ts-expect-error - no types
|
||||
...createCommand(),
|
||||
},
|
||||
];
|
||||
}
|
22
internal/lint-configs/eslint-config/src/configs/comments.ts
Normal file
22
internal/lint-configs/eslint-config/src/configs/comments.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function comments(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginComments] = await Promise.all([
|
||||
// @ts-expect-error - no types
|
||||
import('eslint-plugin-eslint-comments'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
'eslint-comments': pluginComments,
|
||||
},
|
||||
rules: {
|
||||
'eslint-comments/no-aggregating-enable': 'error',
|
||||
'eslint-comments/no-duplicate-disable': 'error',
|
||||
'eslint-comments/no-unlimited-disable': 'error',
|
||||
'eslint-comments/no-unused-enable': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
46
internal/lint-configs/eslint-config/src/configs/ignores.ts
Normal file
46
internal/lint-configs/eslint-config/src/configs/ignores.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function ignores(): Promise<Linter.FlatConfig[]> {
|
||||
return [
|
||||
{
|
||||
ignores: [
|
||||
'**/node_modules',
|
||||
'**/dist',
|
||||
'**/.husky',
|
||||
'**/Dockerfile',
|
||||
'**/package-lock.json',
|
||||
'**/yarn.lock',
|
||||
'**/pnpm-lock.yaml',
|
||||
'**/bun.lockb',
|
||||
|
||||
'**/output',
|
||||
'**/coverage',
|
||||
'**/temp',
|
||||
'**/.temp',
|
||||
'**/tmp',
|
||||
'**/.tmp',
|
||||
'**/.history',
|
||||
'**/.vitepress/cache',
|
||||
'**/.nuxt',
|
||||
'**/.next',
|
||||
'**/.vercel',
|
||||
'**/.changeset',
|
||||
'**/.idea',
|
||||
'**/.cache',
|
||||
'**/.output',
|
||||
'**/.vite-inspect',
|
||||
|
||||
'**/CHANGELOG*.md',
|
||||
'**/*.min.*',
|
||||
'**/LICENSE*',
|
||||
'**/__snapshots__',
|
||||
'**/auto-import?(s).d.ts',
|
||||
'**/components.d.ts',
|
||||
'**/vite.config.mts.*',
|
||||
'**/*.sh',
|
||||
'**/*.ttf',
|
||||
'**/*.woff',
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
27
internal/lint-configs/eslint-config/src/configs/import.ts
Normal file
27
internal/lint-configs/eslint-config/src/configs/import.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function importPluginConfig(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginImport] = await Promise.all([
|
||||
// @ts-expect-error - no types
|
||||
import('eslint-plugin-i'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
import: pluginImport,
|
||||
},
|
||||
rules: {
|
||||
'import/first': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'import/no-mutable-exports': 'error',
|
||||
'import/no-named-default': 'error',
|
||||
'import/no-self-import': 'error',
|
||||
'import/no-unresolved': 'off',
|
||||
|
||||
'import/no-webpack-loader-syntax': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
15
internal/lint-configs/eslint-config/src/configs/index.ts
Normal file
15
internal/lint-configs/eslint-config/src/configs/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export * from './command';
|
||||
export * from './comments';
|
||||
export * from './ignores';
|
||||
export * from './import';
|
||||
export * from './javascript';
|
||||
export * from './jsdoc';
|
||||
export * from './jsonc';
|
||||
export * from './node';
|
||||
export * from './perfectionist';
|
||||
export * from './prettier';
|
||||
export * from './regexp';
|
||||
export * from './test';
|
||||
export * from './typescript';
|
||||
export * from './unicorn';
|
||||
export * from './vue';
|
243
internal/lint-configs/eslint-config/src/configs/javascript.ts
Normal file
243
internal/lint-configs/eslint-config/src/configs/javascript.ts
Normal file
@ -0,0 +1,243 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
// @ts-expect-error - no types
|
||||
import js from '@eslint/js';
|
||||
// @ts-expect-error - no types
|
||||
import pluginUnusedImports from 'eslint-plugin-unused-imports';
|
||||
import globals from 'globals';
|
||||
|
||||
export async function javascript(): Promise<Linter.FlatConfig[]> {
|
||||
return [
|
||||
{
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.es2021,
|
||||
...globals.node,
|
||||
document: 'readonly',
|
||||
navigator: 'readonly',
|
||||
window: 'readonly',
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
sourceType: 'module',
|
||||
},
|
||||
linterOptions: {
|
||||
reportUnusedDisableDirectives: true,
|
||||
},
|
||||
plugins: {
|
||||
'unused-imports': pluginUnusedImports,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
'accessor-pairs': [
|
||||
'error',
|
||||
{ enforceForClassMembers: true, setWithoutGet: true },
|
||||
],
|
||||
'array-callback-return': 'error',
|
||||
'block-scoped-var': 'error',
|
||||
'constructor-super': 'error',
|
||||
'default-case-last': 'error',
|
||||
'dot-notation': ['error', { allowKeywords: true }],
|
||||
eqeqeq: ['error', 'always'],
|
||||
'keyword-spacing': 'off',
|
||||
|
||||
'new-cap': [
|
||||
'error',
|
||||
{ capIsNew: false, newIsCap: true, properties: true },
|
||||
],
|
||||
'no-alert': 'error',
|
||||
'no-array-constructor': 'error',
|
||||
'no-async-promise-executor': 'error',
|
||||
'no-caller': 'error',
|
||||
'no-case-declarations': 'error',
|
||||
'no-class-assign': 'error',
|
||||
'no-compare-neg-zero': 'error',
|
||||
'no-cond-assign': ['error', 'always'],
|
||||
'no-console': ['error', { allow: ['warn', 'error'] }],
|
||||
'no-const-assign': 'error',
|
||||
'no-control-regex': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-delete-var': 'error',
|
||||
'no-dupe-args': 'error',
|
||||
'no-dupe-class-members': 'error',
|
||||
'no-dupe-keys': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||
'no-empty-character-class': 'error',
|
||||
'no-empty-function': 'off',
|
||||
'no-empty-pattern': 'error',
|
||||
'no-eval': 'error',
|
||||
'no-ex-assign': 'error',
|
||||
'no-extend-native': 'error',
|
||||
'no-extra-bind': 'error',
|
||||
'no-extra-boolean-cast': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-func-assign': 'error',
|
||||
'no-global-assign': 'error',
|
||||
'no-implied-eval': 'error',
|
||||
'no-import-assign': 'error',
|
||||
'no-invalid-regexp': 'error',
|
||||
'no-irregular-whitespace': 'error',
|
||||
'no-iterator': 'error',
|
||||
'no-labels': ['error', { allowLoop: false, allowSwitch: false }],
|
||||
'no-lone-blocks': 'error',
|
||||
'no-loss-of-precision': 'error',
|
||||
'no-misleading-character-class': 'error',
|
||||
'no-multi-str': 'error',
|
||||
'no-new': 'error',
|
||||
'no-new-func': 'error',
|
||||
'no-new-object': 'error',
|
||||
'no-new-symbol': 'error',
|
||||
'no-new-wrappers': 'error',
|
||||
'no-obj-calls': 'error',
|
||||
'no-octal': 'error',
|
||||
'no-octal-escape': 'error',
|
||||
'no-proto': 'error',
|
||||
'no-prototype-builtins': 'error',
|
||||
'no-redeclare': ['error', { builtinGlobals: false }],
|
||||
'no-regex-spaces': 'error',
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{ message: 'Use `globalThis` instead.', name: 'global' },
|
||||
{ message: 'Use `globalThis` instead.', name: 'self' },
|
||||
],
|
||||
'no-restricted-properties': [
|
||||
'error',
|
||||
{
|
||||
message:
|
||||
'Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.',
|
||||
property: '__proto__',
|
||||
},
|
||||
{
|
||||
message: 'Use `Object.defineProperty` instead.',
|
||||
property: '__defineGetter__',
|
||||
},
|
||||
{
|
||||
message: 'Use `Object.defineProperty` instead.',
|
||||
property: '__defineSetter__',
|
||||
},
|
||||
{
|
||||
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
|
||||
property: '__lookupGetter__',
|
||||
},
|
||||
{
|
||||
message: 'Use `Object.getOwnPropertyDescriptor` instead.',
|
||||
property: '__lookupSetter__',
|
||||
},
|
||||
],
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
'DebuggerStatement',
|
||||
'LabeledStatement',
|
||||
'WithStatement',
|
||||
'TSEnumDeclaration[const=true]',
|
||||
'TSExportAssignment',
|
||||
],
|
||||
'no-self-assign': ['error', { props: true }],
|
||||
'no-self-compare': 'error',
|
||||
'no-sequences': 'error',
|
||||
'no-shadow-restricted-names': 'error',
|
||||
'no-sparse-arrays': 'error',
|
||||
'no-template-curly-in-string': 'error',
|
||||
'no-this-before-super': 'error',
|
||||
'no-throw-literal': 'error',
|
||||
'no-undef': 'error',
|
||||
'no-undef-init': 'error',
|
||||
'no-unexpected-multiline': 'error',
|
||||
'no-unmodified-loop-condition': 'error',
|
||||
'no-unneeded-ternary': ['error', { defaultAssignment: false }],
|
||||
'no-unreachable': 'error',
|
||||
'no-unreachable-loop': 'error',
|
||||
'no-unsafe-finally': 'error',
|
||||
'no-unsafe-negation': 'error',
|
||||
'no-unused-expressions': [
|
||||
'error',
|
||||
{
|
||||
allowShortCircuit: true,
|
||||
allowTaggedTemplates: true,
|
||||
allowTernary: true,
|
||||
},
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'none',
|
||||
caughtErrors: 'none',
|
||||
ignoreRestSiblings: true,
|
||||
vars: 'all',
|
||||
},
|
||||
],
|
||||
'no-use-before-define': [
|
||||
'error',
|
||||
{ classes: false, functions: false, variables: true },
|
||||
],
|
||||
'no-useless-backreference': 'error',
|
||||
'no-useless-call': 'error',
|
||||
'no-useless-catch': 'error',
|
||||
'no-useless-computed-key': 'error',
|
||||
'no-useless-constructor': 'error',
|
||||
'no-useless-rename': 'error',
|
||||
'no-useless-return': 'error',
|
||||
'no-var': 'error',
|
||||
'no-with': 'error',
|
||||
'object-shorthand': [
|
||||
'error',
|
||||
'always',
|
||||
{ avoidQuotes: true, ignoreConstructors: false },
|
||||
],
|
||||
'one-var': ['error', { initialized: 'never' }],
|
||||
'prefer-arrow-callback': [
|
||||
'error',
|
||||
{
|
||||
allowNamedFunctions: false,
|
||||
allowUnboundThis: true,
|
||||
},
|
||||
],
|
||||
'prefer-const': [
|
||||
'error',
|
||||
{
|
||||
destructuring: 'all',
|
||||
ignoreReadBeforeAssign: true,
|
||||
},
|
||||
],
|
||||
'prefer-exponentiation-operator': 'error',
|
||||
|
||||
'prefer-promise-reject-errors': 'error',
|
||||
'prefer-regex-literals': ['error', { disallowRedundantWrapping: true }],
|
||||
'prefer-rest-params': 'error',
|
||||
'prefer-spread': 'error',
|
||||
'prefer-template': 'error',
|
||||
'space-before-function-paren': 'off',
|
||||
'spaced-comment': 'error',
|
||||
'symbol-description': 'error',
|
||||
'unicode-bom': ['error', 'never'],
|
||||
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'use-isnan': [
|
||||
'error',
|
||||
{ enforceForIndexOf: true, enforceForSwitchCase: true },
|
||||
],
|
||||
'valid-typeof': ['error', { requireStringLiterals: true }],
|
||||
|
||||
'vars-on-top': 'error',
|
||||
yoda: ['error', 'never'],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
33
internal/lint-configs/eslint-config/src/configs/jsdoc.ts
Normal file
33
internal/lint-configs/eslint-config/src/configs/jsdoc.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function jsdoc(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginJsdoc] = await Promise.all([
|
||||
import('eslint-plugin-jsdoc'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
// @ts-expect-error - no types
|
||||
jsdoc: pluginJsdoc,
|
||||
},
|
||||
rules: {
|
||||
'jsdoc/check-access': 'warn',
|
||||
'jsdoc/check-param-names': 'warn',
|
||||
'jsdoc/check-property-names': 'warn',
|
||||
'jsdoc/check-types': 'warn',
|
||||
'jsdoc/empty-tags': 'warn',
|
||||
'jsdoc/implements-on-classes': 'warn',
|
||||
'jsdoc/no-defaults': 'warn',
|
||||
'jsdoc/no-multi-asterisks': 'warn',
|
||||
'jsdoc/require-param-name': 'warn',
|
||||
'jsdoc/require-property': 'warn',
|
||||
'jsdoc/require-property-description': 'warn',
|
||||
'jsdoc/require-property-name': 'warn',
|
||||
'jsdoc/require-returns-check': 'warn',
|
||||
'jsdoc/require-returns-description': 'warn',
|
||||
'jsdoc/require-yields-check': 'warn',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
257
internal/lint-configs/eslint-config/src/configs/jsonc.ts
Normal file
257
internal/lint-configs/eslint-config/src/configs/jsonc.ts
Normal file
@ -0,0 +1,257 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function jsonc(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginJsonc, parserJsonc] = await Promise.all([
|
||||
import('eslint-plugin-jsonc'),
|
||||
import('jsonc-eslint-parser'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
files: ['**/*.json', '**/*.json5', '**/*.jsonc', '*.code-workspace'],
|
||||
languageOptions: {
|
||||
parser: parserJsonc as any,
|
||||
},
|
||||
plugins: {
|
||||
jsonc: pluginJsonc as any,
|
||||
},
|
||||
rules: {
|
||||
'jsonc/no-bigint-literals': 'error',
|
||||
'jsonc/no-binary-expression': 'error',
|
||||
'jsonc/no-binary-numeric-literals': 'error',
|
||||
'jsonc/no-dupe-keys': 'error',
|
||||
'jsonc/no-escape-sequence-in-identifier': 'error',
|
||||
'jsonc/no-floating-decimal': 'error',
|
||||
'jsonc/no-hexadecimal-numeric-literals': 'error',
|
||||
'jsonc/no-infinity': 'error',
|
||||
'jsonc/no-multi-str': 'error',
|
||||
'jsonc/no-nan': 'error',
|
||||
'jsonc/no-number-props': 'error',
|
||||
'jsonc/no-numeric-separators': 'error',
|
||||
'jsonc/no-octal': 'error',
|
||||
'jsonc/no-octal-escape': 'error',
|
||||
'jsonc/no-octal-numeric-literals': 'error',
|
||||
'jsonc/no-parenthesized': 'error',
|
||||
'jsonc/no-plus-sign': 'error',
|
||||
'jsonc/no-regexp-literals': 'error',
|
||||
'jsonc/no-sparse-arrays': 'error',
|
||||
'jsonc/no-template-literals': 'error',
|
||||
'jsonc/no-undefined-value': 'error',
|
||||
'jsonc/no-unicode-codepoint-escapes': 'error',
|
||||
'jsonc/no-useless-escape': 'error',
|
||||
'jsonc/space-unary-ops': 'error',
|
||||
'jsonc/valid-json-number': 'error',
|
||||
'jsonc/vue-custom-block/no-parsing-error': 'error',
|
||||
},
|
||||
},
|
||||
sortTsconfig(),
|
||||
sortPackageJson(),
|
||||
];
|
||||
}
|
||||
|
||||
function sortPackageJson(): Linter.FlatConfig {
|
||||
return {
|
||||
files: ['**/package.json'],
|
||||
rules: {
|
||||
'jsonc/sort-array-values': [
|
||||
'error',
|
||||
{
|
||||
order: { type: 'asc' },
|
||||
pathPattern: '^files$|^pnpm.neverBuiltDependencies$',
|
||||
},
|
||||
],
|
||||
'jsonc/sort-keys': [
|
||||
'error',
|
||||
{
|
||||
order: [
|
||||
'publisher',
|
||||
'name',
|
||||
'version',
|
||||
'private',
|
||||
'description',
|
||||
'displayName',
|
||||
'type',
|
||||
'author',
|
||||
'license',
|
||||
'funding',
|
||||
'homepage',
|
||||
'repository',
|
||||
'bugs',
|
||||
'keywords',
|
||||
'categories',
|
||||
'scripts',
|
||||
'files',
|
||||
'sideEffects',
|
||||
'bin',
|
||||
'main',
|
||||
'module',
|
||||
'unpkg',
|
||||
'jsdelivr',
|
||||
'types',
|
||||
'typesVersions',
|
||||
'imports',
|
||||
'exports',
|
||||
'publishConfig',
|
||||
'icon',
|
||||
'activationEvents',
|
||||
'contributes',
|
||||
'peerDependencies',
|
||||
'peerDependenciesMeta',
|
||||
'dependencies',
|
||||
'optionalDependencies',
|
||||
'devDependencies',
|
||||
'engines',
|
||||
'packageManager',
|
||||
'pnpm',
|
||||
'overrides',
|
||||
'resolutions',
|
||||
'husky',
|
||||
'simple-git-hooks',
|
||||
'lint-staged',
|
||||
'eslintConfig',
|
||||
],
|
||||
pathPattern: '^$',
|
||||
},
|
||||
{
|
||||
order: { type: 'asc' },
|
||||
pathPattern: '^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$',
|
||||
},
|
||||
{
|
||||
order: { type: 'asc' },
|
||||
pathPattern: '^(?:resolutions|overrides|pnpm.overrides)$',
|
||||
},
|
||||
{
|
||||
order: ['types', 'import', 'require', 'default'],
|
||||
pathPattern: '^exports.*$',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function sortTsconfig(): Linter.FlatConfig {
|
||||
return {
|
||||
files: [
|
||||
'**/tsconfig.json',
|
||||
'**/tsconfig.*.json',
|
||||
'internal/tsconfig/*.json',
|
||||
],
|
||||
rules: {
|
||||
'jsonc/sort-keys': [
|
||||
'error',
|
||||
{
|
||||
order: [
|
||||
'extends',
|
||||
'compilerOptions',
|
||||
'references',
|
||||
'files',
|
||||
'include',
|
||||
'exclude',
|
||||
],
|
||||
pathPattern: '^$',
|
||||
},
|
||||
{
|
||||
order: [
|
||||
/* Projects */
|
||||
'incremental',
|
||||
'composite',
|
||||
'tsBuildInfoFile',
|
||||
'disableSourceOfProjectReferenceRedirect',
|
||||
'disableSolutionSearching',
|
||||
'disableReferencedProjectLoad',
|
||||
/* Language and Environment */
|
||||
'target',
|
||||
'jsx',
|
||||
'jsxFactory',
|
||||
'jsxFragmentFactory',
|
||||
'jsxImportSource',
|
||||
'lib',
|
||||
'moduleDetection',
|
||||
'noLib',
|
||||
'reactNamespace',
|
||||
'useDefineForClassFields',
|
||||
'emitDecoratorMetadata',
|
||||
'experimentalDecorators',
|
||||
/* Modules */
|
||||
'baseUrl',
|
||||
'rootDir',
|
||||
'rootDirs',
|
||||
'customConditions',
|
||||
'module',
|
||||
'moduleResolution',
|
||||
'moduleSuffixes',
|
||||
'noResolve',
|
||||
'paths',
|
||||
'resolveJsonModule',
|
||||
'resolvePackageJsonExports',
|
||||
'resolvePackageJsonImports',
|
||||
'typeRoots',
|
||||
'types',
|
||||
'allowArbitraryExtensions',
|
||||
'allowImportingTsExtensions',
|
||||
'allowUmdGlobalAccess',
|
||||
/* JavaScript Support */
|
||||
'allowJs',
|
||||
'checkJs',
|
||||
'maxNodeModuleJsDepth',
|
||||
/* Type Checking */
|
||||
'strict',
|
||||
'strictBindCallApply',
|
||||
'strictFunctionTypes',
|
||||
'strictNullChecks',
|
||||
'strictPropertyInitialization',
|
||||
'allowUnreachableCode',
|
||||
'allowUnusedLabels',
|
||||
'alwaysStrict',
|
||||
'exactOptionalPropertyTypes',
|
||||
'noFallthroughCasesInSwitch',
|
||||
'noImplicitAny',
|
||||
'noImplicitOverride',
|
||||
'noImplicitReturns',
|
||||
'noImplicitThis',
|
||||
'noPropertyAccessFromIndexSignature',
|
||||
'noUncheckedIndexedAccess',
|
||||
'noUnusedLocals',
|
||||
'noUnusedParameters',
|
||||
'useUnknownInCatchVariables',
|
||||
/* Emit */
|
||||
'declaration',
|
||||
'declarationDir',
|
||||
'declarationMap',
|
||||
'downlevelIteration',
|
||||
'emitBOM',
|
||||
'emitDeclarationOnly',
|
||||
'importHelpers',
|
||||
'importsNotUsedAsValues',
|
||||
'inlineSourceMap',
|
||||
'inlineSources',
|
||||
'mapRoot',
|
||||
'newLine',
|
||||
'noEmit',
|
||||
'noEmitHelpers',
|
||||
'noEmitOnError',
|
||||
'outDir',
|
||||
'outFile',
|
||||
'preserveConstEnums',
|
||||
'preserveValueImports',
|
||||
'removeComments',
|
||||
'sourceMap',
|
||||
'sourceRoot',
|
||||
'stripInternal',
|
||||
/* Interop Constraints */
|
||||
'allowSyntheticDefaultImports',
|
||||
'esModuleInterop',
|
||||
'forceConsistentCasingInFileNames',
|
||||
'isolatedModules',
|
||||
'preserveSymlinks',
|
||||
'verbatimModuleSyntax',
|
||||
/* Completeness */
|
||||
'skipDefaultLibCheck',
|
||||
'skipLibCheck',
|
||||
],
|
||||
pathPattern: '^compilerOptions$',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
54
internal/lint-configs/eslint-config/src/configs/node.ts
Normal file
54
internal/lint-configs/eslint-config/src/configs/node.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function node(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginNode] = await Promise.all([import('eslint-plugin-n')] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
n: pluginNode,
|
||||
},
|
||||
rules: {
|
||||
'n/handle-callback-err': ['error', '^(err|error)$'],
|
||||
'n/no-deprecated-api': 'error',
|
||||
'n/no-exports-assign': 'error',
|
||||
'n/no-extraneous-import': [
|
||||
'error',
|
||||
{
|
||||
allowModules: [
|
||||
'unbuild',
|
||||
'@vben/vite-config',
|
||||
'vitest',
|
||||
'vite',
|
||||
'@vue/test-utils',
|
||||
'@vben/tailwind-config',
|
||||
],
|
||||
},
|
||||
],
|
||||
'n/no-new-require': 'error',
|
||||
'n/no-path-concat': 'error',
|
||||
// 'n/no-unpublished-import': 'off',
|
||||
'n/no-unsupported-features/es-syntax': [
|
||||
'error',
|
||||
{
|
||||
ignores: [],
|
||||
version: '>=18.0.0',
|
||||
},
|
||||
],
|
||||
'n/prefer-global/buffer': ['error', 'never'],
|
||||
// 'n/no-missing-import': 'off',
|
||||
'n/prefer-global/process': ['error', 'never'],
|
||||
'n/process-exit-as-throw': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'scripts/**/*.?([cm])[jt]s?(x)',
|
||||
'internal/**/*.?([cm])[jt]s?(x)',
|
||||
],
|
||||
rules: {
|
||||
'n/prefer-global/process': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function perfectionist(): Promise<Linter.FlatConfig[]> {
|
||||
const [perfectionistNatural] = await Promise.all([
|
||||
// @ts-expect-error - no types
|
||||
import('eslint-plugin-perfectionist/configs/recommended-natural'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
perfectionistNatural,
|
||||
{
|
||||
rules: {
|
||||
'perfectionist/sort-exports': [
|
||||
'error',
|
||||
{
|
||||
order: 'asc',
|
||||
type: 'natural',
|
||||
},
|
||||
],
|
||||
'perfectionist/sort-imports': [
|
||||
'error',
|
||||
{
|
||||
'custom-groups': {
|
||||
type: {
|
||||
vben: 'vue',
|
||||
vue: ['vue', 'vue-*', '@vue*'],
|
||||
},
|
||||
value: {
|
||||
vben: 'vben',
|
||||
vue: ['@vben-*', '@vben-core/*'],
|
||||
},
|
||||
},
|
||||
groups: [
|
||||
'side-effect',
|
||||
'type',
|
||||
'vue',
|
||||
'builtin',
|
||||
'vben',
|
||||
'external',
|
||||
'internal-type',
|
||||
'internal',
|
||||
['parent', 'sibling', 'index'],
|
||||
'style',
|
||||
'object',
|
||||
'unknown',
|
||||
'type',
|
||||
['parent-type', 'sibling-type', 'index-type'],
|
||||
],
|
||||
'internal-pattern': ['@/layouts/**', '@/router/**', '@/views/**'],
|
||||
'newlines-between': 'always',
|
||||
order: 'asc',
|
||||
type: 'natural',
|
||||
},
|
||||
],
|
||||
'perfectionist/sort-named-exports': [
|
||||
'error',
|
||||
{
|
||||
order: 'asc',
|
||||
type: 'natural',
|
||||
},
|
||||
],
|
||||
'perfectionist/sort-objects': [
|
||||
'error',
|
||||
{
|
||||
'custom-groups': {
|
||||
items: 'items',
|
||||
list: 'list',
|
||||
children: 'children',
|
||||
},
|
||||
groups: ['unknown', 'items', 'list', 'children'],
|
||||
'ignore-pattern': ['children'],
|
||||
order: 'asc',
|
||||
'partition-by-comment': 'Part:**',
|
||||
type: 'natural',
|
||||
},
|
||||
],
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
17
internal/lint-configs/eslint-config/src/configs/prettier.ts
Normal file
17
internal/lint-configs/eslint-config/src/configs/prettier.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function prettier(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginPrettier] = await Promise.all([
|
||||
import('eslint-plugin-prettier'),
|
||||
] as const);
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
prettier: pluginPrettier,
|
||||
},
|
||||
rules: {
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
18
internal/lint-configs/eslint-config/src/configs/regexp.ts
Normal file
18
internal/lint-configs/eslint-config/src/configs/regexp.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function regexp(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginRegexp] = await Promise.all([
|
||||
import('eslint-plugin-regexp'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
regexp: pluginRegexp,
|
||||
},
|
||||
rules: {
|
||||
...pluginRegexp.configs.recommended.rules,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
44
internal/lint-configs/eslint-config/src/configs/test.ts
Normal file
44
internal/lint-configs/eslint-config/src/configs/test.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function test(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginTest, pluginNoOnlyTests] = await Promise.all([
|
||||
import('eslint-plugin-vitest'),
|
||||
// @ts-expect-error - no types
|
||||
import('eslint-plugin-no-only-tests'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
files: [
|
||||
`**/__tests__/**/*.?([cm])[jt]s?(x)`,
|
||||
`**/*.spec.?([cm])[jt]s?(x)`,
|
||||
`**/*.test.?([cm])[jt]s?(x)`,
|
||||
`**/*.bench.?([cm])[jt]s?(x)`,
|
||||
`**/*.benchmark.?([cm])[jt]s?(x)`,
|
||||
],
|
||||
plugins: {
|
||||
test: {
|
||||
...pluginTest,
|
||||
rules: {
|
||||
// @ts-expect-error - no types
|
||||
...pluginTest.rules,
|
||||
...pluginNoOnlyTests.rules,
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'node/prefer-global/process': 'off',
|
||||
'test/consistent-test-it': [
|
||||
'error',
|
||||
{ fn: 'it', withinDescribe: 'it' },
|
||||
],
|
||||
'test/no-identical-title': 'error',
|
||||
'test/no-import-node-test': 'error',
|
||||
'test/no-only-tests': 'error',
|
||||
'test/prefer-hooks-in-order': 'error',
|
||||
'test/prefer-lowercase-title': 'error',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function typescript(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginTs, parserTs] = await Promise.all([
|
||||
import('@typescript-eslint/eslint-plugin'),
|
||||
// @ts-expect-error missing types
|
||||
import('@typescript-eslint/parser'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
files: ['**/*.?([cm])[jt]s?(x)'],
|
||||
languageOptions: {
|
||||
parser: parserTs,
|
||||
parserOptions: {
|
||||
createDefaultProgram: false,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 'latest',
|
||||
extraFileExtensions: ['.vue'],
|
||||
jsxPragma: 'React',
|
||||
project: './tsconfig.*?.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
'@typescript-eslint': pluginTs,
|
||||
},
|
||||
rules: {
|
||||
...pluginTs.configs['eslint-recommended'].overrides?.[0].rules,
|
||||
...pluginTs.configs.strict.rules,
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-check': false,
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-ignore': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description',
|
||||
},
|
||||
],
|
||||
|
||||
'@typescript-eslint/ban-types': 'error',
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/keyword-spacing': [
|
||||
'error',
|
||||
{
|
||||
after: true,
|
||||
before: true,
|
||||
overrides: {
|
||||
case: { after: true },
|
||||
return: { after: true },
|
||||
throw: { after: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-empty-function': [
|
||||
'error',
|
||||
{
|
||||
allow: ['arrowFunctions', 'functions', 'methods'],
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'error',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'error',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
40
internal/lint-configs/eslint-config/src/configs/unicorn.ts
Normal file
40
internal/lint-configs/eslint-config/src/configs/unicorn.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import type { Linter } from 'eslint';
|
||||
|
||||
export async function unicorn(): Promise<Linter.FlatConfig[]> {
|
||||
const [pluginUnicorn] = await Promise.all([
|
||||
// @ts-expect-error - missing types
|
||||
import('eslint-plugin-unicorn'),
|
||||
] as const);
|
||||
|
||||
return [
|
||||
{
|
||||
plugins: {
|
||||
unicorn: pluginUnicorn,
|
||||
},
|
||||
rules: {
|
||||
...pluginUnicorn.configs.recommended.rules,
|
||||
|
||||
'unicorn/consistent-destructuring': 'off',
|
||||
'unicorn/consistent-function-scoping': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'unicorn/import-style': 'off',
|
||||
'unicorn/no-array-for-each': 'off',
|
||||
'unicorn/no-null': 'off',
|
||||
'unicorn/prefer-at': 'off',
|
||||
'unicorn/prefer-dom-node-text-content': 'off',
|
||||
'unicorn/prefer-export-from': ['error', { ignoreUsedVariables: true }],
|
||||
'unicorn/prefer-top-level-await': 'off',
|
||||
'unicorn/prevent-abbreviations': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'scripts/**/*.?([cm])[jt]s?(x)',
|
||||
'internal/**/*.?([cm])[jt]s?(x)',
|
||||
],
|
||||
rules: {
|
||||
'unicorn/no-process-exit': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user