Compare commits

...

2 Commits

Author SHA1 Message Date
臧琨 79a9bc9ec6 重构的分支 3 years ago
liliang@qniao.cn 1b9b6ada10 添加src/views/sys/scrapy/index.vue爬虫页面 3 years ago
531 changed files with 8331 additions and 37794 deletions
Split View
  1. 6
      .env
  2. 8
      .env.development
  3. 11
      .eslintrc.js
  4. 11
      .gitattributes
  5. 39
      .github/ISSUE_TEMPLATE/1-bug.md
  6. 32
      .github/ISSUE_TEMPLATE/2-feature.md
  7. 28
      .github/ISSUE_TEMPLATE/3-bug-cn.md
  8. 8
      .github/ISSUE_TEMPLATE/config.yml
  9. 89
      .github/commit-convention.md
  10. 5
      .github/contributing.md
  11. 34
      .github/pull_request_template.md
  12. 123
      .github/workflows/deploy.yml
  13. 17
      .github/workflows/issue-close-require.yml
  14. 29
      .github/workflows/issue-labeled.yml
  15. 24
      .github/workflows/release.yml
  16. 5
      .gitignore
  17. 8
      .husky/commit-msg
  18. 9
      .husky/common.sh
  19. 10
      .husky/pre-commit
  20. 13
      .vscode/extensions.json
  21. 13
      .vscode/launch.json
  22. 160
      .vscode/settings.json
  23. 1262
      CHANGELOG.en_US.md
  24. 1586
      CHANGELOG.md
  25. 1317
      CHANGELOG.zh_CN.md
  26. 1
      CNAME
  27. 222
      LICENSE
  28. 184
      README.md
  29. 175
      README.zh-CN.md
  30. 4
      build/generate/icon/index.ts
  31. 12
      build/script/buildConf.ts
  32. 6
      build/script/postBuild.ts
  33. 10
      build/utils.ts
  34. 6
      build/vite/plugin/compress.ts
  35. 6
      build/vite/plugin/html.ts
  36. 20
      build/vite/plugin/index.ts
  37. 13
      build/vite/plugin/styleImport.ts
  38. 4
      build/vite/plugin/svgSprite.ts
  39. 6
      build/vite/plugin/theme.ts
  40. 74
      commitlint.config.js
  41. 16
      mock/_createProductionServer.ts
  42. 20
      mock/_util.ts
  43. 3
      mock/demo/table-demo.ts
  44. 4
      mock/sys/menu.ts
  45. 14
      mock/sys/user.ts
  46. 190
      package.json
  47. 234
      public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
  48. 230
      public/resource/tinymce/skins/ui/oxide-dark/content.min.css
  49. 12
      public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css
  50. 870
      public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
  51. 234
      public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
  52. 7
      public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
  53. 234
      public/resource/tinymce/skins/ui/oxide/content.inline.min.css
  54. 230
      public/resource/tinymce/skins/ui/oxide/content.min.css
  55. 12
      public/resource/tinymce/skins/ui/oxide/content.mobile.min.css
  56. 870
      public/resource/tinymce/skins/ui/oxide/skin.min.css
  57. 234
      public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
  58. 7
      public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css
  59. 1
      spider-management-backend-web
  60. 1
      src/App.vue
  61. 16
      src/api/demo/account.ts
  62. 9
      src/api/demo/cascader.ts
  63. 12
      src/api/demo/model/areaModel.ts
  64. 15
      src/api/demo/model/optionsModel.ts
  65. 20
      src/api/demo/model/tableModel.ts
  66. 11
      src/api/demo/select.ts
  67. 20
      src/api/demo/table.ts
  68. 11
      src/api/demo/tree.ts
  69. 2
      src/api/model/baseModel.ts
  70. 4
      src/api/sys/model/uploadModel.ts
  71. 12
      src/api/sys/upload.ts
  72. 16
      src/api/sys/user.ts
  73. 4
      src/components/Application/src/AppLocalePicker.vue
  74. 3
      src/components/Button/src/BasicButton.vue
  75. 9
      src/components/Button/src/props.ts
  76. 3
      src/components/CardList/src/CardList.vue
  77. 2
      src/components/CardList/src/data.ts
  78. 4
      src/components/CodeEditor/src/codemirror/codemirror.css
  79. 97
      src/components/Container/src/collapse/CollapseContainer.vue
  80. 56
      src/components/Container/src/collapse/CollapseHeader.vue
  81. 24
      src/components/ContextMenu/src/ContextMenu.vue
  82. 1
      src/components/ContextMenu/src/typing.ts
  83. 5
      src/components/Cropper/src/CopperModal.vue
  84. 6
      src/components/Cropper/src/CropperAvatar.vue
  85. 5
      src/components/Description/src/Description.vue
  86. 2
      src/components/Drawer/src/BasicDrawer.vue
  87. 2
      src/components/Drawer/src/components/DrawerFooter.vue
  88. 7
      src/components/Drawer/src/typing.ts
  89. 2
      src/components/Dropdown/src/Dropdown.vue
  90. 26
      src/components/Excel/src/Export2Excel.ts
  91. 79
      src/components/Excel/src/ImportExcel.vue
  92. 2
      src/components/Form/index.ts
  93. 39
      src/components/Form/src/BasicForm.vue
  94. 4
      src/components/Form/src/componentMap.ts
  95. 7
      src/components/Form/src/components/ApiCascader.vue
  96. 17
      src/components/Form/src/components/ApiRadioGroup.vue
  97. 31
      src/components/Form/src/components/ApiSelect.vue
  98. 134
      src/components/Form/src/components/ApiTransfer.vue
  99. 90
      src/components/Form/src/components/ApiTree.vue
  100. 49
      src/components/Form/src/components/FormItem.vue

6
.env

@ -1,8 +1,8 @@
# port
VITE_PORT = 3100
VITE_PORT = 3201
# spa-title
VITE_GLOB_APP_TITLE = Vben Admin
VITE_GLOB_APP_TITLE = 统一认证管理后台
# spa shortname
VITE_GLOB_APP_SHORT_NAME = vue_vben_admin
VITE_GLOB_APP_SHORT_NAME = one_auth

8
.env.development

@ -1,22 +1,22 @@
# Whether to open mock
VITE_USE_MOCK = true
VITE_USE_MOCK = false
# public path
VITE_PUBLIC_PATH = /
# Cross-domain proxy, you can configure multiple
# Please note that no line breaks
VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
#VITE_PROXY = [["/basic-api","http://localhost:3000"],["/upload","http://localhost:3300/upload"]]
# VITE_PROXY=[["/api","https://vvbin.cn/test"]]
# Delete console
VITE_DROP_CONSOLE = false
# Basic interface address SPA
VITE_GLOB_API_URL=/basic-api
VITE_GLOB_API_URL=http://localhost:8001
# File upload address, optional
VITE_GLOB_UPLOAD_URL=/upload
VITE_GLOB_UPLOAD_URL=/admin/upload/file
# Interface prefix
VITE_GLOB_API_URL_PREFIX=

11
.eslintrc.js

@ -1,4 +1,6 @@
module.exports = {
// @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
env: {
browser: true,
@ -18,7 +20,9 @@ module.exports = {
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended',
'plugin:jest/recommended',
],
rules: {
'vue/script-setup-uses-vars': 'error',
@ -58,7 +62,6 @@ module.exports = {
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/require-explicit-emits': 'off',
'vue/html-self-closing': [
'error',
{
@ -71,6 +74,6 @@ module.exports = {
math: 'always',
},
],
'vue/multi-word-component-names': 'off',
'vue/multi-word-component-names': 'off'
},
};
});

11
.gitattributes

@ -1,11 +0,0 @@
# 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

39
.github/ISSUE_TEMPLATE/1-bug.md

@ -1,39 +0,0 @@
---
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

@ -1,32 +0,0 @@
---
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

@ -1,28 +0,0 @@
---
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

@ -1,8 +0,0 @@
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

@ -1,89 +0,0 @@
## 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

@ -1,5 +0,0 @@
# 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

@ -1,34 +0,0 @@
### `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

123
.github/workflows/deploy.yml

@ -1,123 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,24 +0,0 @@
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.

5
.gitignore

@ -27,8 +27,3 @@ pnpm-debug.log*
*.njsproj
*.sln
*.sw?
package-lock.json
pnpm-lock.yaml
.history

8
.husky/commit-msg

@ -1,8 +0,0 @@
#!/bin/sh
# shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh"
PATH="/usr/local/bin:$PATH"
npx --no-install commitlint --edit "$1"

9
.husky/common.sh

@ -1,9 +0,0 @@
#!/bin/sh
command_exists () {
command -v "$1" >/dev/null 2>&1
}
# Workaround for Windows 10, Git Bash and Yarn
if command_exists winpty && test -t 1; then
exec < /dev/tty
fi

10
.husky/pre-commit

@ -1,10 +0,0 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
[ -n "$CI" ] && exit 0
PATH="/usr/local/bin:$PATH"
# Format and submit code according to lintstagedrc.js configuration
npm run lint:lint-staged

13
.vscode/extensions.json

@ -1,13 +0,0 @@
{
"recommendations": [
"vue.volar",
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode",
"mrmlnc.vscode-less",
"lokalise.i18n-ally",
"antfu.iconify",
"mikestead.dotenv",
"heybourn.headwind"
]
}

13
.vscode/launch.json

@ -1,13 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:3100",
"webRoot": "${workspaceFolder}/src",
"sourceMaps": true
}
]
}

160
.vscode/settings.json

@ -1,160 +0,0 @@
{
"typescript.tsdk": "./node_modules/typescript/lib",
"volar.tsPlugin": true,
"volar.tsPluginStatus": false,
"npm.packageManager": "pnpm",
"editor.tabSize": 2,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.eol": "\n",
"search.exclude": {
"**/node_modules": true,
"**/*.log": true,
"**/*.log*": true,
"**/bower_components": true,
"**/dist": true,
"**/elehukouben": true,
"**/.git": true,
"**/.gitignore": true,
"**/.svn": true,
"**/.DS_Store": true,
"**/.idea": true,
"**/.vscode": false,
"**/yarn.lock": true,
"**/tmp": true,
"out": true,
"dist": true,
"node_modules": true,
"CHANGELOG.md": true,
"examples": true,
"res": true,
"screenshots": true,
"yarn-error.log": true,
"**/.yarn": true
},
"files.exclude": {
"**/.cache": true,
"**/.editorconfig": true,
"**/.eslintcache": true,
"**/bower_components": true,
"**/.idea": true,
"**/tmp": true,
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": 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
},
"stylelint.enable": true,
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
"path-intellisense.mappings": {
"/@/": "${workspaceRoot}/src"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[vue]": {
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.fixAll.stylelint": true
}
},
"i18n-ally.localesPaths": ["src/locales/lang"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"],
"cSpell.words": [
"vben",
"windi",
"browserslist",
"tailwindcss",
"esnext",
"antv",
"tinymce",
"qrcode",
"sider",
"pinia",
"sider",
"nprogress",
"INTLIFY",
"stylelint",
"esno",
"vitejs",
"sortablejs",
"mockjs",
"codemirror",
"iconify",
"commitlint",
"vditor",
"echarts",
"cropperjs",
"logicflow",
"vueuse",
"zxcvbn",
"lintstagedrc",
"brotli",
"tailwindcss",
"sider",
"pnpm",
"antd"
],
"vetur.format.scriptInitialIndent": true,
"vetur.format.styleInitialIndent": true,
"vetur.validation.script": false,
"MicroPython.executeButton": [
{
"text": "▶",
"tooltip": "运行",
"alignment": "left",
"command": "extension.executeFile",
"priority": 3.5
}
],
"MicroPython.syncButton": [
{
"text": "$(sync)",
"tooltip": "同步",
"alignment": "left",
"command": "extension.execute",
"priority": 4
}
]
}

1262
CHANGELOG.en_US.md
File diff suppressed because it is too large
View File

1586
CHANGELOG.md
File diff suppressed because it is too large
View File

1317
CHANGELOG.zh_CN.md
File diff suppressed because it is too large
View File

1
CNAME

@ -1 +0,0 @@
vben.vvbin.cn

222
LICENSE

@ -1,21 +1,201 @@
MIT License
Copyright (c) 2020-present, Vben
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.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2022] [meta unit]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

184
README.md

@ -1,169 +1,39 @@
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="200" height="200" src="https://anncwb.github.io/anncwb/images/logo.png"> </a> <br> <br>
# one-auth-backend
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
#### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
<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.
1. xxxx
2. xxxx
3. xxxx
## 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
1. xxxx
2. xxxx
3. xxxx
## 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
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
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-style-import](https://github.com/anncwb/vite-plugin-style-import) - Used for component library style introduction on demand
- [vite-plugin-theme](https://github.com/anncwb/vite-plugin-theme) - Used for online switching of theme colors and other color-related configurations
- [vite-plugin-imagemin](https://github.com/anncwb/vite-plugin-imagemin) - Used to pack compressed image resources
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - Used to pack input .gz|.brotil files
- [vite-plugin-svg-icons](https://github.com/anncwb/vite-plugin-svg-icons) - Used to quickly generate svg sprite
## 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)
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

175
README.zh-CN.md

@ -1,175 +0,0 @@
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="200" height="200" src="https://anncwb.github.io/anncwb/images/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-style-import](https://github.com/anncwb/vite-plugin-style-import) - 用于组件库样式按需引入
- [vite-plugin-theme](https://github.com/anncwb/vite-plugin-theme) - 用于在线切换主题色等颜色相关配置
- [vite-plugin-imagemin](https://github.com/anncwb/vite-plugin-imagemin) - 用于打包压缩图片资源
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - 用于打包输出.gz|.brotil 文件
- [vite-plugin-svg-icons](https://github.com/anncwb/vite-plugin-svg-icons) - 用于快速生成 svg 雪碧图
## 后台整合示例
- [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)

4
build/generate/icon/index.ts

@ -1,7 +1,7 @@
import path from 'path';
import fs from 'fs-extra';
import inquirer from 'inquirer';
import colors from 'picocolors';
import chalk from 'chalk';
import pkg from '../../../package.json';
async function generateIcon() {
@ -64,7 +64,7 @@ async function generateIcon() {
}
fs.emptyDir(path.join(process.cwd(), 'node_modules/.vite'));
console.log(
`${colors.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
`${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`,
);
});
}

12
build/script/buildConf.ts

@ -3,7 +3,7 @@
*/
import { GLOB_CONFIG_FILE_NAME, OUTPUT_DIR } from '../constant';
import fs, { writeFileSync } from 'fs-extra';
import colors from 'picocolors';
import chalk from 'chalk';
import { getEnvConfig, getRootPath } from '../utils';
import { getConfigFileName } from '../getConfigFileName';
@ -21,22 +21,20 @@ function createConfig(params: CreateConfigParams) {
try {
const windowConf = `window.${configName}`;
// Ensure that the variable will not be modified
let configStr = `${windowConf}=${JSON.stringify(config)};`;
configStr += `
const configStr = `${windowConf}=${JSON.stringify(config)};
Object.freeze(${windowConf});
Object.defineProperty(window, "${configName}", {
configurable: false,
writable: false,
});
`.replace(/\s/g, '');
fs.mkdirp(getRootPath(OUTPUT_DIR));
writeFileSync(getRootPath(`${OUTPUT_DIR}/${configFileName}`), configStr);
console.log(colors.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(colors.gray(OUTPUT_DIR + '/' + colors.green(configFileName)) + '\n');
console.log(chalk.cyan(`✨ [${pkg.name}]`) + ` - configuration file is build successfully:`);
console.log(chalk.gray(OUTPUT_DIR + '/' + chalk.green(configFileName)) + '\n');
} catch (error) {
console.log(colors.red('configuration file configuration file failed to package:\n' + error));
console.log(chalk.red('configuration file configuration file failed to package:\n' + error));
}
}

6
build/script/postBuild.ts

@ -1,7 +1,7 @@
// #!/usr/bin/env node
import { runBuildConfig } from './buildConf';
import colors from 'picocolors';
import chalk from 'chalk';
import pkg from '../../package.json';
@ -14,9 +14,9 @@ export const runBuild = async () => {
runBuildConfig();
}
console.log(`${colors.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
console.log(`${chalk.cyan(`[${pkg.name}]`)}` + ' - build successfully!');
} catch (error) {
console.log(colors.red('vite build error:\n' + error));
console.log(chalk.red('vite build error:\n' + error));
process.exit(1);
}
};

10
build/utils.ts

@ -36,11 +36,11 @@ export function wrapperEnv(envConf: Recordable): ViteEnv {
}
}
ret[envName] = realName;
// if (typeof realName === 'string') {
// process.env[envName] = realName;
// } else if (typeof realName === 'object') {
// process.env[envName] = JSON.stringify(realName);
// }
if (typeof realName === 'string') {
process.env[envName] = realName;
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName);
}
}
return ret;
}

6
build/vite/plugin/compress.ts

@ -2,16 +2,16 @@
* Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
* https://github.com/anncwb/vite-plugin-compression
*/
import type { PluginOption } from 'vite';
import type { Plugin } from 'vite';
import compressPlugin from 'vite-plugin-compression';
export function configCompressPlugin(
compress: 'gzip' | 'brotli' | 'none',
deleteOriginFile = false,
): PluginOption | PluginOption[] {
): Plugin | Plugin[] {
const compressList = compress.split(',');
const plugins: PluginOption[] = [];
const plugins: Plugin[] = [];
if (compressList.includes('gzip')) {
plugins.push(

6
build/vite/plugin/html.ts

@ -2,8 +2,8 @@
* Plugin to minimize and use ejs template syntax in index.html.
* https://github.com/anncwb/vite-plugin-html
*/
import type { PluginOption } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';
import type { Plugin } from 'vite';
import html from 'vite-plugin-html';
import pkg from '../../../package.json';
import { GLOB_CONFIG_FILE_NAME } from '../../constant';
@ -16,7 +16,7 @@ export function configHtmlPlugin(env: ViteEnv, isBuild: boolean) {
return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`;
};
const htmlPlugin: PluginOption[] = createHtmlPlugin({
const htmlPlugin: Plugin[] = html({
minify: isBuild,
inject: {
// Inject data into ejs template

20
build/vite/plugin/index.ts

@ -1,11 +1,10 @@
import { PluginOption } from 'vite';
import type { Plugin } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import legacy from '@vitejs/plugin-legacy';
import purgeIcons from 'vite-plugin-purge-icons';
import windiCSS from 'vite-plugin-windicss';
import VitePluginCertificate from 'vite-plugin-mkcert';
//import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import { configHtmlPlugin } from './html';
import { configPwaConfig } from './pwa';
import { configMockPlugin } from './mock';
@ -15,6 +14,7 @@ import { configVisualizerConfig } from './visualizer';
import { configThemePlugin } from './theme';
import { configImageminPlugin } from './imagemin';
import { configSvgIconsPlugin } from './svgSprite';
import { configHmrPlugin } from './hmr';
export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
const {
@ -25,21 +25,21 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE,
} = viteEnv;
const vitePlugins: (PluginOption | PluginOption[])[] = [
const vitePlugins: (Plugin | Plugin[])[] = [
// have to
vue(),
// have to
vueJsx(),
// support name
//vueSetupExtend(),
VitePluginCertificate({
source: 'coding',
}),
vueSetupExtend(),
];
// vite-plugin-windicss
vitePlugins.push(windiCSS());
// TODO
!isBuild && vitePlugins.push(configHmrPlugin());
// @vitejs/plugin-legacy
VITE_LEGACY && isBuild && vitePlugins.push(legacy());
@ -61,12 +61,12 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
// rollup-plugin-visualizer
vitePlugins.push(configVisualizerConfig());
// vite-plugin-theme
//vite-plugin-theme
vitePlugins.push(configThemePlugin(isBuild));
// The following plugins only work in the production environment
if (isBuild) {
// vite-plugin-imagemin
//vite-plugin-imagemin
VITE_USE_IMAGEMIN && vitePlugins.push(configImageminPlugin());
// rollup-plugin-gzip

13
build/vite/plugin/styleImport.ts

@ -2,13 +2,13 @@
* Introduces component library styles on demand.
* https://github.com/anncwb/vite-plugin-style-import
*/
import { createStyleImportPlugin, VxeTableResolve } from 'vite-plugin-style-import';
import styleImport from 'vite-plugin-style-import';
export function configStyleImportPlugin(_isBuild: boolean) {
if (!_isBuild) {
export function configStyleImportPlugin(isBuild: boolean) {
if (!isBuild) {
return [];
}
const styleImportPlugin = createStyleImportPlugin({
const styleImportPlugin = styleImport({
libs: [
{
libraryName: 'ant-design-vue',
@ -19,7 +19,6 @@ export function configStyleImportPlugin(_isBuild: boolean) {
'anchor-link',
'sub-menu',
'menu-item',
'menu-divider',
'menu-item-group',
'breadcrumb-item',
'breadcrumb-separator',
@ -49,7 +48,6 @@ export function configStyleImportPlugin(_isBuild: boolean) {
// 这里是需要额外引入样式的子组件列表
// 单独引入子组件时需引入组件样式,否则会在打包后导致子组件样式丢失
const replaceList = {
textarea: 'input',
'typography-text': 'typography',
'typography-title': 'typography',
'typography-paragraph': 'typography',
@ -65,8 +63,6 @@ export function configStyleImportPlugin(_isBuild: boolean) {
'layout-footer': 'layout',
'layout-header': 'layout',
'month-picker': 'date-picker',
'range-picker': 'date-picker',
'image-preview-group': 'image',
};
return ignoreList.includes(name)
@ -77,7 +73,6 @@ export function configStyleImportPlugin(_isBuild: boolean) {
},
},
],
resolves: [VxeTableResolve()],
});
return styleImportPlugin;
}

4
build/vite/plugin/svgSprite.ts

@ -3,11 +3,11 @@
* https://github.com/anncwb/vite-plugin-svg-icons
*/
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import SvgIconsPlugin from 'vite-plugin-svg-icons';
import path from 'path';
export function configSvgIconsPlugin(isBuild: boolean) {
const svgIconsPlugin = createSvgIconsPlugin({
const svgIconsPlugin = SvgIconsPlugin({
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
svgoOptions: isBuild,
// default

6
build/vite/plugin/theme.ts

@ -2,7 +2,7 @@
* Vite plugin for website theme color switching
* https://github.com/anncwb/vite-plugin-theme
*/
import type { PluginOption } from 'vite';
import type { Plugin } from 'vite';
import path from 'path';
import {
viteThemePlugin,
@ -14,7 +14,7 @@ import {
import { getThemeColors, generateColors } from '../../config/themeConfig';
import { generateModifyVars } from '../../generate/generateModifyVars';
export function configThemePlugin(isBuild: boolean): PluginOption[] {
export function configThemePlugin(isBuild: boolean): Plugin[] {
const colors = generateColors({
mixDarken,
mixLighten,
@ -85,5 +85,5 @@ export function configThemePlugin(isBuild: boolean): PluginOption[] {
}),
];
return plugin as unknown as PluginOption[];
return plugin as unknown as Plugin[];
}

74
commitlint.config.js

@ -1,23 +1,3 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const scopes = fs
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name.replace(/s$/, ''));
// 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$/, '');
/** @type {import('cz-git').UserConfig} */
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
@ -50,58 +30,4 @@ module.exports = {
],
],
},
prompt: {
/** @use `yarn commit :f` */
alias: {
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
b: 'build: bump dependencies',
c: 'chore: update config',
},
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
defaultScope: scopeComplete,
scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
allowCustomIssuePrefixs: false,
// English
typesAppend: [
{ value: 'wip', name: 'wip: work in process' },
{ value: 'workflow', name: 'workflow: workflow improvements' },
{ value: 'types', name: 'types: type definition file changes' },
],
// 中英文对照版
// 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: 自定义',
},
};

16
mock/_createProductionServer.ts

@ -1,21 +1,5 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
// 问题描述
// 1. `import.meta.globEager` 已被弃用, 需要升级vite版本,有兼容问题
// 2. `vite-plugin-mock` 插件问题 https://github.com/vbenjs/vite-plugin-mock/issues/56
// const modules: Record<string, any> = import.meta.glob("./**/*.ts", {
// import: "default",
// eager: true,
// });
// const mockModules = Object.keys(modules).reduce((pre, key) => {
// if (!key.includes("/_")) {
// pre.push(...modules[key]);
// }
// return pre;
// }, [] as any[]);
const modules = import.meta.globEager('./**/*.ts');
const mockModules: any[] = [];

20
mock/_util.ts

@ -1,12 +1,11 @@
// Interface data format used to return a unified format
import { ResultEnum } from '/@/enums/httpEnum';
export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}) {
export function resultSuccess<T = Recordable>(result: T, { message = '' } = {}) {
return {
code: ResultEnum.SUCCESS,
// code: 0,
result,
message,
type: 'success',
type: 1,
};
}
@ -27,10 +26,7 @@ export function resultPageSuccess<T = any>(
};
}
export function resultError(
message = 'Request failed',
{ code = ResultEnum.ERROR, result = null } = {},
) {
export function resultError(message = 'Request failed', { code = -1, result = null } = {}) {
return {
code,
result,
@ -41,9 +37,11 @@ export function resultError(
export function pagination<T = any>(pageNo: number, pageSize: number, array: T[]): T[] {
const offset = (pageNo - 1) * Number(pageSize);
return offset + Number(pageSize) >= array.length
? array.slice(offset, array.length)
: array.slice(offset, offset + Number(pageSize));
const ret =
offset + Number(pageSize) >= array.length
? array.slice(offset, array.length)
: array.slice(offset, offset + Number(pageSize));
return ret;
}
export interface requestParams {

3
mock/demo/table-demo.ts

@ -27,9 +27,6 @@ const demoList = (() => {
name6: '@cname()',
name7: '@cname()',
name8: '@cname()',
radio1: `选项${index + 1}`,
radio2: `选项${index + 1}`,
radio3: `选项${index + 1}`,
avatar: Random.image('400x400', Random.color(), Random.color(), Random.first()),
imgArr: getRandomPics(Math.ceil(Math.random() * 3) + 1),
imgs: getRandomPics(Math.ceil(Math.random() * 3) + 1),

4
mock/sys/menu.ts

@ -221,11 +221,11 @@ const linkRoute = {
name: 'Doc',
meta: {
title: 'routes.demo.iframe.doc',
frameSrc: 'https://doc.vvbin.cn/',
frameSrc: 'https://vvbin.cn/doc-next/',
},
},
{
path: 'https://doc.vvbin.cn/',
path: 'https://vvbin.cn/doc-next/',
name: 'DocExternal',
component: 'LAYOUT',
meta: {

14
mock/sys/user.ts

@ -7,7 +7,7 @@ export function createFakeUserList() {
userId: '1',
username: 'vben',
realName: 'Vben Admin',
avatar: '',
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640',
desc: 'manager',
password: '123456',
token: 'fakeToken1',
@ -24,7 +24,7 @@ export function createFakeUserList() {
username: 'test',
password: '123456',
realName: 'test user',
avatar: '',
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=339449197&s=640',
desc: 'tester',
token: 'fakeToken2',
homePath: '/dashboard/workbench',
@ -69,7 +69,7 @@ export default [
},
},
{
url: '/basic-api/getUserInfo',
url: '/basic-api/user/get/user-info',
method: 'get',
response: (request: requestParams) => {
const token = getRequestToken(request);
@ -111,12 +111,4 @@ export default [
return resultSuccess(undefined, { message: 'Token has been destroyed' });
},
},
{
url: '/basic-api/testRetry',
statusCode: 405,
method: 'get',
response: () => {
return resultError('Error!');
},
},
] as MockMethod[];

190
package.json

@ -1,13 +1,12 @@
{
"name": "vben-admin",
"version": "2.8.0",
"name": "aone-auth-admin-backend",
"version": "1.0.0",
"author": {
"name": "vben",
"email": "anncwb@126.com",
"url": "https://github.com/anncwb"
"name": "meta unit",
"email": "metaunit@163.com",
"url": "https://gitee.com/meta_unit"
},
"scripts": {
"commit": "czg",
"bootstrap": "pnpm install",
"serve": "npm run dev",
"dev": "vite",
@ -18,7 +17,6 @@
"type:check": "vue-tsc --noEmit --skipLibCheck",
"preview": "npm run build && vite preview",
"preview:dist": "vite preview",
"log": "conventional-changelog -p angular -i CHANGELOG.md -s",
"clean:cache": "rimraf node_modules/.cache/ && rimraf node_modules/.vite",
"clean:lib": "rimraf node_modules",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
@ -26,6 +24,7 @@
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged",
"test:unit": "jest",
"test:unit-coverage": "jest --coverage",
"test:gzip": "npx http-server dist --cors --gzip -c-1",
"test:br": "npx http-server dist --cors --brotli -c-1",
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
@ -34,127 +33,119 @@
},
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons-vue": "^6.1.0",
"@iconify/iconify": "^2.2.1",
"@logicflow/core": "^1.1.13",
"@logicflow/extension": "^1.1.13",
"@vue/runtime-core": "^3.2.33",
"@vue/shared": "^3.2.33",
"@vueuse/core": "^8.3.0",
"@vueuse/shared": "^8.3.0",
"@zxcvbn-ts/core": "^2.0.1",
"ant-design-vue": "^3.2.0",
"axios": "^0.26.1",
"codemirror": "^5.65.3",
"@ant-design/icons-vue": "^6.0.1",
"@iconify/iconify": "^2.0.4",
"@logicflow/core": "^0.7.5",
"@logicflow/extension": "^0.7.5",
"@vueuse/core": "^6.8.0",
"@vueuse/shared": "^6.8.0",
"@zxcvbn-ts/core": "^1.0.0",
"ant-design-vue": "2.2.8",
"axios": "^0.24.0",
"codemirror": "^5.63.3",
"cropperjs": "^1.5.12",
"crypto-js": "^4.1.1",
"dayjs": "^1.11.1",
"echarts": "^5.3.2",
"exceljs": "^4.3.0",
"intro.js": "^5.1.0",
"echarts": "^5.2.2",
"intro.js": "^4.3.0",
"lodash-es": "^4.17.21",
"mockjs": "^1.1.0",
"moment": "^2.29.1",
"nprogress": "^0.2.0",
"path-to-regexp": "^6.2.0",
"pinia": "2.0.12",
"pinia": "2.0.2",
"print-js": "^1.6.0",
"qrcode": "^1.5.0",
"qs": "^6.10.3",
"qrcode": "^1.4.4",
"qs": "^6.10.1",
"resize-observer-polyfill": "^1.5.1",
"showdown": "^2.1.0",
"sortablejs": "^1.15.0",
"tinymce": "^5.10.7",
"vditor": "^3.8.13",
"vue": "^3.2.45",
"showdown": "^1.9.1",
"sortablejs": "^1.14.0",
"tinymce": "^5.10.1",
"vditor": "^3.8.7",
"vue": "^3.2.21",
"vue-i18n": "^9.1.9",
"vue-json-pretty": "^2.0.6",
"vue-router": "^4.0.14",
"vue-json-pretty": "^2.0.5",
"vue-router": "^4.0.12",
"vue-types": "^4.1.1",
"vxe-table": "^4.3.9",
"vxe-table-plugin-export-xlsx": "^3.0.4",
"xe-utils": "^3.5.7",
"xlsx": "^0.18.5",
"vuedraggable": "^4.1.0"
"xlsx": "^0.17.3"
},
"devDependencies": {
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@iconify/json": "^2.1.30",
"@purge-icons/generated": "^0.8.1",
"@commitlint/cli": "^14.1.0",
"@commitlint/config-conventional": "^14.1.0",
"@iconify/json": "^1.1.426",
"@purge-icons/generated": "^0.7.0",
"@types/codemirror": "^5.60.5",
"@types/crypto-js": "^4.1.1",
"@types/crypto-js": "^4.0.2",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.2.1",
"@types/inquirer": "^8.1.3",
"@types/intro.js": "^3.0.2",
"@types/lodash-es": "^4.17.6",
"@types/mockjs": "^1.0.6",
"@types/node": "^17.0.25",
"@types/jest": "^27.0.2",
"@types/lodash-es": "^4.17.5",
"@types/mockjs": "^1.0.4",
"@types/node": "^16.11.7",
"@types/nprogress": "^0.2.0",
"@types/qrcode": "^1.4.2",
"@types/qrcode": "^1.4.1",
"@types/qs": "^6.9.7",
"@types/showdown": "^1.9.4",
"@types/sortablejs": "^1.10.7",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"@vitejs/plugin-legacy": "^1.8.1",
"@vitejs/plugin-vue": "^2.3.1",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vue/compiler-sfc": "^3.2.33",
"@vue/test-utils": "^2.0.0-rc.21",
"autoprefixer": "^10.4.4",
"conventional-changelog-cli": "^2.2.2",
"@typescript-eslint/eslint-plugin": "^5.3.1",
"@typescript-eslint/parser": "^5.3.1",
"@vitejs/plugin-legacy": "^1.6.2",
"@vitejs/plugin-vue": "^1.9.4",
"@vitejs/plugin-vue-jsx": "^1.2.0",
"@vue/compiler-sfc": "3.2.21",
"@vue/test-utils": "^2.0.0-rc.16",
"autoprefixer": "^10.4.0",
"commitizen": "^4.2.4",
"conventional-changelog-cli": "^2.1.1",
"cross-env": "^7.0.3",
"cz-git": "^1.3.9",
"czg": "^1.3.9",
"dotenv": "^16.0.0",
"eslint": "^8.13.0",
"eslint-config-prettier": "^8.5.0",
"dotenv": "^10.0.0",
"eslint": "^8.2.0",
"eslint-config-prettier": "^8.3.0",
"eslint-define-config": "^1.1.3",
"eslint-plugin-jest": "^25.2.4",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.6.0",
"esno": "^0.14.1",
"fs-extra": "^10.1.0",
"eslint-plugin-vue": "^8.0.3",
"esno": "^0.12.0",
"fs-extra": "^10.0.0",
"husky": "^7.0.4",
"inquirer": "^8.2.2",
"inquirer": "^8.2.0",
"jest": "^27.3.1",
"less": "^4.1.2",
"lint-staged": "12.3.7",
"lint-staged": "11.2.6",
"npm-run-all": "^4.1.5",
"picocolors": "^1.0.0",
"postcss": "^8.4.12",
"postcss-html": "^1.4.1",
"postcss-less": "^6.0.0",
"prettier": "^2.6.2",
"postcss": "^8.3.11",
"postcss-html": "^1.2.0",
"postcss-less": "^5.0.0",
"prettier": "^2.4.1",
"rimraf": "^3.0.2",
"rollup": "^2.70.2",
"rollup-plugin-visualizer": "^5.6.0",
"sass": "^1.57.1",
"stylelint": "^14.7.1",
"rollup-plugin-visualizer": "^5.5.2",
"stylelint": "^14.0.1",
"stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^7.0.0",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^25.0.0",
"stylelint-config-standard": "^23.0.0",
"stylelint-order": "^5.0.0",
"ts-node": "^10.7.0",
"typescript": "^4.6.3",
"vite": "^2.9.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-mkcert": "^1.6.0",
"ts-jest": "^27.0.7",
"ts-node": "^10.4.0",
"typescript": "^4.4.4",
"vite": "^2.6.14",
"vite-plugin-compression": "^0.3.5",
"vite-plugin-html": "^2.1.1",
"vite-plugin-imagemin": "^0.4.6",
"vite-plugin-mock": "^2.9.6",
"vite-plugin-purge-icons": "^0.8.1",
"vite-plugin-pwa": "^0.11.13",
"vite-plugin-style-import": "^2.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-theme": "^0.8.6",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vite-plugin-windicss": "^1.8.4",
"vue-eslint-parser": "^8.3.0",
"vue-tsc": "^1.0.9"
"vite-plugin-purge-icons": "^0.7.0",
"vite-plugin-pwa": "^0.11.3",
"vite-plugin-style-import": "^1.3.0",
"vite-plugin-svg-icons": "^1.0.5",
"vite-plugin-theme": "^0.8.1",
"vite-plugin-vue-setup-extend": "^0.1.0",
"vite-plugin-windicss": "^1.5.1",
"vue-eslint-parser": "^8.0.1",
"vue-tsc": "^0.29.3"
},
"resolutions": {
"//": "Used to install imagemin dependencies, because imagemin may not be installed in China. If it is abroad, you can delete it",
"bin-wrapper": "npm:bin-wrapper-china",
"rollup": "^2.56.3",
"gifsicle": "5.2.0"
"rollup": "^2.56.3"
},
"repository": {
"type": "git",
@ -191,10 +182,5 @@
"*.md": [
"prettier --write"
]
},
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
}

234
public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
File diff suppressed because it is too large
View File

230
public/resource/tinymce/skins/ui/oxide-dark/content.min.css
File diff suppressed because it is too large
View File

12
public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css

@ -4,4 +4,14 @@
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;}
body{-webkit-text-size-adjust: none;}
body img{max-width: 96vw;}
body table img{max-width: 95%;}
body{font-family: sans-serif;}
table{border-collapse: collapse;}

870
public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
File diff suppressed because it is too large
View File

234
public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
File diff suppressed because it is too large
View File

7
public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css

@ -1,7 +0,0 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

234
public/resource/tinymce/skins/ui/oxide/content.inline.min.css
File diff suppressed because it is too large
View File

230
public/resource/tinymce/skins/ui/oxide/content.min.css
File diff suppressed because it is too large
View File

12
public/resource/tinymce/skins/ui/oxide/content.mobile.min.css

@ -4,4 +4,14 @@
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;}
body{-webkit-text-size-adjust: none;}
body img{max-width: 96vw;}
body table img{max-width: 95%;}
body{font-family: sans-serif;}
table{border-collapse: collapse;}

870
public/resource/tinymce/skins/ui/oxide/skin.min.css
File diff suppressed because it is too large
View File

234
public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
File diff suppressed because it is too large
View File

7
public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css

@ -1,7 +0,0 @@
/**
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
* Licensed under the LGPL or a commercial license.
* For LGPL see License.txt in the project root for license information.
* For commercial licenses see https://www.tiny.cloud/
*/
body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}

1
spider-management-backend-web

@ -1 +0,0 @@
Subproject commit f25cfcc7beafd63ca7c80757785ac3f0cf4b9a3a

1
src/App.vue

@ -12,7 +12,6 @@
import { useTitle } from '/@/hooks/web/useTitle';
import { useLocale } from '/@/locales/useLocale';
import 'dayjs/locale/zh-cn';
// support Multi-language
const { getAntdLocale } = useLocale();

16
src/api/demo/account.ts

@ -1,16 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
import { GetAccountInfoModel } from './model/accountModel';
enum Api {
ACCOUNT_INFO = '/account/getAccountInfo',
SESSION_TIMEOUT = '/user/sessionTimeout',
TOKEN_EXPIRED = '/user/tokenExpired',
}
// Get personal center-basic settings
export const accountInfoApi = () => defHttp.get<GetAccountInfoModel>({ url: Api.ACCOUNT_INFO });
export const sessionTimeoutApi = () => defHttp.post<void>({ url: Api.SESSION_TIMEOUT });
export const tokenExpiredApi = () => defHttp.post<void>({ url: Api.TOKEN_EXPIRED });

9
src/api/demo/cascader.ts

@ -1,9 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
import { AreaModel, AreaParams } from '/@/api/demo/model/areaModel';
enum Api {
AREA_RECORD = '/cascader/getAreaRecord',
}
export const areaRecord = (data: AreaParams) =>
defHttp.post<AreaModel>({ url: Api.AREA_RECORD, data });

12
src/api/demo/model/areaModel.ts

@ -1,12 +0,0 @@
export interface AreaModel {
id: string;
code: string;
parentCode: string;
name: string;
levelType: number;
[key: string]: string | number;
}
export interface AreaParams {
parentCode: string;
}

15
src/api/demo/model/optionsModel.ts

@ -1,15 +0,0 @@
import { BasicFetchResult } from '/@/api/model/baseModel';
export interface DemoOptionsItem {
label: string;
value: string;
}
export interface selectParams {
id: number | string;
}
/**
* @description: Request list return value
*/
export type DemoOptionsGetResultModel = BasicFetchResult<DemoOptionsItem>;

20
src/api/demo/model/tableModel.ts

@ -1,20 +0,0 @@
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
/**
* @description: Request list interface parameters
*/
export type DemoParams = BasicPageParams;
export interface DemoListItem {
id: string;
beginTime: string;
endTime: string;
address: string;
name: string;
no: number;
status: number;
}
/**
* @description: Request list return value
*/
export type DemoListGetResultModel = BasicFetchResult<DemoListItem>;

11
src/api/demo/select.ts

@ -1,11 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
import { DemoOptionsItem, selectParams } from './model/optionsModel';
enum Api {
OPTIONS_LIST = '/select/getDemoOptions',
}
/**
* @description: Get sample options value
*/
export const optionsListApi = (params?: selectParams) =>
defHttp.get<DemoOptionsItem[]>({ url: Api.OPTIONS_LIST, params });

20
src/api/demo/table.ts

@ -1,20 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
import { DemoParams, DemoListGetResultModel } from './model/tableModel';
enum Api {
DEMO_LIST = '/table/getDemoList',
}
/**
* @description: Get sample list value
*/
export const demoListApi = (params: DemoParams) =>
defHttp.get<DemoListGetResultModel>({
url: Api.DEMO_LIST,
params,
headers: {
// @ts-ignore
ignoreCancelToken: true,
},
});

11
src/api/demo/tree.ts

@ -1,11 +0,0 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
TREE_OPTIONS_LIST = '/tree/getDemoOptions',
}
/**
* @description: Get sample options value
*/
export const treeOptionsListApi = (params?: Recordable) =>
defHttp.get<Recordable[]>({ url: Api.TREE_OPTIONS_LIST, params });

2
src/api/model/baseModel.ts

@ -1,5 +1,5 @@
export interface BasicPageParams {
page: number;
pageNo: number;
pageSize: number;
}

4
src/api/sys/model/uploadModel.ts

@ -1,5 +1,3 @@
export interface UploadApiResult {
message: string;
code: number;
export interface UploadFileResult {
url: string;
}

12
src/api/sys/upload.ts

@ -1,22 +1,22 @@
import { UploadApiResult } from './model/uploadModel';
import { UploadFileResult } from './model/uploadModel';
import { defHttp } from '/@/utils/http/axios';
import { UploadFileParams } from '/#/axios';
import { useGlobSetting } from '/@/hooks/setting';
const { uploadUrl = '' } = useGlobSetting();
const { uploadUrl = '/admin/upload/file' } = useGlobSetting();
/**
* @description: Upload interface
*/
export function uploadApi(
export const uploadApi = (
params: UploadFileParams,
onUploadProgress: (progressEvent: ProgressEvent) => void,
) {
return defHttp.uploadFile<UploadApiResult>(
) => {
return defHttp.uploadFile<UploadFileResult>(
{
url: uploadUrl,
onUploadProgress,
},
params,
);
}
};

16
src/api/sys/user.ts

@ -6,9 +6,8 @@ import { ErrorMessageMode } from '/#/axios';
enum Api {
Login = '/login',
Logout = '/logout',
GetUserInfo = '/getUserInfo',
GetUserInfo = '/user/get/user-info',
GetPermCode = '/getPermCode',
TestRetry = '/testRetry',
}
/**
@ -40,16 +39,3 @@ export function getPermCode() {
export function doLogout() {
return defHttp.get({ url: Api.Logout });
}
export function testRetry() {
return defHttp.get(
{ url: Api.TestRetry },
{
retryRequest: {
isOpenRetry: true,
count: 5,
waitTime: 1000,
},
},
);
}

4
src/components/Application/src/AppLocalePicker.vue

@ -4,11 +4,11 @@
-->
<template>
<Dropdown
placement="bottom"
placement="bottomCenter"
:trigger="['click']"
:dropMenuList="localeList"
:selectedKeys="selectedKeys"
@menu-event="handleMenuEvent"
@menuEvent="handleMenuEvent"
overlayClassName="app-locale-picker-overlay"
>
<span class="cursor-pointer flex items-center">

3
src/components/Button/src/BasicButton.vue

@ -10,15 +10,14 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { Button } from 'ant-design-vue';
export default defineComponent({
name: 'AButton',
extends: Button,
inheritAttrs: false,
});
</script>
<script lang="ts" setup>
import { computed, unref } from 'vue';
import { Button } from 'ant-design-vue';
import Icon from '/@/components/Icon/src/Icon.vue';
import { buttonProps } from './props';
import { useAttrs } from '/@/hooks/core/useAttrs';

9
src/components/Button/src/props.ts

@ -1,12 +1,5 @@
const validColors = ['error', 'warning', 'success', ''] as const;
type ButtonColorType = typeof validColors[number];
export const buttonProps = {
color: {
type: String as PropType<ButtonColorType>,
validator: (v) => validColors.includes(v),
default: '',
},
color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) },
loading: { type: Boolean },
disabled: { type: Boolean },
/**

3
src/components/CardList/src/CardList.vue

@ -3,6 +3,7 @@
<div class="p-4 mb-2 bg-white">
<BasicForm @register="registerForm" />
</div>
{{ sliderProp.width }}
<div class="p-2 bg-white">
<List
:grid="{ gutter: 5, xs: 1, sm: 2, md: 4, lg: 4, xl: 6, xxl: grid }"
@ -38,7 +39,7 @@
<Image :src="item.imgs[0]" />
</div>
</template>
<template #actions>
<template class="ant-card-actions" #actions>
<!-- <SettingOutlined key="setting" />-->
<EditOutlined key="edit" />
<Dropdown

2
src/components/CardList/src/data.ts

@ -1,5 +1,5 @@
import { ref } from 'vue';
// 每行个数
//每行个数
export const grid = ref(12);
// slider属性
export const useSlider = (min = 6, max = 12) => {

4
src/components/CodeEditor/src/codemirror/codemirror.css

@ -53,7 +53,7 @@
color: var(--comment);
text-align: right;
white-space: nowrap;
opacity: 0.6;
opacity: 60%;
}
.CodeMirror-guttermarker {
@ -90,7 +90,7 @@
display: inline-block;
font-size: 0.8em;
content: '>';
opacity: 0.8;
opacity: 80%;
transform: rotate(90deg);
transition: transform 0.2s;
}

97
src/components/Container/src/collapse/CollapseContainer.vue

@ -1,14 +1,40 @@
<script lang="tsx">
import { ref, unref, defineComponent, type PropType, type ExtractPropTypes } from 'vue';
import { isNil } from 'lodash-es';
<template>
<div :class="prefixCls">
<CollapseHeader v-bind="$props" :prefixCls="prefixCls" :show="show" @expand="handleExpand">
<template #title>
<slot name="title"></slot>
</template>
<template #action>
<slot name="action"></slot>
</template>
</CollapseHeader>
<div class="p-2">
<CollapseTransition :enable="canExpan">
<Skeleton v-if="loading" :active="loading" />
<div :class="`${prefixCls}__body`" v-else v-show="show">
<slot></slot>
</div>
</CollapseTransition>
</div>
<div :class="`${prefixCls}__footer`" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue';
import { ref } from 'vue';
// component
import { Skeleton } from 'ant-design-vue';
import { CollapseTransition } from '/@/components/Transition';
import CollapseHeader from './CollapseHeader.vue';
import { triggerWindowResize } from '/@/utils/event';
// hook
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useDesign } from '/@/hooks/web/useDesign';
const collapseContainerProps = {
const props = defineProps({
title: { type: String, default: '' },
loading: { type: Boolean },
/**
@ -31,60 +57,23 @@
* Delayed loading time
*/
lazyTime: { type: Number, default: 0 },
};
export type CollapseContainerProps = ExtractPropTypes<typeof collapseContainerProps>;
export default defineComponent({
name: 'CollapseContainer',
props: collapseContainerProps,
setup(props, { expose, slots }) {
const { prefixCls } = useDesign('collapse-container');
const show = ref(true);
const handleExpand = (val: boolean) => {
show.value = isNil(val) ? !show.value : val;
if (props.triggerWindowResize) {
// 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200);
}
};
expose({ handleExpand });
});
return () => (
<div class={unref(prefixCls)}>
<CollapseHeader
{...props}
prefixCls={unref(prefixCls)}
onExpand={handleExpand}
show={show.value}
v-slots={{
title: slots.title,
action: slots.action,
}}
/>
const show = ref(true);
<div class="p-2">
<CollapseTransition enable={props.canExpan}>
{props.loading ? (
<Skeleton active={props.loading} />
) : (
<div class={`${prefixCls}__body`} v-show={show.value}>{slots.default?.()}</div>
)}
</CollapseTransition>
</div>
const { prefixCls } = useDesign('collapse-container');
{slots.footer && <div class={`${prefixCls}__footer`}>{slots.footer()}</div>}
</div>
);
},
});
/**
* @description: Handling development events
*/
function handleExpand() {
show.value = !show.value;
if (props.triggerWindowResize) {
// 200 milliseconds here is because the expansion has animation,
useTimeoutFn(triggerWindowResize, 200);
}
}
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-collapse-container';

56
src/components/Container/src/collapse/CollapseHeader.vue

@ -1,44 +1,38 @@
<script lang="tsx">
import { defineComponent, computed, unref, type ExtractPropTypes } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
<template>
<div :class="[`${prefixCls}__header px-2 py-5`, $attrs.class]">
<BasicTitle :helpMessage="helpMessage" normal>
<template v-if="title">
{{ title }}
</template>
<template v-else>
<slot name="title"></slot>
</template>
</BasicTitle>
<div :class="`${prefixCls}__action`">
<slot name="action"></slot>
<BasicArrow v-if="canExpan" up :expand="show" @click="$emit('expand')" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicArrow, BasicTitle } from '/@/components/Basic';
const collapseHeaderProps = {
prefixCls: String,
title: String,
show: Boolean,
canExpan: Boolean,
const props = {
prefixCls: { type: String },
helpMessage: {
type: [Array, String] as PropType<string[] | string>,
default: '',
},
title: { type: String },
show: { type: Boolean },
canExpan: { type: Boolean },
};
export type CollapseHeaderProps = ExtractPropTypes<typeof collapseHeaderProps>;
export default defineComponent({
name: 'CollapseHeader',
components: { BasicArrow, BasicTitle },
inheritAttrs: false,
props: collapseHeaderProps,
props,
emits: ['expand'],
setup(props, { slots, attrs, emit }) {
const { prefixCls } = useDesign('collapse-container');
const _prefixCls = computed(() => props.prefixCls || unref(prefixCls));
return () => (
<div class={[`${unref(_prefixCls)}__header px-2 py-5`, attrs.class]}>
<BasicTitle helpMessage={props.helpMessage} normal>
{slots.title?.() || props.title}
</BasicTitle>
<div class={`${unref(_prefixCls)}__action`}>
{slots.action
? slots.action({ expand: props.show, onClick: () => emit('expand') })
: props.canExpan && (
<BasicArrow up expand={props.show} onClick={() => emit('expand')} />
)}
</div>
</div>
);
},
});
</script>

24
src/components/ContextMenu/src/ContextMenu.vue

@ -1,6 +1,6 @@
<script lang="tsx">
import type { ContextMenuItem, ItemContentProps, Axis } from './typing';
import type { FunctionalComponent, CSSProperties, PropType } from 'vue';
import type { FunctionalComponent, CSSProperties } from 'vue';
import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted } from 'vue';
import Icon from '/@/components/Icon';
import { Menu, Divider } from 'ant-design-vue';
@ -60,11 +60,9 @@
const top = body.clientHeight < y + menuHeight ? y - menuHeight : y;
return {
...styles,
position: 'absolute',
width: `${width}px`,
left: `${left + 1}px`,
top: `${top + 1}px`,
zIndex: 9999,
};
});
@ -89,8 +87,7 @@
}
function renderMenuItem(items: ContextMenuItem[]) {
const visibleItems = items.filter((item) => !item.hidden);
return visibleItems.map((item) => {
return items.map((item) => {
const { disabled, label, children, divider = false } = item;
const contentProps = {
@ -127,11 +124,15 @@
}
const { items } = props;
return (
<div class={prefixCls}>
<Menu inlineIndent={12} mode="vertical" ref={wrapRef} style={unref(getStyle)}>
{renderMenuItem(items)}
</Menu>
</div>
<Menu
inlineIndent={12}
mode="vertical"
class={prefixCls}
ref={wrapRef}
style={unref(getStyle)}
>
{renderMenuItem(items)}
</Menu>
);
};
},
@ -184,9 +185,6 @@
background-clip: padding-box;
user-select: none;
&__item {
margin: 0 !important;
}
.item-style();
.ant-divider {

1
src/components/ContextMenu/src/typing.ts

@ -6,7 +6,6 @@ export interface Axis {
export interface ContextMenuItem {
label: string;
icon?: string;
hidden?: boolean;
disabled?: boolean;
handler?: Fn;
divider?: boolean;

5
src/components/Cropper/src/CopperModal.vue

@ -129,7 +129,6 @@
uploadApi: {
type: Function as PropType<(params: apiFunParams) => Promise<any>>,
},
src: { type: String },
};
export default defineComponent({
@ -139,7 +138,7 @@
emits: ['uploadSuccess', 'register'],
setup(props, { emit }) {
let filename = '';
const src = ref(props.src || '');
const src = ref('');
const previewSource = ref('');
const cropper = ref<Cropper>();
let scaleX = 1;
@ -187,7 +186,7 @@
try {
setModalProps({ confirmLoading: true });
const result = await uploadApi({ name: 'file', file: blob, filename });
emit('uploadSuccess', { source: previewSource.value, data: result.url });
emit('uploadSuccess', { source: previewSource.value, data: result.data });
closeModal();
} finally {
setModalProps({ confirmLoading: false });

6
src/components/Cropper/src/CropperAvatar.vue

@ -22,7 +22,7 @@
<CopperModal
@register="register"
@upload-success="handleUploadSuccess"
@uploadSuccess="handleUploadSuccess"
:uploadApi="uploadApi"
:src="sourceValue"
/>
@ -91,9 +91,9 @@
},
);
function handleUploadSuccess({ source, data }) {
function handleUploadSuccess({ source }) {
sourceValue.value = source;
emit('change', { source, data });
emit('change', source);
createMessage.success(t('component.cropper.uploadSuccess'));
}

5
src/components/Description/src/Description.vue

@ -3,7 +3,7 @@
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions/index';
import type { CSSProperties } from 'vue';
import type { CollapseContainerOptions } from '/@/components/Container/index';
import { defineComponent, computed, ref, unref, toRefs } from 'vue';
import { defineComponent, computed, ref, unref } from 'vue';
import { get } from 'lodash-es';
import { Descriptions } from 'ant-design-vue';
import { CollapseContainer } from '/@/components/Container/index';
@ -121,9 +121,6 @@
return null;
}
const getField = get(_data, field);
if (getField && !toRefs(_data).hasOwnProperty(field)) {
return isFunction(render) ? render('', _data) : '';
}
return isFunction(render) ? render(getField, _data) : getField ?? '';
};

2
src/components/Drawer/src/BasicDrawer.vue

@ -94,7 +94,7 @@
opt.width = '100%';
}
const detailCls = `${prefixCls}__detail`;
opt.class = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
opt.wrapClassName = wrapClassName ? `${wrapClassName} ${detailCls}` : detailCls;
if (!getContainer) {
// TODO type error?

2
src/components/Drawer/src/components/DrawerFooter.vue

@ -47,7 +47,7 @@
const heightStr = `${props.height}`;
return {
height: heightStr,
lineHeight: `calc(${heightStr} - 1px)`,
lineHeight: heightStr,
};
});

7
src/components/Drawer/src/typing.ts

@ -1,6 +1,6 @@
import type { ButtonProps } from 'ant-design-vue/lib/button/buttonTypes';
import type { CSSProperties, VNodeChild, ComputedRef } from 'vue';
import type { ScrollContainerOptions } from '/@/components/Container/index';
import type { ScrollContainerOptions } from '/@/components/Container';
export interface DrawerInstance {
setDrawerProps: (props: Partial<DrawerProps> | boolean) => void;
@ -69,6 +69,7 @@ export interface DrawerFooterProps {
showFooter: boolean;
footerHeight: string | number;
}
export interface DrawerProps extends DrawerFooterProps {
isDetail?: boolean;
loading?: boolean;
@ -128,12 +129,13 @@ export interface DrawerProps extends DrawerFooterProps {
* @type any (string | slot)
*/
title?: VNodeChild | JSX.Element;
/**
* The class name of the container of the Drawer dialog.
* @type string
*/
wrapClassName?: string;
class?: string;
/**
* Style of wrapper element which **contains mask** compare to `drawerStyle`
* @type object
@ -186,6 +188,7 @@ export interface DrawerProps extends DrawerFooterProps {
*/
onClose?: (e?: Event) => void;
}
export interface DrawerActionType {
scrollBottom: () => void;
scrollTo: (to: number) => void;

2
src/components/Dropdown/src/Dropdown.vue

@ -57,7 +57,7 @@
* @type string[]
*/
trigger: {
type: Array as PropType<('contextmenu' | 'click' | 'hover')[]>,
type: [Array] as PropType<('contextmenu' | 'click' | 'hover')[]>,
default: () => {
return ['contextmenu'];
},

26
src/components/Excel/src/Export2Excel.ts

@ -1,4 +1,4 @@
import * as xlsx from 'xlsx';
import xlsx from 'xlsx';
import type { WorkBook } from 'xlsx';
import type { JsonToSheet, AoAToSheet } from './typing';
@ -6,28 +6,6 @@ const { utils, writeFile } = xlsx;
const DEF_FILE_NAME = 'excel-list.xlsx';
/**
* @param data source data
* @param worksheet worksheet object
* @param min min width
*/
function setColumnWidth(data, worksheet, min = 3) {
const obj = {};
worksheet['!cols'] = [];
data.forEach((item) => {
Object.keys(item).forEach((key) => {
const cur = item[key];
const length = cur?.length ?? min;
obj[key] = Math.max(length, obj[key] ?? min);
});
});
Object.keys(obj).forEach((key) => {
worksheet['!cols'].push({
wch: obj[key],
});
});
}
export function jsonToSheetXlsx<T = any>({
data,
header,
@ -42,7 +20,7 @@ export function jsonToSheetXlsx<T = any>({
}
const worksheet = utils.json_to_sheet(arrData, json2sheetOpts);
setColumnWidth(arrData, worksheet);
/* add worksheet to workbook */
const workbook: WorkBook = {
SheetNames: [filename],

79
src/components/Excel/src/ImportExcel.vue

@ -14,7 +14,7 @@
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import * as XLSX from 'xlsx';
import XLSX from 'xlsx';
import { dateUtil } from '/@/utils/dateUtil';
import type { ExcelData } from './typing';
@ -31,51 +31,11 @@
type: Number,
default: 8,
},
//
isReturnFile: {
type: Boolean,
default: false,
},
},
emits: ['success', 'error', 'cancel'],
emits: ['success', 'error'],
setup(props, { emit }) {
const inputRef = ref<HTMLInputElement | null>(null);
const loadingRef = ref<Boolean>(false);
const cancelRef = ref<Boolean>(true);
function shapeWorkSheel(sheet: XLSX.WorkSheet, range: XLSX.Range) {
let str = ' ',
char = 65,
customWorkSheet = {
t: 's',
v: str,
r: '<t> </t><phoneticPr fontId="1" type="noConversion"/>',
h: str,
w: str,
};
if (!sheet || !sheet['!ref']) return [];
let c = 0,
r = 1;
while (c < range.e.c + 1) {
while (r < range.e.r + 1) {
if (!sheet[String.fromCharCode(char) + r]) {
sheet[String.fromCharCode(char) + r] = customWorkSheet;
}
r++;
}
r = 1;
str += ' ';
customWorkSheet = {
t: 's',
v: str,
r: '<t> </t><phoneticPr fontId="1" type="noConversion"/>',
h: str,
w: str,
};
c++;
char++;
}
}
/**
* @description: 第一行作为头部
@ -84,8 +44,8 @@
if (!sheet || !sheet['!ref']) return [];
const headers: string[] = [];
// A3:B7=>{s:{c:0, r:2}, e:{c:1, r:6}}
const range: XLSX.Range = XLSX.utils.decode_range(sheet['!ref']);
shapeWorkSheel(sheet, range);
const range = XLSX.utils.decode_range(sheet['!ref']);
const R = range.s.r;
/* start in the first row */
for (let C = range.s.c; C <= range.e.c; ++C) {
@ -177,45 +137,18 @@
* @description: 触发选择文件管理器
*/
function handleInputClick(e: Event) {
const target = e && (e.target as HTMLInputElement);
const files = target?.files;
const files = e && (e.target as HTMLInputElement).files;
const rawFile = files && files[0]; // only setting files[0]
target.value = '';
if (!rawFile) return;
cancelRef.value = false;
if (props.isReturnFile) {
emit('success', rawFile);
return;
}
upload(rawFile);
}
/**
* @description 文件选择器关闭后,判断取消状态
*/
function handleFocusChange() {
const timeId = setInterval(() => {
if (cancelRef.value === true) {
emit('cancel');
}
clearInterval(timeId);
window.removeEventListener('focus', handleFocusChange);
}, 1000);
}
/**
* @description: 点击上传按钮
*/
function handleUpload() {
const inputRefDom = unref(inputRef);
if (inputRefDom) {
cancelRef.value = true;
inputRefDom.click();
window.addEventListener('focus', handleFocusChange);
}
inputRefDom && inputRefDom.click();
}
return { handleUpload, handleInputClick, inputRef };

2
src/components/Form/index.ts

@ -9,9 +9,7 @@ export { useForm } from './src/hooks/useForm';
export { default as ApiSelect } from './src/components/ApiSelect.vue';
export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue';
export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue';
export { default as ApiTree } from './src/components/ApiTree.vue';
export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue';
export { default as ApiCascader } from './src/components/ApiCascader.vue';
export { default as ApiTransfer } from './src/components/ApiTransfer.vue';
export { BasicForm };

39
src/components/Form/src/BasicForm.vue

@ -10,7 +10,6 @@
<slot name="formHeader"></slot>
<template v-for="schema in getSchema" :key="schema.field">
<FormItem
:isAdvanced="fieldsIsAdvancedMap[schema.field]"
:tableAction="tableAction"
:formActionType="formActionType"
:schema="schema"
@ -59,18 +58,15 @@
import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus';
import { useModalContext } from '/@/components/Modal';
import { useDebounceFn } from '@vueuse/core';
import { basicProps } from './props';
import { useDesign } from '/@/hooks/web/useDesign';
import { cloneDeep } from 'lodash-es';
import { isFunction, isArray } from '/@/utils/is';
export default defineComponent({
name: 'BasicForm',
components: { FormItem, Form, Row, FormAction },
props: basicProps,
emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'],
emits: ['advanced-change', 'reset', 'submit', 'register'],
setup(props, { emit, attrs }) {
const formModel = reactive<Recordable>({});
const modalFn = useModalContext();
@ -120,13 +116,13 @@
const getSchema = computed((): FormSchema[] => {
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
for (const schema of schemas) {
const { defaultValue, component, isHandleDateDefaultValue = true } = schema;
const { defaultValue, component } = schema;
// handle date type
if (isHandleDateDefaultValue && defaultValue && dateItemType.includes(component)) {
if (defaultValue && dateItemType.includes(component)) {
if (!Array.isArray(defaultValue)) {
schema.defaultValue = dateUtil(defaultValue);
} else {
const def: any[] = [];
const def: moment.Moment[] = [];
defaultValue.forEach((item) => {
def.push(dateUtil(item));
});
@ -135,15 +131,13 @@
}
}
if (unref(getProps).showAdvancedButton) {
return cloneDeep(
schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[],
);
return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
} else {
return cloneDeep(schemas as FormSchema[]);
return schemas as FormSchema[];
}
});
const { handleToggleAdvanced, fieldsIsAdvancedMap } = useAdvanced({
const { handleToggleAdvanced } = useAdvanced({
advanceState,
emit,
getProps,
@ -176,7 +170,7 @@
updateSchema,
resetSchema,
appendSchemaByField,
removeSchemaByField,
removeSchemaByFiled,
resetFields,
scrollToField,
} = useFormEvents({
@ -231,28 +225,16 @@
},
);
watch(
() => formModel,
useDebounceFn(() => {
unref(getProps).submitOnChange && handleSubmit();
}, 300),
{ deep: true },
);
async function setProps(formProps: Partial<FormProps>): Promise<void> {
propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
}
function setFormModel(key: string, value: any, schema: FormSchema) {
function setFormModel(key: string, value: any) {
formModel[key] = value;
const { validateTrigger } = unref(getBindValue);
if (isFunction(schema.dynamicRules) || isArray(schema.rules)) {
return;
}
if (!validateTrigger || validateTrigger === 'change') {
validateFields([key]).catch((_) => {});
}
emit('field-value-change', key, value);
}
function handleEnterPress(e: KeyboardEvent) {
@ -273,7 +255,7 @@
updateSchema,
resetSchema,
setProps,
removeSchemaByField,
removeSchemaByFiled,
appendSchemaByField,
clearValidate,
validateFields,
@ -304,7 +286,6 @@
getFormActionBindProps: computed(
(): Recordable => ({ ...getProps.value, ...advanceState }),
),
fieldsIsAdvancedMap,
...formActionType,
};
},

4
src/components/Form/src/componentMap.ts

@ -24,10 +24,8 @@ import {
import ApiRadioGroup from './components/ApiRadioGroup.vue';
import RadioButtonGroup from './components/RadioButtonGroup.vue';
import ApiSelect from './components/ApiSelect.vue';
import ApiTree from './components/ApiTree.vue';
import ApiTreeSelect from './components/ApiTreeSelect.vue';
import ApiCascader from './components/ApiCascader.vue';
import ApiTransfer from './components/ApiTransfer.vue';
import { BasicUpload } from '/@/components/Upload';
import { StrengthMeter } from '/@/components/StrengthMeter';
import { IconPicker } from '/@/components/Icon';
@ -45,7 +43,6 @@ componentMap.set('AutoComplete', AutoComplete);
componentMap.set('Select', Select);
componentMap.set('ApiSelect', ApiSelect);
componentMap.set('ApiTree', ApiTree);
componentMap.set('TreeSelect', TreeSelect);
componentMap.set('ApiTreeSelect', ApiTreeSelect);
componentMap.set('ApiRadioGroup', ApiRadioGroup);
@ -58,7 +55,6 @@ componentMap.set('ApiCascader', ApiCascader);
componentMap.set('Cascader', Cascader);
componentMap.set('Slider', Slider);
componentMap.set('Rate', Rate);
componentMap.set('ApiTransfer', ApiTransfer);
componentMap.set('DatePicker', DatePicker);
componentMap.set('MonthPicker', DatePicker.MonthPicker);

7
src/components/Form/src/components/ApiCascader.vue

@ -26,7 +26,7 @@
import { get, omit } from 'lodash-es';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
interface Option {
value: string;
label: string;
@ -76,7 +76,7 @@
const loading = ref<boolean>(false);
const emitData = ref<any[]>([]);
const isFirstLoad = ref(true);
const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props, 'value', 'change', emitData);
@ -170,7 +170,7 @@
);
function handleChange(keys, args) {
emitData.value = args;
emitData.value = keys;
emit('defaultChange', keys, args);
}
@ -188,7 +188,6 @@
state,
options,
loading,
t,
handleChange,
loadData,
handleRenderDisplay,

17
src/components/Form/src/components/ApiRadioGroup.vue

@ -2,17 +2,12 @@
* @Description:It is troublesome to implement radio button group in the form. So it is extracted independently as a separate component
-->
<template>
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid">
<RadioGroup v-bind="attrs" v-model:value="state" button-style="solid" @change="handleChange">
<template v-for="item in getOptions" :key="`${item.value}`">
<RadioButton
v-if="props.isBtn"
:value="item.value"
:disabled="item.disabled"
@click="handleClick(item)"
>
<RadioButton v-if="props.isBtn" :value="item.value" :disabled="item.disabled">
{{ item.label }}
</RadioButton>
<Radio v-else :value="item.value" :disabled="item.disabled" @click="handleClick(item)">
<Radio v-else :value="item.value" :disabled="item.disabled">
{{ item.label }}
</Radio>
</template>
@ -67,7 +62,7 @@
const attrs = useAttrs();
const { t } = useI18n();
// Embedded in the form, just use the hook binding to perform form verification
const [state] = useRuleFormItem(props, 'value', 'change', emitData);
const [state] = useRuleFormItem(props);
// Processing options value
const getOptions = computed(() => {
@ -125,11 +120,11 @@
emit('options-change', unref(getOptions));
}
function handleClick(...args) {
function handleChange(_, ...args) {
emitData.value = args;
}
return { state, getOptions, attrs, loading, t, handleClick, props };
return { state, getOptions, attrs, loading, t, handleChange, props };
},
});
</script>

31
src/components/Form/src/components/ApiSelect.vue

@ -1,6 +1,6 @@
<template>
<Select
@dropdown-visible-change="handleFetch"
@dropdownVisibleChange="handleFetch"
v-bind="$attrs"
@change="handleChange"
:options="getOptions"
@ -48,15 +48,17 @@
default: null,
},
// api params
params: propTypes.any.def({}),
params: {
type: Object as PropType<Recordable>,
default: () => ({}),
},
// support xxx.xxx.xx
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('label'),
valueField: propTypes.string.def('value'),
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
},
emits: ['options-change', 'change', 'update:value'],
emits: ['options-change', 'change'],
setup(props, { emit }) {
const options = ref<OptionsItem[]>([]);
const loading = ref(false);
@ -85,16 +87,9 @@
});
watchEffect(() => {
props.immediate && !props.alwaysLoad && fetch();
props.immediate && fetch();
});
watch(
() => state.value,
(v) => {
emit('update:value', v);
},
);
watch(
() => props.params,
() => {
@ -126,14 +121,10 @@
}
}
async function handleFetch(visible) {
if (visible) {
if (props.alwaysLoad) {
await fetch();
} else if (!props.immediate && unref(isFirstLoad)) {
await fetch();
isFirstLoad.value = false;
}
async function handleFetch() {
if (!props.immediate && unref(isFirstLoad)) {
await fetch();
isFirstLoad.value = false;
}
}

134
src/components/Form/src/components/ApiTransfer.vue

@ -1,134 +0,0 @@
<template>
<Transfer
:data-source="getdataSource"
:filter-option="filterOption"
:render="(item) => item.title"
:showSelectAll="showSelectAll"
:selectedKeys="selectedKeys"
:targetKeys="getTargetKeys"
:showSearch="showSearch"
@change="handleChange"
/>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, unref, watchEffect } from 'vue';
import { Transfer } from 'ant-design-vue';
import { isFunction } from '/@/utils/is';
import { get, omit } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { useI18n } from '/@/hooks/web/useI18n';
import { TransferDirection, TransferItem } from 'ant-design-vue/lib/transfer';
export default defineComponent({
name: 'ApiTransfer',
components: { Transfer },
props: {
value: { type: Array as PropType<Array<string>> },
api: {
type: Function as PropType<(arg?: Recordable) => Promise<TransferItem[]>>,
default: null,
},
params: { type: Object },
dataSource: { type: Array as PropType<Array<TransferItem>> },
immediate: propTypes.bool.def(true),
alwaysLoad: propTypes.bool.def(false),
afterFetch: { type: Function as PropType<Fn> },
resultField: propTypes.string.def(''),
labelField: propTypes.string.def('title'),
valueField: propTypes.string.def('key'),
showSearch: { type: Boolean, default: false },
disabled: { type: Boolean, default: false },
filterOption: {
type: Function as PropType<(inputValue: string, item: TransferItem) => boolean>,
},
selectedKeys: { type: Array as PropType<Array<string>> },
showSelectAll: { type: Boolean, default: false },
targetKeys: { type: Array as PropType<Array<string>> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const _dataSource = ref<TransferItem[]>([]);
const _targetKeys = ref<string[]>([]);
const { t } = useI18n();
const getAttrs = computed(() => {
return {
...(!props.api ? { dataSource: unref(_dataSource) } : {}),
...attrs,
};
});
const getdataSource = computed(() => {
const { labelField, valueField } = props;
return unref(_dataSource).reduce((prev, next: Recordable) => {
if (next) {
prev.push({
...omit(next, [labelField, valueField]),
title: next[labelField],
key: next[valueField],
});
}
return prev;
}, [] as TransferItem[]);
});
const getTargetKeys = computed<string[]>(() => {
if (unref(_targetKeys).length > 0) {
return unref(_targetKeys);
}
if (Array.isArray(props.value)) {
return props.value;
}
return [];
});
function handleChange(keys: string[], direction: TransferDirection, moveKeys: string[]) {
_targetKeys.value = keys;
console.log(direction);
console.log(moveKeys);
emit('change', keys);
}
watchEffect(() => {
props.immediate && !props.alwaysLoad && fetch();
});
watch(
() => props.params,
() => {
fetch();
},
{ deep: true },
);
async function fetch() {
const api = props.api;
if (!api || !isFunction(api)) {
if (Array.isArray(props.dataSource)) {
_dataSource.value = props.dataSource;
}
return;
}
_dataSource.value = [];
try {
const res = await api(props.params);
if (Array.isArray(res)) {
_dataSource.value = res;
emitChange();
return;
}
if (props.resultField) {
_dataSource.value = get(res, props.resultField) || [];
}
emitChange();
} catch (error) {
console.warn(error);
} finally {
}
}
function emitChange() {
emit('options-change', unref(getdataSource));
}
return { getTargetKeys, getdataSource, t, getAttrs, handleChange };
},
});
</script>

90
src/components/Form/src/components/ApiTree.vue

@ -1,90 +0,0 @@
<template>
<a-tree v-bind="getAttrs" @change="handleChange">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template #suffixIcon v-if="loading">
<LoadingOutlined spin />
</template>
</a-tree>
</template>
<script lang="ts">
import { computed, defineComponent, watch, ref, onMounted, unref } from 'vue';
import { Tree } from 'ant-design-vue';
import { isArray, isFunction } from '/@/utils/is';
import { get } from 'lodash-es';
import { propTypes } from '/@/utils/propTypes';
import { LoadingOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'ApiTree',
components: { ATree: Tree, LoadingOutlined },
props: {
api: { type: Function as PropType<(arg?: Recordable) => Promise<Recordable>> },
params: { type: Object },
immediate: { type: Boolean, default: true },
resultField: propTypes.string.def(''),
afterFetch: { type: Function as PropType<Fn> },
},
emits: ['options-change', 'change'],
setup(props, { attrs, emit }) {
const treeData = ref<Recordable[]>([]);
const isFirstLoaded = ref<Boolean>(false);
const loading = ref(false);
const getAttrs = computed(() => {
return {
...(props.api ? { treeData: unref(treeData) } : {}),
...attrs,
};
});
function handleChange(...args) {
emit('change', ...args);
}
watch(
() => props.params,
() => {
!unref(isFirstLoaded) && fetch();
},
{ deep: true },
);
watch(
() => props.immediate,
(v) => {
v && !isFirstLoaded.value && fetch();
},
);
onMounted(() => {
props.immediate && fetch();
});
async function fetch() {
const { api, afterFetch } = props;
if (!api || !isFunction(api)) return;
loading.value = true;
treeData.value = [];
let result;
try {
result = await api(props.params);
} catch (e) {
console.error(e);
}
if (afterFetch && isFunction(afterFetch)) {
result = afterFetch(result);
}
loading.value = false;
if (!result) return;
if (!isArray(result)) {
result = get(result, props.resultField);
}
treeData.value = (result as Recordable[]) || [];
isFirstLoaded.value = true;
emit('options-change', treeData.value);
}
return { getAttrs, loading, handleChange };
},
});
</script>

49
src/components/Form/src/components/FormItem.vue

@ -1,16 +1,17 @@
<script lang="tsx">
import type { PropType, Ref } from 'vue';
import { computed, defineComponent, toRefs, unref } from 'vue';
import type { FormActionType, FormProps, FormSchema } from '../types/form';
import type { FormActionType, FormProps } from '../types/form';
import type { FormSchema } from '../types/form';
import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
import type { TableActionType } from '/@/components/Table';
import { Col, Divider, Form } from 'ant-design-vue';
import { defineComponent, computed, unref, toRefs } from 'vue';
import { Form, Col, Divider } from 'ant-design-vue';
import { componentMap } from '../componentMap';
import { BasicHelp } from '/@/components/Basic';
import { isBoolean, isFunction, isNull } from '/@/utils/is';
import { getSlot } from '/@/utils/helper/tsxHelper';
import { createPlaceholderMessage, setComponentRuleType } from '../helper';
import { cloneDeep, upperFirst } from 'lodash-es';
import { upperFirst, cloneDeep } from 'lodash-es';
import { useItemLabelWidth } from '../hooks/useLabelWidth';
import { useI18n } from '/@/hooks/web/useI18n';
@ -35,7 +36,7 @@
default: () => ({}),
},
setFormModel: {
type: Function as PropType<(key: string, value: any, schema: FormSchema) => void>,
type: Function as PropType<(key: string, value: any) => void>,
default: null,
},
tableAction: {
@ -44,9 +45,6 @@
formActionType: {
type: Object as PropType<FormActionType>,
},
isAdvanced: {
type: Boolean,
},
},
setup(props, { slots }) {
const { t } = useI18n();
@ -80,14 +78,10 @@
componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
}
if (schema.component === 'Divider') {
componentProps = Object.assign(
{ type: 'horizontal' },
{
orientation: 'left',
plain: true,
},
componentProps,
);
componentProps = Object.assign({ type: 'horizontal' }, componentProps, {
orientation: 'left',
plain: true,
});
}
return componentProps as Recordable;
});
@ -110,8 +104,8 @@
const { show, ifShow } = props.schema;
const { showAdvancedButton } = props.formProps;
const itemIsAdvanced = showAdvancedButton
? isBoolean(props.isAdvanced)
? props.isAdvanced
? isBoolean(props.schema.isAdvanced)
? props.schema.isAdvanced
: true
: true;
@ -184,21 +178,8 @@
const getRequired = isFunction(required) ? required(unref(getValues)) : required;
/*
* 1若设置了required属性又没有其他的rules就创建一个验证规则
* 2若设置了required属性又存在其他的rules则只rules中不存在required属性时才添加验证required的规则
* 也就是说rules中的required优先级大于required
*/
if (getRequired) {
if (!rules || rules.length === 0) {
rules = [{ required: getRequired, validator }];
} else {
const requiredIndex: number = rules.findIndex((rule) => Reflect.has(rule, 'required'));
if (requiredIndex === -1) {
rules.push({ required: getRequired, validator });
}
}
if ((!rules || rules.length === 0) && getRequired) {
rules = [{ required: getRequired, validator }];
}
const requiredRuleIndex: number = rules.findIndex(
@ -257,7 +238,7 @@
}
const target = e ? e.target : null;
const value = target ? (isCheck ? target.checked : target.value) : e;
props.setFormModel(field, value, props.schema);
props.setFormModel(field, value);
},
};
const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save