Browse Source

chore: init project

pull/3993/head
vben 4 months ago
commit
399334ac57
  1. 4
      .browserslistrc
  2. 5
      .changeset/README.md
  3. 15
      .changeset/config.json
  4. 1
      .commitlintrc.mjs
  5. 21
      .editorconfig
  6. 11
      .gitattributes
  7. 2
      .gitconfig
  8. 39
      .github/ISSUE_TEMPLATE/1-bug.md
  9. 32
      .github/ISSUE_TEMPLATE/2-feature.md
  10. 28
      .github/ISSUE_TEMPLATE/3-bug-cn.md
  11. 8
      .github/ISSUE_TEMPLATE/config.yml
  12. 89
      .github/commit-convention.md
  13. 5
      .github/contributing.md
  14. 34
      .github/pull_request_template.md
  15. 118
      .github/workflows/deploy.yml
  16. 17
      .github/workflows/issue-close-require.yml
  17. 29
      .github/workflows/issue-labeled.yml
  18. 24
      .github/workflows/release.yml
  19. 38
      .gitignore
  20. 6
      .gitpod.yml
  21. 6
      .husky/commit-msg
  22. 7
      .husky/pre-commit
  23. 1
      .lintstagedrc.mjs
  24. 27
      .ls-lint.yml
  25. 1
      .node-version
  26. 12
      .npmrc
  27. 13
      .prettierignore
  28. 1
      .prettierrc.mjs
  29. 3
      .stylelintignore
  30. 3
      .tazerc.json
  31. 34
      .vscode/extensions.json
  32. 37
      .vscode/global.code-snippets
  33. 186
      .vscode/settings.json
  34. 9
      LICENSE
  35. 165
      README.md
  36. 171
      README.zh-CN.md
  37. 2
      apps/antd-view/.env
  38. 6
      apps/antd-view/.env.analyze
  39. 3
      apps/antd-view/.env.development
  40. 5
      apps/antd-view/.env.production
  41. 22
      apps/antd-view/index.html
  42. 33
      apps/antd-view/mock/_util.ts
  43. 101
      apps/antd-view/mock/user.ts
  44. 47
      apps/antd-view/package.json
  45. 1
      apps/antd-view/postcss.config.mjs
  46. BIN
      apps/antd-view/public/favicon.ico
  47. 40
      apps/antd-view/src/app.vue
  48. 127
      apps/antd-view/src/layout.vue
  49. 43
      apps/antd-view/src/main.ts
  50. 10
      apps/antd-view/src/mock-prod-server.ts
  51. 7
      apps/antd-view/src/preference.ts
  52. 183
      apps/antd-view/src/router/guard/access.ts
  53. 43
      apps/antd-view/src/router/guard/index.ts
  54. 59
      apps/antd-view/src/router/index.ts
  55. 78
      apps/antd-view/src/router/routes/builtin.ts
  56. 66
      apps/antd-view/src/router/routes/index.ts
  57. 8
      apps/antd-view/src/router/routes/layout.ts
  58. 71
      apps/antd-view/src/router/routes/modules/nested.ts
  59. 37
      apps/antd-view/src/router/routes/modules/outside.ts
  60. 1
      apps/antd-view/src/services/index.ts
  61. 23
      apps/antd-view/src/services/modules/user/index.ts
  62. 18
      apps/antd-view/src/services/modules/user/typing.ts
  63. 152
      apps/antd-view/src/services/request.ts
  64. 16
      apps/antd-view/src/views/about/index.vue
  65. 24
      apps/antd-view/src/views/authentication/code-login.vue
  66. 17
      apps/antd-view/src/views/authentication/forget-password.vue
  67. 69
      apps/antd-view/src/views/authentication/login.vue
  68. 9
      apps/antd-view/src/views/authentication/qrcode-login.vue
  69. 19
      apps/antd-view/src/views/authentication/register.vue
  70. 7
      apps/antd-view/src/views/dashboard/index.vue
  71. 16
      apps/antd-view/src/views/nested/menu-1.vue
  72. 16
      apps/antd-view/src/views/nested/menu-2.vue
  73. 15
      apps/antd-view/src/views/nested/menu-3-1.vue
  74. 15
      apps/antd-view/src/views/nested/menu-3-2-1.vue
  75. 1
      apps/antd-view/tailwind.config.mjs
  76. 12
      apps/antd-view/tsconfig.json
  77. 9
      apps/antd-view/tsconfig.node.json
  78. 35
      apps/antd-view/vite.config.mts
  79. 5
      eslint.config.mjs
  80. 7
      internal/lint-configs/commitlint-config/build.config.ts
  81. 42
      internal/lint-configs/commitlint-config/package.json
  82. 145
      internal/lint-configs/commitlint-config/src/index.ts
  83. 5
      internal/lint-configs/commitlint-config/tsconfig.json
  84. 7
      internal/lint-configs/eslint-config/build.config.ts
  85. 61
      internal/lint-configs/eslint-config/package.json
  86. 10
      internal/lint-configs/eslint-config/src/configs/command.ts
  87. 22
      internal/lint-configs/eslint-config/src/configs/comments.ts
  88. 46
      internal/lint-configs/eslint-config/src/configs/ignores.ts
  89. 27
      internal/lint-configs/eslint-config/src/configs/import.ts
  90. 15
      internal/lint-configs/eslint-config/src/configs/index.ts
  91. 243
      internal/lint-configs/eslint-config/src/configs/javascript.ts
  92. 33
      internal/lint-configs/eslint-config/src/configs/jsdoc.ts
  93. 257
      internal/lint-configs/eslint-config/src/configs/jsonc.ts
  94. 54
      internal/lint-configs/eslint-config/src/configs/node.ts
  95. 81
      internal/lint-configs/eslint-config/src/configs/perfectionist.ts
  96. 17
      internal/lint-configs/eslint-config/src/configs/prettier.ts
  97. 18
      internal/lint-configs/eslint-config/src/configs/regexp.ts
  98. 44
      internal/lint-configs/eslint-config/src/configs/test.ts
  99. 81
      internal/lint-configs/eslint-config/src/configs/typescript.ts
  100. 40
      internal/lint-configs/eslint-config/src/configs/unicorn.ts

4
.browserslistrc

@ -0,0 +1,4 @@
> 1%
last 2 versions
not dead
not ie 11

5
.changeset/README.md

@ -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

@ -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

@ -0,0 +1 @@
export { default } from '@vben/commitlint-config';

21
.editorconfig

@ -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

@ -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

@ -0,0 +1,2 @@
[core]
ignorecase = false

39
.github/ISSUE_TEMPLATE/1-bug.md

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1,6 @@
ports:
- port: 3344
onOpen: open-preview
tasks:
- init: corepack enable && pnpm install
command: pnpm run dev

6
.husky/commit-msg

@ -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

@ -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

@ -0,0 +1 @@
export { default } from '@vben/lint-staged-config';

27
.ls-lint.yml

@ -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

@ -0,0 +1 @@
20

12
.npmrc

@ -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

@ -0,0 +1,13 @@
dist
.local
.output.js
node_modules
.nvmrc
**/*.svg
**/*.sh
public
.npmrc
*-lock.yaml

1
.prettierrc.mjs

@ -0,0 +1 @@
export { default } from '@vben/prettier-config';

3
.stylelintignore

@ -0,0 +1,3 @@
dist
public
__tests__

3
.tazerc.json

@ -0,0 +1,3 @@
{
"exclude": ["zx", "eslint"]
}

34
.vscode/extensions.json

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1,2 @@
# spa-title
VITE_GLOB_APP_TITLE = Vben Admin Pro

6
apps/antd-view/.env.analyze

@ -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

@ -0,0 +1,3 @@
VITE_PUBLIC_PATH = /
VITE_GLOB_API_URL=/vben-api

5
apps/antd-view/.env.production

@ -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

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1 @@
export { default } from '@vben/tailwind-config/postcss';

BIN
apps/antd-view/public/favicon.ico

40
apps/antd-view/src/app.vue

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1 @@
export * from './modules/user';

23
apps/antd-view/src/services/modules/user/index.ts

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -0,0 +1 @@
export { default } from '@vben/tailwind-config';

12
apps/antd-view/tsconfig.json

@ -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

@ -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

@ -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

@ -0,0 +1,5 @@
// @ts-check
import { defineConfig } from '@vben/eslint-config';
export default defineConfig();

7
internal/lint-configs/commitlint-config/build.config.ts

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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',
},
},
];
}

81
internal/lint-configs/eslint-config/src/configs/perfectionist.ts

@ -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

@ -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

@ -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

@ -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',
},
},
];
}

81
internal/lint-configs/eslint-config/src/configs/typescript.ts

@ -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

@ -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 changed in this diff

Loading…
Cancel
Save