mirror of
http://git.xinwangdao.com/cnnc-embedded-parts-detect/detect-embeded.git
synced 2025-06-23 21:14:13 +08:00
Initial commit
This commit is contained in:
commit
0b1166c718
4
.browserslistrc
Normal file
4
.browserslistrc
Normal file
@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
107
.commitlintrc.js
Normal file
107
.commitlintrc.js
Normal file
@ -0,0 +1,107 @@
|
||||
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'],
|
||||
rules: {
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0],
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'style',
|
||||
'docs',
|
||||
'test',
|
||||
'refactor',
|
||||
'build',
|
||||
'ci',
|
||||
'chore',
|
||||
'revert',
|
||||
'wip',
|
||||
'workflow',
|
||||
'types',
|
||||
'release',
|
||||
],
|
||||
],
|
||||
},
|
||||
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: 自定义',
|
||||
},
|
||||
};
|
3
.dockerignore
Normal file
3
.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.vscode/
|
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@ -0,0 +1,19 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
max_line_length = 100
|
||||
|
||||
[*.{yml,yaml,json}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
23
.env.analyze
Normal file
23
.env.analyze
Normal file
@ -0,0 +1,23 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK = true
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Whether to enable gzip or brotli compression
|
||||
# Optional: gzip | brotli | none
|
||||
# If you need multiple forms, you can use `,` to separate
|
||||
VITE_BUILD_COMPRESS = 'none'
|
||||
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/basic-api
|
||||
|
||||
# File upload address, optional
|
||||
# It can be forwarded by nginx or write the actual address directly
|
||||
VITE_GLOB_UPLOAD_URL=/upload
|
||||
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
||||
|
||||
VITE_ENABLE_ANALYZE = true
|
14
.env.development
Normal file
14
.env.development
Normal file
@ -0,0 +1,14 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK=false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH=/
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=http://localhost:3920
|
||||
|
||||
# File upload address, optional
|
||||
VITE_GLOB_UPLOAD_URL=http://localhost:3910/v1/system/upload
|
||||
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
22
.env.docker
Normal file
22
.env.docker
Normal file
@ -0,0 +1,22 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK = false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# timeout(seconds)
|
||||
VITE_TIMEOUT = 15
|
||||
# Delete console
|
||||
VITE_DROP_CONSOLE = true
|
||||
|
||||
# Whether to enable gzip or brotli compression
|
||||
# Optional: gzip | brotli | none
|
||||
# If you need multiple forms, you can use `,` to separate
|
||||
VITE_BUILD_COMPRESS = 'none'
|
||||
VITE_GLOB_API_URL="__vg_base_url"
|
||||
|
||||
# File upload address, optional
|
||||
# It can be forwarded by nginx or write the actual address directly
|
||||
VITE_GLOB_UPLOAD_URL=/files/upload
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
20
.env.production
Normal file
20
.env.production
Normal file
@ -0,0 +1,20 @@
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK=false
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH=./
|
||||
|
||||
# Whether to enable gzip or brotli compression
|
||||
# Optional: gzip | brotli | none
|
||||
# If you need multiple forms, you can use `,` to separate
|
||||
VITE_BUILD_COMPRESS='none'
|
||||
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=http://localhost:3910
|
||||
|
||||
# File upload address, optional
|
||||
VITE_GLOB_UPLOAD_URL=http://localhost:3910/v1/system/upload
|
||||
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
21
.env.test
Normal file
21
.env.test
Normal file
@ -0,0 +1,21 @@
|
||||
NODE_ENV=production
|
||||
# Whether to open mock
|
||||
VITE_USE_MOCK = true
|
||||
|
||||
# public path
|
||||
VITE_PUBLIC_PATH = /
|
||||
|
||||
# Whether to enable gzip or brotli compression
|
||||
# Optional: gzip | brotli | none
|
||||
# If you need multiple forms, you can use `,` to separate
|
||||
VITE_BUILD_COMPRESS = 'none'
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/basic-api
|
||||
|
||||
# File upload address, optional
|
||||
# It can be forwarded by nginx or write the actual address directly
|
||||
VITE_GLOB_UPLOAD_URL=/upload
|
||||
|
||||
# Interface prefix
|
||||
VITE_GLOB_API_URL_PREFIX=
|
15
.eslintignore
Normal file
15
.eslintignore
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
Dockerfile
|
7
.eslintrc.js
Normal file
7
.eslintrc.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben'],
|
||||
rules: {
|
||||
"unused-imports/no-unused-imports": "off"
|
||||
}
|
||||
};
|
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
|
||||
|
||||
# Automatically normalize line endings (to LF) for all text-based files.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
|
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
.cache
|
||||
.turbo
|
||||
|
||||
tests/server/static
|
||||
tests/server/static/upload
|
||||
|
||||
.local
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
|
||||
.history
|
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@ -0,0 +1,6 @@
|
||||
ports:
|
||||
- port: 3344
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- init: pnpm install
|
||||
command: pnpm run dev
|
7
.npmrc
Normal file
7
.npmrc
Normal file
@ -0,0 +1,7 @@
|
||||
public-hoist-pattern[]=husky
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=*prettier*
|
||||
public-hoist-pattern[]=lint-staged
|
||||
public-hoist-pattern[]=*stylelint*
|
||||
public-hoist-pattern[]=@commitlint/cli
|
||||
public-hoist-pattern[]=@vben/eslint-config
|
12
.prettierignore
Normal file
12
.prettierignore
Normal file
@ -0,0 +1,12 @@
|
||||
dist
|
||||
.local
|
||||
.output.js
|
||||
node_modules
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
public
|
||||
.npmrc
|
||||
|
||||
*-lock.yaml
|
19
.prettierrc.js
Normal file
19
.prettierrc.js
Normal file
@ -0,0 +1,19 @@
|
||||
module.exports = {
|
||||
printWidth: 100,
|
||||
semi: true,
|
||||
vueIndentScriptAndStyle: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
proseWrap: 'never',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
endOfLine: 'auto',
|
||||
plugins: ['prettier-plugin-packagejson'],
|
||||
overrides: [
|
||||
{
|
||||
files: '.*rc',
|
||||
options: {
|
||||
parser: 'json',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
2
.stylelintignore
Normal file
2
.stylelintignore
Normal file
@ -0,0 +1,2 @@
|
||||
dist
|
||||
public
|
4
.stylelintrc.js
Normal file
4
.stylelintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben/stylelint-config'],
|
||||
};
|
1262
CHANGELOG.en_US.md
Normal file
1262
CHANGELOG.en_US.md
Normal file
File diff suppressed because it is too large
Load Diff
1586
CHANGELOG.md
Normal file
1586
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
1317
CHANGELOG.zh_CN.md
Normal file
1317
CHANGELOG.zh_CN.md
Normal file
File diff suppressed because it is too large
Load Diff
26
Dockerfile
Normal file
26
Dockerfile
Normal file
@ -0,0 +1,26 @@
|
||||
# node 构建
|
||||
FROM node:16-alpine as build-stage
|
||||
# 署名
|
||||
MAINTAINER Adoin 'adoin@qq.com'
|
||||
WORKDIR /app
|
||||
COPY . ./
|
||||
# 设置 node 阿里镜像
|
||||
RUN npm config set registry https://registry.npm.taobao.org
|
||||
# 设置--max-old-space-size
|
||||
ENV NODE_OPTIONS=--max-old-space-size=16384
|
||||
# 设置阿里镜像、pnpm、依赖、编译
|
||||
RUN npm install pnpm -g && \
|
||||
pnpm install --frozen-lockfile && \
|
||||
pnpm build:docker
|
||||
# node部分结束
|
||||
RUN echo "🎉 编 🎉 译 🎉 成 🎉 功 🎉"
|
||||
# nginx 部署
|
||||
FROM nginx:1.23.3-alpine as production-stage
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html/dist
|
||||
COPY --from=build-stage /app/nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
## 将/usr/share/nginx/html/dist/assets/index.js 和/usr/share/nginx/html/dist/_app.config.js中的"$vg_base_url"替换为环境变量中的VG_BASE_URL,$vg_sub_domain 替换成VG_SUB_DOMAIN,$vg_default_user替换成VG_DEFAULT_USER,$vg_default_password替换成VG_DEFAULT_PASSWORD 而后启动nginx
|
||||
CMD sed -i "s|__vg_base_url|$VG_BASE_URL|g" /usr/share/nginx/html/dist/assets/index.js && \
|
||||
sed -i "s|__vg_base_url|$VG_BASE_URL|g" /usr/share/nginx/html/dist/_app.config.js && \
|
||||
nginx -g 'daemon off;'
|
||||
RUN echo "🎉 架 🎉 设 🎉 成 🎉 功 🎉"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
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.
|
184
README.md
Normal file
184
README.md
Normal file
@ -0,0 +1,184 @@
|
||||
<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)
|
||||
|
||||
<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`, `vite4`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||
|
||||
## Feature
|
||||
|
||||
- **State of The Art Development**:Use front-end front-end technology development such as Vue3/vite2
|
||||
- **TypeScript**: Application-level JavaScript language
|
||||
- **Theming**: Configurable themes
|
||||
- **International**:Built-in complete internationalization program
|
||||
- **Mock Server** Built-in mock data scheme
|
||||
- **Authority** Built-in complete dynamic routing permission generation scheme.
|
||||
- **Component** Multiple commonly used components are encapsulated twice
|
||||
|
||||
## Preview
|
||||
|
||||
- [vue-vben-admin](https://vben.vvbin.cn/) - Full version Chinese site
|
||||
- [vue-vben-admin-gh-pages](https://anncwb.github.io/vue-vben-admin/) - Full version of the github site
|
||||
- [vben-admin-thin-next](https://vben.vvbin.cn/thin/next/) - Simplified Chinese site
|
||||
- [vben-admin-thin-gh-pages](https://anncwb.github.io/vben-admin-thin-next/) -Simplified github site
|
||||
|
||||
Test account: vben/123456
|
||||
|
||||
<p align="center">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||
</p>
|
||||
|
||||
### Use Gitpod
|
||||
|
||||
Open the project in Gitpod (free online dev environment for GitHub) and start coding immediately.
|
||||
|
||||
[](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://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
|
||||
```
|
||||
|
||||
- docker
|
||||
|
||||
### The dockerFile is located in the project root directory and supports differential deployment
|
||||
|
||||
#### build image
|
||||
|
||||
```bash
|
||||
docker build -t vue-vben-admin .
|
||||
```
|
||||
|
||||
#### Environment variables are dynamically used to achieve differentiated container deployment. Different VG_BASE_URL environment variables point to different back-end service addresses. In the following example, http://localhost:3333 is used as the back-end service address and the container is mapped to port 6666
|
||||
|
||||
```bash
|
||||
docker run --name vue-vben-admin -d -p 6666:80 -e VG_BASE_URL=http://localhost:3333 vue-vben-admin
|
||||
```
|
||||
|
||||
Then you can navigate http://localhost:6666
|
||||
|
||||
## 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 Or submit a Pull Request。
|
||||
|
||||
**Pull Request:**
|
||||
|
||||
1. Fork code!
|
||||
2. Create your own branch: `git checkout -b feat/xxxx`
|
||||
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. Push your branch: `git push origin feat/xxxx`
|
||||
5. submit`pull request`
|
||||
|
||||
## Git Contribution submission specification
|
||||
|
||||
- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||
|
||||
- `feat` Add new features
|
||||
- `fix` Fix the problem/BUG
|
||||
- `style` The code style is related and does not affect the running result
|
||||
- `perf` Optimization/performance improvement
|
||||
- `refactor` Refactor
|
||||
- `revert` Undo edit
|
||||
- `test` Test related
|
||||
- `docs` Documentation/notes
|
||||
- `chore` Dependency update/scaffolding configuration modification etc.
|
||||
- `workflow` Workflow improvements
|
||||
- `ci` Continuous integration
|
||||
- `types` Type definition file changes
|
||||
- `wip` In development
|
||||
|
||||
## Related warehouse
|
||||
|
||||
If these plugins are helpful to you, you can give a star support
|
||||
|
||||
- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) - Used for local and development environment data mock
|
||||
- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) - Used for html template conversion and compression
|
||||
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - Used to pack input .gz|.brotil files
|
||||
- [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!
|
||||
|
||||

|
||||
|
||||
<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)
|
190
README.zh-CN.md
Normal file
190
README.zh-CN.md
Normal file
@ -0,0 +1,190 @@
|
||||
<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)
|
||||
|
||||
<h1>Vue vben admin</h1>
|
||||
</div>
|
||||
|
||||
**中文** | [English](./README.md)
|
||||
|
||||
## 简介
|
||||
|
||||
Vue Vben Admin 是一个免费开源的中后台模版。使用了最新的`vue3`,`vite4`,`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 的免费在线开发环境)中打开项目,并立即开始编码.
|
||||
|
||||
[](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://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
|
||||
```
|
||||
|
||||
- docker
|
||||
|
||||
### dockerFile 位于项目根目录下 并且支持差异化部署
|
||||
|
||||
#### 构建镜像
|
||||
|
||||
```bash
|
||||
docker build -t vue-vben-admin .
|
||||
```
|
||||
|
||||
#### 动态使用环境变量实现容器差异化部署,通过不同的 VG_BASE_URL 环境变量,指向不同的后端服务地址,下面例子使用 http://localhost:3333 作为后端服务地址,并且将容器映射到 6666 端口
|
||||
|
||||
```bash
|
||||
docker run --name vue-vben-admin -d -p 6666:80 -e VG_BASE_URL=http://localhost:3333 vue-vben-admin
|
||||
```
|
||||
|
||||
而后可以打开 http://localhost:6666 访问
|
||||
|
||||
## 更新日志
|
||||
|
||||
[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) - 简化版
|
||||
|
||||
## 如何贡献
|
||||
|
||||
非常欢迎你的加入 或者提交一个 Pull Request。
|
||||
|
||||
**Pull Request:**
|
||||
|
||||
1. Fork 代码!
|
||||
2. 创建自己的分支: `git checkout -b feat/xxxx`
|
||||
3. 提交你的修改: `git commit -am 'feat(function): add xxxxx'`
|
||||
4. 推送您的分支: `git push origin feat/xxxx`
|
||||
5. 提交`pull request`
|
||||
|
||||
## Git 贡献提交规范
|
||||
|
||||
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||
|
||||
- `feat` 增加新功能
|
||||
- `fix` 修复问题/BUG
|
||||
- `style` 代码风格相关无影响运行结果的
|
||||
- `perf` 优化/性能提升
|
||||
- `refactor` 重构
|
||||
- `revert` 撤销修改
|
||||
- `test` 测试相关
|
||||
- `docs` 文档/注释
|
||||
- `chore` 依赖更新/脚手架配置修改等
|
||||
- `workflow` 工作流改进
|
||||
- `ci` 持续集成
|
||||
- `types` 类型定义文件更改
|
||||
- `wip` 开发中
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
本地开发推荐使用`Chrome 80+` 浏览器
|
||||
|
||||
支持现代浏览器, 不支持 IE
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 相关仓库
|
||||
|
||||
如果这些插件对你有帮助,可以给一个 star 支持下
|
||||
|
||||
- [vite-plugin-mock](https://github.com/anncwb/vite-plugin-mock) - 用于本地及开发环境数据 mock
|
||||
- [vite-plugin-html](https://github.com/anncwb/vite-plugin-html) - 用于 html 模版转换及压缩
|
||||
- [vite-plugin-compression](https://github.com/anncwb/vite-plugin-compression) - 用于打包输出.gz|.brotil 文件
|
||||
- [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)
|
||||
|
||||
## 捐赠
|
||||
|
||||
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
||||
|
||||

|
||||
|
||||
<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)
|
1
_app.config.js
Normal file
1
_app.config.js
Normal file
@ -0,0 +1 @@
|
||||
window.__PRODUCTION__667A80FD89C6989176D163A75E7353F0__CONF__={"VITE_GLOB_API_URL":"http://146.56.195.129:9088/api","VITE_GLOB_UPLOAD_URL":"http://146.56.195.129:9088/api/v1/system/upload","VITE_GLOB_API_URL_PREFIX":"","VITE_GLOB_APP_TITLE":"智能视频监控平台"};Object.freeze(window.__PRODUCTION__667A80FD89C6989176D163A75E7353F0__CONF__);Object.defineProperty(window, "__PRODUCTION__667A80FD89C6989176D163A75E7353F0__CONF__", {configurable: false,writable: false,});
|
0
apps/portal-view/.gitkeep
Normal file
0
apps/portal-view/.gitkeep
Normal file
15
apps/test-server/README.md
Normal file
15
apps/test-server/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Test Server
|
||||
|
||||
It is used to start the test interface service, which can test the upload, websocket, login and other interfaces.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
|
||||
cd ./test/server
|
||||
|
||||
pnpm install
|
||||
|
||||
pnpm run start
|
||||
|
||||
```
|
18
apps/test-server/controller/FileController.ts
Normal file
18
apps/test-server/controller/FileController.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import FileService from '../service/FileService';
|
||||
|
||||
class FileController {
|
||||
private service: FileService = new FileService();
|
||||
|
||||
upload = async (ctx) => {
|
||||
const files = ctx.request.files.file;
|
||||
console.log(files);
|
||||
|
||||
if (files.length === undefined) {
|
||||
this.service.upload(ctx, files, false);
|
||||
} else {
|
||||
this.service.upload(ctx, files, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default new FileController();
|
15
apps/test-server/controller/UserController.ts
Normal file
15
apps/test-server/controller/UserController.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import UserService from '../service/UserService';
|
||||
|
||||
class UserController {
|
||||
private service: UserService = new UserService();
|
||||
|
||||
login = async (ctx) => {
|
||||
ctx.body = await this.service.login();
|
||||
};
|
||||
|
||||
getUserInfoById = async (ctx) => {
|
||||
ctx.body = await this.service.getUserInfoById();
|
||||
};
|
||||
}
|
||||
|
||||
export default new UserController();
|
18
apps/test-server/ecosystem.config.js
Normal file
18
apps/test-server/ecosystem.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
const { name } = require('./package.json');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name,
|
||||
script: path.resolve(__dirname, './dist/index.js'),
|
||||
instances: require('os').cpus().length,
|
||||
autorestart: true,
|
||||
watch: true,
|
||||
env_production: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 8080,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
63
apps/test-server/index.ts
Normal file
63
apps/test-server/index.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import Koa from 'koa';
|
||||
import path from 'path';
|
||||
import Router from 'koa-router';
|
||||
import body from 'koa-body';
|
||||
import cors from 'koa2-cors';
|
||||
import koaStatic from 'koa-static';
|
||||
import websockify from 'koa-websocket';
|
||||
import route from 'koa-route';
|
||||
|
||||
import AppRoutes from './routes';
|
||||
|
||||
const PORT = 3300;
|
||||
|
||||
const app = websockify(new Koa());
|
||||
|
||||
app.ws.use(function (ctx, next) {
|
||||
ctx.websocket.send('connection succeeded!');
|
||||
return next(ctx);
|
||||
});
|
||||
|
||||
app.ws.use(
|
||||
route.all('/test', function (ctx) {
|
||||
// ctx.websocket.send('Hello World');
|
||||
ctx.websocket.on('message', function (message) {
|
||||
// do something with the message from client
|
||||
|
||||
if (message !== 'ping') {
|
||||
const data = JSON.stringify({
|
||||
id: Math.ceil(Math.random() * 1000),
|
||||
time: new Date().getTime(),
|
||||
res: `${message}`,
|
||||
});
|
||||
ctx.websocket.send(data);
|
||||
}
|
||||
console.log(message);
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const router = new Router();
|
||||
|
||||
// router
|
||||
AppRoutes.forEach((route) => router[route.method](route.path, route.action));
|
||||
|
||||
app.use(cors());
|
||||
app.use(
|
||||
body({
|
||||
encoding: 'gzip',
|
||||
multipart: true,
|
||||
formidable: {
|
||||
// uploadDir: path.join(__dirname, '/upload/'), // 设置文件上传目录
|
||||
keepExtensions: true,
|
||||
maxFieldsSize: 20 * 1024 * 1024,
|
||||
},
|
||||
}),
|
||||
);
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
app.use(koaStatic(path.join(__dirname)));
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Application started successfully: http://localhost:${PORT}`);
|
||||
});
|
8
apps/test-server/nodemon.json
Normal file
8
apps/test-server/nodemon.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"watch": ["src"],
|
||||
"ext": "ts",
|
||||
"exec": "ts-node -r tsconfig-paths/register index.ts",
|
||||
"events": {
|
||||
"restart": "clear"
|
||||
}
|
||||
}
|
36
apps/test-server/package.json
Normal file
36
apps/test-server/package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"compile": "rimraf ./dist && tsup ./index.ts --dts --format cjs,esm ",
|
||||
"prod": "npx pm2 start ecosystem.config.js --env production",
|
||||
"restart": "pm2 restart ecosystem.config.js --env production",
|
||||
"start": "nodemon",
|
||||
"stop": "npx pm2 stop ecosystem.config.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^11.1.1",
|
||||
"koa": "^2.14.2",
|
||||
"koa-body": "^6.0.1",
|
||||
"koa-bodyparser": "^4.4.1",
|
||||
"koa-route": "^3.2.0",
|
||||
"koa-router": "^12.0.0",
|
||||
"koa-static": "^5.0.0",
|
||||
"koa-websocket": "^7.0.0",
|
||||
"koa2-cors": "^2.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/koa": "^2.13.6",
|
||||
"@types/koa-bodyparser": "^5.0.2",
|
||||
"@types/koa-router": "^7.4.4",
|
||||
"@types/node": "^20.4.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"pm2": "^5.3.0",
|
||||
"rimraf": "^5.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"tsup": "^7.1.0",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
23
apps/test-server/routes.ts
Normal file
23
apps/test-server/routes.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import UserController from './controller/UserController';
|
||||
import FileController from './controller/FileController';
|
||||
|
||||
export default [
|
||||
// user
|
||||
{
|
||||
path: '/login',
|
||||
method: 'post',
|
||||
action: UserController.login,
|
||||
},
|
||||
{
|
||||
path: '/getUserInfoById',
|
||||
method: 'get',
|
||||
action: UserController.getUserInfoById,
|
||||
},
|
||||
|
||||
// file
|
||||
{
|
||||
path: '/upload',
|
||||
method: 'post',
|
||||
action: FileController.upload,
|
||||
},
|
||||
];
|
54
apps/test-server/service/FileService.ts
Normal file
54
apps/test-server/service/FileService.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
const uploadUrl = 'http://localhost:3300/static/upload';
|
||||
const filePath = path.join(__dirname, '../static/upload/');
|
||||
|
||||
fs.ensureDir(filePath);
|
||||
export default class UserService {
|
||||
async upload(ctx, files, isMultiple) {
|
||||
let fileReader, fileResource, writeStream;
|
||||
|
||||
const fileFunc = function (file) {
|
||||
fileReader = fs.createReadStream(file.path);
|
||||
fileResource = filePath + `/${file.name}`;
|
||||
console.log(fileResource);
|
||||
|
||||
writeStream = fs.createWriteStream(fileResource);
|
||||
fileReader.pipe(writeStream);
|
||||
};
|
||||
|
||||
const returnFunc = function (flag) {
|
||||
if (flag) {
|
||||
let url = '';
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
url += uploadUrl + `/${files[i].name},`;
|
||||
}
|
||||
url = url.replace(/,$/gi, '');
|
||||
ctx.body = {
|
||||
url: url,
|
||||
code: 0,
|
||||
message: 'upload Success!',
|
||||
};
|
||||
} else {
|
||||
ctx.body = {
|
||||
url: uploadUrl + `/${files.name}`,
|
||||
code: 0,
|
||||
message: 'upload Success!',
|
||||
};
|
||||
}
|
||||
};
|
||||
console.log(isMultiple, files.length);
|
||||
|
||||
if (isMultiple) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const f1 = files[i];
|
||||
fileFunc(f1);
|
||||
}
|
||||
} else {
|
||||
fileFunc(files);
|
||||
}
|
||||
fs.ensureDir(filePath);
|
||||
returnFunc(isMultiple);
|
||||
}
|
||||
}
|
25
apps/test-server/service/UserService.ts
Normal file
25
apps/test-server/service/UserService.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Result } from '../utils';
|
||||
|
||||
const fakeUserInfo = {
|
||||
userId: '1',
|
||||
username: 'vben',
|
||||
realName: 'Vben Admin',
|
||||
desc: 'manager',
|
||||
password: '123456',
|
||||
token: 'fakeToken1',
|
||||
roles: [
|
||||
{
|
||||
roleName: 'Super Admin',
|
||||
value: 'super',
|
||||
},
|
||||
],
|
||||
};
|
||||
export default class UserService {
|
||||
async login() {
|
||||
return Result.success(fakeUserInfo);
|
||||
}
|
||||
|
||||
async getUserInfoById() {
|
||||
return Result.success(fakeUserInfo);
|
||||
}
|
||||
}
|
7
apps/test-server/tsconfig.json
Normal file
7
apps/test-server/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/ts-config/node-server.json",
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
9
apps/test-server/utils.ts
Normal file
9
apps/test-server/utils.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export class Result {
|
||||
static success(data: any) {
|
||||
return {
|
||||
code: 0,
|
||||
success: true,
|
||||
result: data,
|
||||
};
|
||||
}
|
||||
}
|
24
embedded.js
Normal file
24
embedded.js
Normal file
@ -0,0 +1,24 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const filePath = path.join(__dirname, 'dist', 'index.html');
|
||||
|
||||
// 读取 HTML 文件
|
||||
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error('读取文件出错:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = data.replace(
|
||||
/<script crossorigin src="\/_app\.config\.js"><\/script>/g,
|
||||
'<script crossorigin src="./_app.config.js"></script>'
|
||||
);
|
||||
fs.writeFile(filePath, result, 'utf8', (err) => {
|
||||
if (err) {
|
||||
console.error('写入文件出错:', err);
|
||||
return;
|
||||
}
|
||||
console.log('html处理成功!');
|
||||
});
|
||||
});
|
164
index.html
Normal file
164
index.html
Normal file
@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" id="htmlRoot">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="renderer" content="webkit" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||
/>
|
||||
<title><%= VITE_GLOB_APP_TITLE %></title>
|
||||
<link rel="icon" href="<%= VITE_PUBLIC_PATH %>favicon.ico" />
|
||||
<style>
|
||||
#app {
|
||||
//background-image: url('<%= VITE_PUBLIC_PATH %>bg.png');
|
||||
//background-size: cover;
|
||||
//background-position: center center;
|
||||
|
||||
background-color: #0d1540;
|
||||
//background-color: rgba(45, 119, 243, 0.4);
|
||||
//background-blend-mode: overlay;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<style>
|
||||
html[data-theme='dark'] .app-loading {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .app-loading .app-loading-title {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.app-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.app-loading .app-loading-wrap {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
|
||||
.app-loading .dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 98px;
|
||||
}
|
||||
|
||||
.app-loading .app-loading-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
color: rgb(0 0 0 / 85%);
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.app-loading .app-loading-logo {
|
||||
display: block;
|
||||
width: 90px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-top: 30px;
|
||||
transform: rotate(45deg);
|
||||
animation: ant-rotate 1.2s infinite linear;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.dot i {
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transform: scale(0.75);
|
||||
transform-origin: 50% 50%;
|
||||
animation: ant-spin-move 1s infinite linear alternate;
|
||||
border-radius: 100%;
|
||||
opacity: 0.3;
|
||||
background-color: #0065cc;
|
||||
}
|
||||
|
||||
.dot i:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dot i:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.dot i:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.dot i:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
@keyframes ant-rotate {
|
||||
to {
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ant-rotate {
|
||||
to {
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ant-spin-move {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ant-spin-move {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="app-loading">
|
||||
<div class="app-loading-wrap">
|
||||
<img src="<%= VITE_PUBLIC_PATH %>logo.png" class="app-loading-logo" alt="Logo" />
|
||||
<!-- <div class="app-loading-dots">-->
|
||||
<!-- <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="app-loading-title"><%= VITE_GLOB_APP_TITLE %></div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
4
internal/eslint-config/.eslintrc.js
Normal file
4
internal/eslint-config/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben/eslint-config/strict'],
|
||||
};
|
10
internal/eslint-config/build.config.ts
Normal file
10
internal/eslint-config/build.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
entries: ['src/index', 'src/strict'],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
49
internal/eslint-config/package.json
Normal file
49
internal/eslint-config/package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@vben/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/eslint-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./strict": {
|
||||
"types": "./dist/strict.d.ts",
|
||||
"import": "./dist/strict.mjs",
|
||||
"require": "./dist/strict.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "pnpm rimraf .turbo node_modules dist",
|
||||
"lint": "pnpm eslint .",
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
||||
"@typescript-eslint/parser": "^6.3.0",
|
||||
"eslint": "^8.46.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.28.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"vue-eslint-parser": "^9.3.1"
|
||||
}
|
||||
}
|
91
internal/eslint-config/src/index.ts
Normal file
91
internal/eslint-config/src/index.ts
Normal file
@ -0,0 +1,91 @@
|
||||
export default {
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
project: './tsconfig.*?.json',
|
||||
createDefaultProgram: false,
|
||||
extraFileExtensions: ['.vue'],
|
||||
},
|
||||
plugins: ['vue', '@typescript-eslint', 'import'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:vue/vue3-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'space-before-function-paren': 'off',
|
||||
|
||||
'import/first': 'error',
|
||||
'import/newline-after-import': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/ban-ts-ignore': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'vue/script-setup-uses-vars': 'error',
|
||||
'vue/no-reserved-component-names': 'off',
|
||||
'vue/custom-event-name-casing': 'off',
|
||||
'vue/attributes-order': 'off',
|
||||
'vue/one-component-per-file': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/multiline-html-element-content-newline': 'off',
|
||||
'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',
|
||||
{
|
||||
html: {
|
||||
void: 'always',
|
||||
normal: 'never',
|
||||
component: 'always',
|
||||
},
|
||||
svg: 'always',
|
||||
math: 'always',
|
||||
},
|
||||
],
|
||||
'vue/multi-word-component-names': 'off',
|
||||
// 'sort-imports': [
|
||||
// 'error',
|
||||
// {
|
||||
// ignoreCase: true,
|
||||
// ignoreDeclarationSort: false,
|
||||
// ignoreMemberSort: false,
|
||||
// memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
|
||||
// allowSeparatedGroups: false,
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
globals: { defineOptions: 'readonly' },
|
||||
};
|
57
internal/eslint-config/src/strict.ts
Normal file
57
internal/eslint-config/src/strict.ts
Normal file
@ -0,0 +1,57 @@
|
||||
export default {
|
||||
extends: ['@vben'],
|
||||
plugins: ['simple-import-sort'],
|
||||
rules: {
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-expect-error': 'allow-with-description',
|
||||
'ts-ignore': 'allow-with-description',
|
||||
'ts-nocheck': 'allow-with-description',
|
||||
'ts-check': false,
|
||||
},
|
||||
],
|
||||
|
||||
/**
|
||||
* 【强制】关键字前后有一个空格
|
||||
* @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/keyword-spacing.md
|
||||
*/
|
||||
'keyword-spacing': 'off',
|
||||
'@typescript-eslint/keyword-spacing': [
|
||||
'error',
|
||||
{
|
||||
before: true,
|
||||
after: true,
|
||||
overrides: {
|
||||
return: { after: true },
|
||||
throw: { after: true },
|
||||
case: { after: true },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
/**
|
||||
* 禁止出现空函数,普通函数(非 async/await/generator)、箭头函数、类上的方法除外
|
||||
* @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-function.md
|
||||
*/
|
||||
'no-empty-function': 'off',
|
||||
'@typescript-eslint/no-empty-function': [
|
||||
'error',
|
||||
{
|
||||
allow: ['arrowFunctions', 'functions', 'methods'],
|
||||
},
|
||||
],
|
||||
|
||||
/**
|
||||
* 优先使用 interface 而不是 type 定义对象类型
|
||||
* @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-definitions.md
|
||||
*/
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||
|
||||
'vue/attributes-order': 'error',
|
||||
'vue/require-default-prop': 'error',
|
||||
},
|
||||
};
|
5
internal/eslint-config/tsconfig.json
Normal file
5
internal/eslint-config/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/ts-config/node.json",
|
||||
"include": ["src"]
|
||||
}
|
4
internal/stylelint-config/.eslintrc.js
Normal file
4
internal/stylelint-config/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben/eslint-config/strict'],
|
||||
};
|
10
internal/stylelint-config/build.config.ts
Normal file
10
internal/stylelint-config/build.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
entries: ['src/index'],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
49
internal/stylelint-config/package.json
Normal file
49
internal/stylelint-config/package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@vben/stylelint-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/stylelint-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "pnpm rimraf .turbo node_modules dist",
|
||||
"lint": "pnpm eslint .",
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "^8.4.24",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-less": "^6.0.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"prettier": "^2.8.8",
|
||||
"stylelint": "^15.10.1",
|
||||
"stylelint-config-property-sort-order-smacss": "^9.1.0",
|
||||
"stylelint-config-recommended": "^13.0.0",
|
||||
"stylelint-config-recommended-scss": "^12.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-config-standard-scss": "^10.0.0",
|
||||
"stylelint-order": "^6.0.3",
|
||||
"stylelint-prettier": "^3.0.0"
|
||||
}
|
||||
}
|
92
internal/stylelint-config/src/index.ts
Normal file
92
internal/stylelint-config/src/index.ts
Normal file
@ -0,0 +1,92 @@
|
||||
export default {
|
||||
extends: ['stylelint-config-standard', 'stylelint-config-property-sort-order-smacss'],
|
||||
plugins: ['stylelint-order', 'stylelint-prettier'],
|
||||
// customSyntax: 'postcss-html',
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.(css|html|vue)'],
|
||||
customSyntax: 'postcss-html',
|
||||
},
|
||||
{
|
||||
files: ['*.less', '**/*.less'],
|
||||
customSyntax: 'postcss-less',
|
||||
extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'],
|
||||
},
|
||||
{
|
||||
files: ['*.scss', '**/*.scss'],
|
||||
customSyntax: 'postcss-scss',
|
||||
extends: ['stylelint-config-standard-scss', 'stylelint-config-recommended-vue/scss'],
|
||||
rule: {
|
||||
'scss/percent-placeholder-pattern': null,
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'media-feature-range-notation': null,
|
||||
'selector-not-notation': null,
|
||||
'import-notation': null,
|
||||
'function-no-unknown': null,
|
||||
'selector-class-pattern': null,
|
||||
'selector-pseudo-class-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoClasses: ['global', 'deep'],
|
||||
},
|
||||
],
|
||||
'selector-pseudo-element-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignorePseudoElements: ['v-deep'],
|
||||
},
|
||||
],
|
||||
'at-rule-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreAtRules: [
|
||||
'tailwind',
|
||||
'apply',
|
||||
'variants',
|
||||
'responsive',
|
||||
'screen',
|
||||
'function',
|
||||
'if',
|
||||
'each',
|
||||
'include',
|
||||
'mixin',
|
||||
'extend',
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-empty-source': null,
|
||||
'string-quotes': null,
|
||||
'named-grid-areas-no-invalid': null,
|
||||
'no-descending-specificity': null,
|
||||
'font-family-no-missing-generic-family-keyword': null,
|
||||
'rule-empty-line-before': [
|
||||
'always',
|
||||
{
|
||||
ignore: ['after-comment', 'first-nested'],
|
||||
},
|
||||
],
|
||||
'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
|
||||
'order/order': [
|
||||
[
|
||||
'dollar-variables',
|
||||
'custom-properties',
|
||||
'at-rules',
|
||||
'declarations',
|
||||
{
|
||||
type: 'at-rule',
|
||||
name: 'supports',
|
||||
},
|
||||
{
|
||||
type: 'at-rule',
|
||||
name: 'media',
|
||||
},
|
||||
'rules',
|
||||
],
|
||||
{ severity: 'error' },
|
||||
],
|
||||
},
|
||||
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
|
||||
};
|
5
internal/stylelint-config/tsconfig.json
Normal file
5
internal/stylelint-config/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/ts-config/node.json",
|
||||
"include": ["src"]
|
||||
}
|
27
internal/ts-config/base.json
Normal file
27
internal/ts-config/base.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Base",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"noImplicitOverride": true,
|
||||
"noUnusedLocals": true,
|
||||
"esModuleInterop": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"composite": false,
|
||||
"declarationMap": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSources": false,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true,
|
||||
"noUnusedParameters": false,
|
||||
"preserveWatchOutput": true,
|
||||
"experimentalDecorators": true,
|
||||
"resolveJsonModule": true,
|
||||
"removeComments": true
|
||||
},
|
||||
"exclude": ["**/node_modules/**", "**/dist/**"]
|
||||
}
|
18
internal/ts-config/node-server.json
Normal file
18
internal/ts-config/node-server.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Node Server Config",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": false,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./"
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
12
internal/ts-config/node.json
Normal file
12
internal/ts-config/node.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Node Config",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true,
|
||||
"noEmit": true,
|
||||
"baseUrl": "./"
|
||||
}
|
||||
}
|
25
internal/ts-config/package.json
Normal file
25
internal/ts-config/package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@vben/ts-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/ts-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
"base.json",
|
||||
"node.json",
|
||||
"vue-app.json",
|
||||
"node-server.json"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/node": "^20.4.0",
|
||||
"vite": "^4.4.0"
|
||||
}
|
||||
}
|
10
internal/ts-config/vue-app.json
Normal file
10
internal/ts-config/vue-app.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Vue Application",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"noImplicitAny": false
|
||||
}
|
||||
}
|
4
internal/vite-config/.eslintrc.js
Normal file
4
internal/vite-config/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben/eslint-config/strict'],
|
||||
};
|
10
internal/vite-config/build.config.ts
Normal file
10
internal/vite-config/build.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
entries: ['src/index'],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
59
internal/vite-config/package.json
Normal file
59
internal/vite-config/package.json
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@vben/vite-config",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "internal/vite-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "pnpm rimraf .turbo node_modules dist",
|
||||
"lint": "pnpm eslint .",
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.0",
|
||||
"vite": "^4.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"ant-design-vue": "^4.0.3",
|
||||
"dayjs": "^1.11.9",
|
||||
"dotenv": "^16.3.1",
|
||||
"fs-extra": "^11.1.1",
|
||||
"less": "^4.1.3",
|
||||
"picocolors": "^1.0.0",
|
||||
"pkg-types": "^1.0.3",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.63.6",
|
||||
"unocss": "^0.53.4",
|
||||
"unplugin-config": "^0.0.13",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-dts": "^3.1.0",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-purge-icons": "^0.9.2",
|
||||
"vite-plugin-svg-icons": "^2.0.1"
|
||||
}
|
||||
}
|
119
internal/vite-config/src/config/application.ts
Normal file
119
internal/vite-config/src/config/application.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { readPackageJSON } from 'pkg-types';
|
||||
import { defineConfig, loadEnv, mergeConfig, type UserConfig } from 'vite';
|
||||
|
||||
import { createPlugins } from '../plugins';
|
||||
import { generateModifyVars } from '../utils/modifyVars';
|
||||
import { commonConfig } from './common';
|
||||
|
||||
interface DefineOptions {
|
||||
overrides?: UserConfig;
|
||||
options?: {
|
||||
//
|
||||
};
|
||||
}
|
||||
|
||||
function defineApplicationConfig(defineOptions: DefineOptions = {}) {
|
||||
const { overrides = {} } = defineOptions;
|
||||
|
||||
return defineConfig(async ({ command, mode }) => {
|
||||
const root = process.cwd();
|
||||
const isBuild = command === 'build';
|
||||
const { VITE_PUBLIC_PATH, VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv(
|
||||
mode,
|
||||
root,
|
||||
);
|
||||
|
||||
const defineData = await createDefineData(root);
|
||||
const plugins = await createPlugins({
|
||||
isBuild,
|
||||
root,
|
||||
enableAnalyze: VITE_ENABLE_ANALYZE === 'true',
|
||||
enableMock: VITE_USE_MOCK === 'true',
|
||||
compress: VITE_BUILD_COMPRESS,
|
||||
});
|
||||
|
||||
const pathResolve = (pathname: string) => resolve(root, '.', pathname);
|
||||
|
||||
const applicationConfig: UserConfig = {
|
||||
base: VITE_PUBLIC_PATH,
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: 'vue-i18n',
|
||||
replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
|
||||
},
|
||||
// /@/xxxx => src/xxxx
|
||||
{
|
||||
find: /\/@\//,
|
||||
replacement: pathResolve('src') + '/',
|
||||
},
|
||||
// /#/xxxx => types/xxxx
|
||||
{
|
||||
find: /\/#\//,
|
||||
replacement: pathResolve('types') + '/',
|
||||
},
|
||||
// @/xxxx => src/xxxx
|
||||
{
|
||||
find: /@\//,
|
||||
replacement: pathResolve('src') + '/',
|
||||
},
|
||||
// #/xxxx => types/xxxx
|
||||
{
|
||||
find: /#\//,
|
||||
replacement: pathResolve('types') + '/',
|
||||
}
|
||||
],
|
||||
},
|
||||
define: defineData,
|
||||
build: {
|
||||
target: 'es2015',
|
||||
cssTarget: 'chrome80',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
// 入口文件名
|
||||
entryFileNames: 'assets/[name].js',
|
||||
manualChunks: {
|
||||
vue: ['vue', 'pinia', 'vue-router'],
|
||||
antd: ['ant-design-vue', '@ant-design/icons-vue'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
less: {
|
||||
modifyVars: generateModifyVars(),
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins,
|
||||
};
|
||||
|
||||
const mergedConfig = mergeConfig(commonConfig(mode), applicationConfig);
|
||||
|
||||
return mergeConfig(mergedConfig, overrides);
|
||||
});
|
||||
}
|
||||
|
||||
async function createDefineData(root: string) {
|
||||
try {
|
||||
const pkgJson = await readPackageJSON(root);
|
||||
const { dependencies, devDependencies, name, version } = pkgJson;
|
||||
|
||||
const __APP_INFO__ = {
|
||||
pkg: { dependencies, devDependencies, name, version },
|
||||
lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
};
|
||||
return {
|
||||
__APP_INFO__: JSON.stringify(__APP_INFO__),
|
||||
};
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export { defineApplicationConfig };
|
27
internal/vite-config/src/config/common.ts
Normal file
27
internal/vite-config/src/config/common.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { presetTypography, presetUno } from 'unocss';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import { type UserConfig } from 'vite';
|
||||
|
||||
const commonConfig: (mode: string) => UserConfig = (mode) => ({
|
||||
server: {
|
||||
host: true,
|
||||
},
|
||||
esbuild: {
|
||||
drop: mode === 'production' ? ['console', 'debugger'] : [],
|
||||
},
|
||||
build: {
|
||||
reportCompressedSize: false,
|
||||
chunkSizeWarningLimit: 1500,
|
||||
rollupOptions: {
|
||||
// TODO: Prevent memory overflow
|
||||
maxParallelFileOps: 3,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
UnoCSS({
|
||||
presets: [presetUno(), presetTypography()],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export { commonConfig };
|
42
internal/vite-config/src/config/package.ts
Normal file
42
internal/vite-config/src/config/package.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { readPackageJSON } from 'pkg-types';
|
||||
import { defineConfig, mergeConfig, type UserConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
import { commonConfig } from './common';
|
||||
|
||||
interface DefineOptions {
|
||||
overrides?: UserConfig;
|
||||
options?: {
|
||||
//
|
||||
};
|
||||
}
|
||||
|
||||
function definePackageConfig(defineOptions: DefineOptions = {}) {
|
||||
const { overrides = {} } = defineOptions;
|
||||
const root = process.cwd();
|
||||
return defineConfig(async ({ mode }) => {
|
||||
const { dependencies = {}, peerDependencies = {} } = await readPackageJSON(root);
|
||||
const packageConfig: UserConfig = {
|
||||
build: {
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
formats: ['es'],
|
||||
fileName: () => 'index.mjs',
|
||||
},
|
||||
rollupOptions: {
|
||||
external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
dts({
|
||||
logLevel: 'error',
|
||||
}),
|
||||
],
|
||||
};
|
||||
const mergedConfig = mergeConfig(commonConfig(mode), packageConfig);
|
||||
|
||||
return mergeConfig(mergedConfig, overrides);
|
||||
});
|
||||
}
|
||||
|
||||
export { definePackageConfig };
|
2
internal/vite-config/src/index.ts
Normal file
2
internal/vite-config/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './config/application';
|
||||
export * from './config/package';
|
23
internal/vite-config/src/plugins/appConfig.ts
Normal file
23
internal/vite-config/src/plugins/appConfig.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import GenerateConfig from 'unplugin-config/vite';
|
||||
import { type PluginOption } from 'vite';
|
||||
|
||||
import { getEnvConfig } from '../utils/env';
|
||||
import { strToHex } from '../utils/hash';
|
||||
|
||||
const GLOBAL_CONFIG_FILE_NAME = '_app.config.js';
|
||||
// This constant sets the output directory for the Vite package
|
||||
export async function createConfigPluginConfig(
|
||||
shouldGenerateConfig: boolean,
|
||||
): Promise<PluginOption> {
|
||||
const config = await getEnvConfig();
|
||||
// LINK /src/utils/env.ts -> getVariableName
|
||||
const APP_NAME = strToHex(config?.VITE_GLOB_APP_TITLE ?? '__APP');
|
||||
// https://github.com/kirklin/unplugin-config
|
||||
return GenerateConfig({
|
||||
disabledConfig: !shouldGenerateConfig,
|
||||
globConfigFileName: GLOBAL_CONFIG_FILE_NAME,
|
||||
appName: APP_NAME,
|
||||
envConfigPrefix: 'VITE_GLOB_',
|
||||
publicPath: config?.VITE_PUBLIC_PATH ?? ''
|
||||
});
|
||||
}
|
38
internal/vite-config/src/plugins/compress.ts
Normal file
38
internal/vite-config/src/plugins/compress.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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 compressPlugin from 'vite-plugin-compression';
|
||||
|
||||
export function configCompressPlugin({
|
||||
compress,
|
||||
deleteOriginFile = false,
|
||||
}: {
|
||||
compress: string;
|
||||
deleteOriginFile?: boolean;
|
||||
}): PluginOption[] {
|
||||
const compressList = compress.split(',');
|
||||
|
||||
const plugins: PluginOption[] = [];
|
||||
|
||||
if (compressList.includes('gzip')) {
|
||||
plugins.push(
|
||||
compressPlugin({
|
||||
ext: '.gz',
|
||||
deleteOriginFile,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (compressList.includes('brotli')) {
|
||||
plugins.push(
|
||||
compressPlugin({
|
||||
ext: '.br',
|
||||
algorithm: 'brotliCompress',
|
||||
deleteOriginFile,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return plugins;
|
||||
}
|
13
internal/vite-config/src/plugins/html.ts
Normal file
13
internal/vite-config/src/plugins/html.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
export function configHtmlPlugin({ isBuild }: { isBuild: boolean }) {
|
||||
const htmlPlugin: PluginOption[] = createHtmlPlugin({
|
||||
minify: isBuild,
|
||||
});
|
||||
return htmlPlugin;
|
||||
}
|
59
internal/vite-config/src/plugins/index.ts
Normal file
59
internal/vite-config/src/plugins/index.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import { type PluginOption } from 'vite';
|
||||
import purgeIcons from 'vite-plugin-purge-icons';
|
||||
|
||||
import { createConfigPluginConfig } from './appConfig';
|
||||
import { configCompressPlugin } from './compress';
|
||||
import { configHtmlPlugin } from './html';
|
||||
import { configMockPlugin } from './mock';
|
||||
import { configSvgIconsPlugin } from './svgSprite';
|
||||
import { configVisualizerConfig } from './visualizer';
|
||||
|
||||
interface Options {
|
||||
isBuild: boolean;
|
||||
root: string;
|
||||
compress: string;
|
||||
enableMock?: boolean;
|
||||
enableAnalyze?: boolean;
|
||||
}
|
||||
|
||||
async function createPlugins({ isBuild, enableMock, compress, enableAnalyze }: Options) {
|
||||
const vitePlugins: (PluginOption | PluginOption[])[] = [vue(), vueJsx()];
|
||||
|
||||
const appConfigPlugin = await createConfigPluginConfig(isBuild);
|
||||
vitePlugins.push(appConfigPlugin);
|
||||
|
||||
// vite-plugin-html
|
||||
vitePlugins.push(configHtmlPlugin({ isBuild }));
|
||||
|
||||
// vite-plugin-svg-icons
|
||||
vitePlugins.push(configSvgIconsPlugin({ isBuild }));
|
||||
|
||||
// vite-plugin-purge-icons
|
||||
vitePlugins.push(purgeIcons());
|
||||
|
||||
// The following plugins only work in the production environment
|
||||
if (isBuild) {
|
||||
// rollup-plugin-gzip
|
||||
vitePlugins.push(
|
||||
configCompressPlugin({
|
||||
compress,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// rollup-plugin-visualizer
|
||||
if (enableAnalyze) {
|
||||
vitePlugins.push(configVisualizerConfig());
|
||||
}
|
||||
|
||||
// vite-plugin-mock
|
||||
if (enableMock) {
|
||||
vitePlugins.push(configMockPlugin({ isBuild }));
|
||||
}
|
||||
|
||||
return vitePlugins;
|
||||
}
|
||||
|
||||
export { createPlugins };
|
19
internal/vite-config/src/plugins/mock.ts
Normal file
19
internal/vite-config/src/plugins/mock.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Mock plugin for development and production.
|
||||
* https://github.com/anncwb/vite-plugin-mock
|
||||
*/
|
||||
import { viteMockServe } from 'vite-plugin-mock';
|
||||
|
||||
export function configMockPlugin({ isBuild }: { isBuild: boolean }) {
|
||||
return viteMockServe({
|
||||
ignore: /^_/,
|
||||
mockPath: 'mock',
|
||||
localEnabled: !isBuild,
|
||||
prodEnabled: isBuild,
|
||||
injectCode: `
|
||||
import { setupProdMockServer } from '../mock/_createProductionServer';
|
||||
|
||||
setupProdMockServer();
|
||||
`,
|
||||
});
|
||||
}
|
17
internal/vite-config/src/plugins/svgSprite.ts
Normal file
17
internal/vite-config/src/plugins/svgSprite.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Vite Plugin for fast creating SVG sprites.
|
||||
* https://github.com/anncwb/vite-plugin-svg-icons
|
||||
*/
|
||||
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import type { PluginOption } from 'vite';
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
|
||||
export function configSvgIconsPlugin({ isBuild }: { isBuild: boolean }) {
|
||||
const svgIconsPlugin = createSvgIconsPlugin({
|
||||
iconDirs: [resolve(process.cwd(), 'src/assets/icons')],
|
||||
svgoOptions: isBuild,
|
||||
});
|
||||
return svgIconsPlugin as PluginOption;
|
||||
}
|
14
internal/vite-config/src/plugins/visualizer.ts
Normal file
14
internal/vite-config/src/plugins/visualizer.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* Package file volume analysis
|
||||
*/
|
||||
import visualizer from 'rollup-plugin-visualizer';
|
||||
import { type PluginOption } from 'vite';
|
||||
|
||||
export function configVisualizerConfig() {
|
||||
return visualizer({
|
||||
filename: './node_modules/.cache/visualizer/stat.html',
|
||||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
}) as PluginOption;
|
||||
}
|
49
internal/vite-config/src/utils/env.ts
Normal file
49
internal/vite-config/src/utils/env.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { join } from 'node:path';
|
||||
|
||||
import dotenv from 'dotenv';
|
||||
import { readFile } from 'fs-extra';
|
||||
|
||||
/**
|
||||
* 获取当前环境下生效的配置文件名
|
||||
*/
|
||||
function getConfFiles() {
|
||||
const script = process.env.npm_lifecycle_script as string;
|
||||
const reg = new RegExp('--mode ([a-z_\\d]+)');
|
||||
const result = reg.exec(script);
|
||||
if (result) {
|
||||
const mode = result[1];
|
||||
return ['.env', `.env.${mode}`];
|
||||
}
|
||||
return ['.env', '.env.production'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the environment variables starting with the specified prefix
|
||||
* @param match prefix
|
||||
* @param confFiles ext
|
||||
*/
|
||||
export async function getEnvConfig(
|
||||
match = 'VITE_GLOB_',
|
||||
confFiles = getConfFiles(),
|
||||
): Promise<{
|
||||
[key: string]: string;
|
||||
}> {
|
||||
let envConfig = {};
|
||||
|
||||
for (const confFile of confFiles) {
|
||||
try {
|
||||
const envPath = await readFile(join(process.cwd(), confFile), { encoding: 'utf8' });
|
||||
const env = dotenv.parse(envPath);
|
||||
envConfig = { ...envConfig, ...env };
|
||||
} catch (e) {
|
||||
console.error(`Error in parsing ${confFile}`, e);
|
||||
}
|
||||
}
|
||||
const reg = new RegExp(`^(${match})`);
|
||||
Object.keys(envConfig).forEach((key) => {
|
||||
if (!reg.test(key)) {
|
||||
Reflect.deleteProperty(envConfig, key);
|
||||
}
|
||||
});
|
||||
return envConfig;
|
||||
}
|
16
internal/vite-config/src/utils/hash.ts
Normal file
16
internal/vite-config/src/utils/hash.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
|
||||
function createContentHash(content: string, hashLSize = 12) {
|
||||
const hash = createHash('sha256').update(content);
|
||||
return hash.digest('hex').slice(0, hashLSize);
|
||||
}
|
||||
function strToHex(str: string) {
|
||||
const result: string[] = [];
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
const hex = str.charCodeAt(i).toString(16);
|
||||
result.push(('000' + hex).slice(-4));
|
||||
}
|
||||
return result.join('').toUpperCase();
|
||||
}
|
||||
|
||||
export { createContentHash, strToHex };
|
52
internal/vite-config/src/utils/modifyVars.ts
Normal file
52
internal/vite-config/src/utils/modifyVars.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { generate } from '@ant-design/colors';
|
||||
// @ts-ignore: typo
|
||||
/* import { getThemeVariables } from 'ant-design-vue/dist/theme'; */
|
||||
import { theme } from 'ant-design-vue/lib';
|
||||
import convertLegacyToken from 'ant-design-vue/lib/theme/convertLegacyToken';
|
||||
|
||||
const { defaultAlgorithm, defaultSeed } = theme;
|
||||
|
||||
const primaryColor = '#0960bd';
|
||||
|
||||
function generateAntColors(color: string, theme: 'default' | 'dark' = 'default') {
|
||||
return generate(color, {
|
||||
theme,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* less global variable
|
||||
*/
|
||||
export function generateModifyVars() {
|
||||
const palettes = generateAntColors(primaryColor);
|
||||
const primary = palettes[5];
|
||||
|
||||
const primaryColorObj: Record<string, string> = {};
|
||||
|
||||
for (let index = 0; index < 10; index++) {
|
||||
primaryColorObj[`primary-${index + 1}`] = palettes[index];
|
||||
}
|
||||
|
||||
// const modifyVars = getThemeVariables();
|
||||
const mapToken = defaultAlgorithm(defaultSeed);
|
||||
const v3Token = convertLegacyToken(mapToken);
|
||||
|
||||
return {
|
||||
...v3Token,
|
||||
// reference: Avoid repeated references
|
||||
hack: `true; @import (reference) "${resolve('src/design/config.less')}";`,
|
||||
'primary-color': primary,
|
||||
...primaryColorObj,
|
||||
'info-color': primary,
|
||||
'processing-color': primary,
|
||||
'success-color': '#55D187', // Success color
|
||||
'error-color': '#ED6F6F', // False color
|
||||
'warning-color': '#EFBD47', // Warning color
|
||||
'font-size-base': '14px', // Main font size
|
||||
'border-radius-base': '2px', // Component/float fillet
|
||||
'link-color': primary, // Link color
|
||||
'app-content-background': '#fafafa', // Link color
|
||||
};
|
||||
}
|
5
internal/vite-config/tsconfig.json
Normal file
5
internal/vite-config/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/ts-config/node.json",
|
||||
"include": ["src"]
|
||||
}
|
34
mock/_createProductionServer.ts
Normal file
34
mock/_createProductionServer.ts
Normal file
@ -0,0 +1,34 @@
|
||||
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.glob('./**/*.ts', { eager: true });
|
||||
|
||||
const mockModules: any[] = [];
|
||||
Object.keys(modules).forEach((key) => {
|
||||
if (key.includes('/_')) {
|
||||
return;
|
||||
}
|
||||
mockModules.push(...(modules as Recordable)[key].default);
|
||||
});
|
||||
|
||||
/**
|
||||
* Used in a production environment. Need to manually import all modules
|
||||
*/
|
||||
export function setupProdMockServer() {
|
||||
createProdMockServer(mockModules);
|
||||
}
|
62
mock/_util.ts
Normal file
62
mock/_util.ts
Normal file
@ -0,0 +1,62 @@
|
||||
// Interface data format used to return a unified format
|
||||
import { ResultEnum } from '/@/enums/httpEnum';
|
||||
|
||||
export function resultSuccess<T = Recordable>(result: T, { message = 'ok' } = {}) {
|
||||
return {
|
||||
code: ResultEnum.SUCCESS,
|
||||
result,
|
||||
message,
|
||||
type: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
export function resultPageSuccess<T = any>(
|
||||
page: number,
|
||||
pageSize: number,
|
||||
list: T[],
|
||||
{ message = 'ok' } = {},
|
||||
) {
|
||||
const pageData = pagination(page, pageSize, list);
|
||||
|
||||
return {
|
||||
...resultSuccess({
|
||||
items: pageData,
|
||||
total: list.length,
|
||||
}),
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
export function resultError(
|
||||
message = 'Request failed',
|
||||
{ code = ResultEnum.ERROR, result = null } = {},
|
||||
) {
|
||||
return {
|
||||
code,
|
||||
result,
|
||||
message,
|
||||
type: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
export interface requestParams {
|
||||
method: string;
|
||||
body: any;
|
||||
headers?: { authorization?: string };
|
||||
query: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
|
||||
*
|
||||
*/
|
||||
export function getRequestToken({ headers }: requestParams): string | undefined {
|
||||
return headers?.authorization;
|
||||
}
|
71
mock/demo/account.ts
Normal file
71
mock/demo/account.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultSuccess, resultError } from '../_util';
|
||||
import { ResultEnum } from '../../src/enums/httpEnum';
|
||||
|
||||
const userInfo = {
|
||||
name: 'Vben',
|
||||
userid: '00000001',
|
||||
email: 'test@gmail.com',
|
||||
signature: '海纳百川,有容乃大',
|
||||
introduction: '微笑着,努力着,欣赏着',
|
||||
title: '交互专家',
|
||||
group: '某某某事业群-某某平台部-某某技术部-UED',
|
||||
tags: [
|
||||
{
|
||||
key: '0',
|
||||
label: '很有想法的',
|
||||
},
|
||||
{
|
||||
key: '1',
|
||||
label: '专注设计',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: '辣~',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: '大长腿',
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
label: '川妹子',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
label: '海纳百川',
|
||||
},
|
||||
],
|
||||
notifyCount: 12,
|
||||
unreadCount: 11,
|
||||
country: 'China',
|
||||
address: 'Xiamen City 77',
|
||||
phone: '0592-268888888',
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/account/getAccountInfo',
|
||||
timeout: 1000,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return resultSuccess(userInfo);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/user/sessionTimeout',
|
||||
method: 'post',
|
||||
statusCode: 401,
|
||||
response: () => {
|
||||
return resultError();
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/user/tokenExpired',
|
||||
method: 'post',
|
||||
statusCode: 200,
|
||||
response: () => {
|
||||
return resultError('Token Expired!', { code: ResultEnum.TIMEOUT as number });
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
325
mock/demo/api-cascader.ts
Normal file
325
mock/demo/api-cascader.ts
Normal file
@ -0,0 +1,325 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const areaList: any[] = [
|
||||
{
|
||||
id: '530825900854620160',
|
||||
code: '430000',
|
||||
parentCode: '100000',
|
||||
levelType: 1,
|
||||
name: '湖南省',
|
||||
province: '湖南省',
|
||||
city: null,
|
||||
district: null,
|
||||
town: null,
|
||||
village: null,
|
||||
parentPath: '430000',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 16:33:42',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530825900883980288',
|
||||
code: '430100',
|
||||
parentCode: '430000',
|
||||
levelType: 2,
|
||||
name: '长沙市',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: null,
|
||||
town: null,
|
||||
village: null,
|
||||
parentPath: '430000,430100',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 16:33:42',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530825900951089152',
|
||||
code: '430102',
|
||||
parentCode: '430100',
|
||||
levelType: 3,
|
||||
name: '芙蓉区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '芙蓉区',
|
||||
town: null,
|
||||
village: null,
|
||||
parentPath: '430000,430100,430102',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 16:33:42',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530825901014003712',
|
||||
code: '430104',
|
||||
parentCode: '430100',
|
||||
levelType: 3,
|
||||
name: '岳麓区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '岳麓区',
|
||||
town: null,
|
||||
village: null,
|
||||
parentPath: '430000,430100,430104',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 16:33:42',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530825900988837888',
|
||||
code: '430103',
|
||||
parentCode: '430100',
|
||||
levelType: 3,
|
||||
name: '天心区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: null,
|
||||
village: null,
|
||||
parentPath: '430000,430100,430103',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 16:33:42',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530826672489115648',
|
||||
code: '430103002',
|
||||
parentCode: '430103',
|
||||
levelType: 4,
|
||||
name: '坡子街街道',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: null,
|
||||
parentPath: '430000,430100,430103,430103002',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-12-14 15:26:43',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241171607552',
|
||||
code: '430103002001',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '八角亭社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '八角亭社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002001',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2021-01-20 14:07:23',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241200967680',
|
||||
code: '430103002002',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '西牌楼社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '西牌楼社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002002',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241230327808',
|
||||
code: '430103002003',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '太平街社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '太平街社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002003',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241259687936',
|
||||
code: '430103002005',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '坡子街社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '坡子街社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002005',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241284853760',
|
||||
code: '430103002006',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '青山祠社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '青山祠社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002006',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241310019584',
|
||||
code: '430103002007',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '沙河社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '沙河社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002007',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241381322752',
|
||||
code: '430103002008',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '碧湘社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '碧湘社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002008',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241410682880',
|
||||
code: '430103002009',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '创远社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '创远社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002009',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241431654400',
|
||||
code: '430103002010',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '楚湘社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '楚湘社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002010',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241465208832',
|
||||
code: '430103002011',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '西湖社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '西湖社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002011',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241502957568',
|
||||
code: '430103002012',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '登仁桥社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '登仁桥社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002012',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
{
|
||||
id: '530840241553289216',
|
||||
code: '430103002013',
|
||||
parentCode: '430103002',
|
||||
levelType: 5,
|
||||
name: '文庙坪社区',
|
||||
province: '湖南省',
|
||||
city: '长沙市',
|
||||
district: '天心区',
|
||||
town: '坡子街街道',
|
||||
village: '文庙坪社区',
|
||||
parentPath: '430000,430100,430103,430103002,430103002013',
|
||||
createTime: '2020-11-30 15:47:31',
|
||||
updateTime: '2020-11-30 17:30:41',
|
||||
customized: false,
|
||||
usable: true,
|
||||
},
|
||||
];
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/cascader/getAreaRecord',
|
||||
timeout: 1000,
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
const { parentCode } = body || {};
|
||||
if (!parentCode) {
|
||||
return resultSuccess(areaList.filter((it) => it.code === '430000'));
|
||||
}
|
||||
return resultSuccess(areaList.filter((it) => it.parentCode === parentCode));
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
28
mock/demo/select-demo.ts
Normal file
28
mock/demo/select-demo.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const demoList = (keyword, count = 20) => {
|
||||
const result = {
|
||||
list: [] as any[],
|
||||
};
|
||||
for (let index = 0; index < count; index++) {
|
||||
result.list.push({
|
||||
name: `${keyword ?? ''}选项${index}`,
|
||||
id: `${index}`,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/select/getDemoOptions',
|
||||
timeout: 1000,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { keyword, count } = query;
|
||||
console.log(keyword);
|
||||
return resultSuccess(demoList(keyword, count));
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
202
mock/demo/system.ts
Normal file
202
mock/demo/system.ts
Normal file
@ -0,0 +1,202 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultError, resultPageSuccess, resultSuccess } from '../_util';
|
||||
|
||||
const accountList = (() => {
|
||||
const result: any[] = [];
|
||||
for (let index = 0; index < 20; index++) {
|
||||
result.push({
|
||||
id: `${index}`,
|
||||
account: '@first',
|
||||
email: '@email',
|
||||
nickname: '@cname()',
|
||||
role: '@first',
|
||||
createTime: '@datetime',
|
||||
remark: '@cword(10,20)',
|
||||
'status|1': ['0', '1'],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
const roleList = (() => {
|
||||
const result: any[] = [];
|
||||
for (let index = 0; index < 4; index++) {
|
||||
result.push({
|
||||
id: index + 1,
|
||||
orderNo: `${index + 1}`,
|
||||
roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
|
||||
roleValue: '@first',
|
||||
createTime: '@datetime',
|
||||
remark: '@cword(10,20)',
|
||||
menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
|
||||
'status|1': ['0', '1'],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
const deptList = (() => {
|
||||
const result: any[] = [];
|
||||
for (let index = 0; index < 3; index++) {
|
||||
result.push({
|
||||
id: `${index}`,
|
||||
deptName: ['华东分部', '华南分部', '西北分部'][index],
|
||||
orderNo: index + 1,
|
||||
createTime: '@datetime',
|
||||
remark: '@cword(10,20)',
|
||||
'status|1': ['0', '0', '1'],
|
||||
children: (() => {
|
||||
const children: any[] = [];
|
||||
for (let j = 0; j < 4; j++) {
|
||||
children.push({
|
||||
id: `${index}-${j}`,
|
||||
deptName: ['研发部', '市场部', '商务部', '财务部'][j],
|
||||
orderNo: j + 1,
|
||||
createTime: '@datetime',
|
||||
remark: '@cword(10,20)',
|
||||
'status|1': ['0', '1'],
|
||||
parentDept: `${index}`,
|
||||
children: undefined,
|
||||
});
|
||||
}
|
||||
return children;
|
||||
})(),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
const menuList = (() => {
|
||||
const result: any[] = [];
|
||||
for (let index = 0; index < 3; index++) {
|
||||
result.push({
|
||||
id: `${index}`,
|
||||
icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index],
|
||||
component: 'LAYOUT',
|
||||
type: '0',
|
||||
menuName: ['Dashboard', '权限管理', '功能'][index],
|
||||
permission: '',
|
||||
orderNo: index + 1,
|
||||
createTime: '@datetime',
|
||||
'status|1': ['0', '0', '1'],
|
||||
children: (() => {
|
||||
const children: any[] = [];
|
||||
for (let j = 0; j < 4; j++) {
|
||||
children.push({
|
||||
id: `${index}-${j}`,
|
||||
type: '1',
|
||||
menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j],
|
||||
icon: 'ion:document',
|
||||
permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
|
||||
component: [
|
||||
'/dashboard/welcome/index',
|
||||
'/dashboard/analysis/index',
|
||||
'/dashboard/workbench/index',
|
||||
'/dashboard/test/index',
|
||||
][j],
|
||||
orderNo: j + 1,
|
||||
createTime: '@datetime',
|
||||
'status|1': ['0', '1'],
|
||||
parentMenu: `${index}`,
|
||||
children: (() => {
|
||||
const children: any[] = [];
|
||||
for (let k = 0; k < 4; k++) {
|
||||
children.push({
|
||||
id: `${index}-${j}-${k}`,
|
||||
type: '2',
|
||||
menuName: '按钮' + (j + 1) + '-' + (k + 1),
|
||||
icon: '',
|
||||
permission:
|
||||
['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index] +
|
||||
':btn' +
|
||||
(k + 1),
|
||||
component: [
|
||||
'/dashboard/welcome/index',
|
||||
'/dashboard/analysis/index',
|
||||
'/dashboard/workbench/index',
|
||||
'/dashboard/test/index',
|
||||
][j],
|
||||
orderNo: j + 1,
|
||||
createTime: '@datetime',
|
||||
'status|1': ['0', '1'],
|
||||
parentMenu: `${index}-${j}`,
|
||||
children: undefined,
|
||||
});
|
||||
}
|
||||
return children;
|
||||
})(),
|
||||
});
|
||||
}
|
||||
return children;
|
||||
})(),
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/system/getAccountList',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { page = 1, pageSize = 20 } = query;
|
||||
return resultPageSuccess(page, pageSize, accountList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/getRoleListByPage',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { page = 1, pageSize = 20 } = query;
|
||||
return resultPageSuccess(page, pageSize, roleList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/setRoleStatus',
|
||||
timeout: 500,
|
||||
method: 'post',
|
||||
response: ({ query }) => {
|
||||
const { id, status } = query;
|
||||
return resultSuccess({ id, status });
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/getAllRoleList',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return resultSuccess(roleList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/getDeptList',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return resultSuccess(deptList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/getMenuList',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return resultSuccess(menuList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/system/accountExist',
|
||||
timeout: 500,
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
const { account } = body || {};
|
||||
if (account && account.indexOf('admin') !== -1) {
|
||||
return resultError('该字段不能包含admin');
|
||||
} else {
|
||||
return resultSuccess(`${account} can use`);
|
||||
}
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
55
mock/demo/table-demo.ts
Normal file
55
mock/demo/table-demo.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { Random } from 'mockjs';
|
||||
import { resultPageSuccess } from '../_util';
|
||||
|
||||
function getRandomPics(count = 10): string[] {
|
||||
const arr: string[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
arr.push(Random.image('800x600', Random.color(), Random.color(), Random.title()));
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const demoList = (() => {
|
||||
const result: any[] = [];
|
||||
for (let index = 0; index < 200; index++) {
|
||||
result.push({
|
||||
id: `${index}`,
|
||||
beginTime: '@datetime',
|
||||
endTime: '@datetime',
|
||||
address: '@city()',
|
||||
name: '@cname()',
|
||||
name1: '@cname()',
|
||||
name2: '@cname()',
|
||||
name3: '@cname()',
|
||||
name4: '@cname()',
|
||||
name5: '@cname()',
|
||||
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),
|
||||
date: `@date('yyyy-MM-dd')`,
|
||||
time: `@time('HH:mm')`,
|
||||
'no|100000-10000000': 100000,
|
||||
'status|1': ['normal', 'enable', 'disable'],
|
||||
});
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/table/getDemoList',
|
||||
timeout: 100,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { page = 1, pageSize = 20 } = query;
|
||||
return resultPageSuccess(page, pageSize, demoList);
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
38
mock/demo/tree-demo.ts
Normal file
38
mock/demo/tree-demo.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
const demoTreeList = (keyword) => {
|
||||
const result = {
|
||||
list: [] as Recordable[],
|
||||
};
|
||||
for (let index = 0; index < 5; index++) {
|
||||
const children: Recordable[] = [];
|
||||
for (let j = 0; j < 3; j++) {
|
||||
children.push({
|
||||
title: `${keyword ?? ''}选项${index}-${j}`,
|
||||
value: `${index}-${j}`,
|
||||
key: `${index}-${j}`,
|
||||
});
|
||||
}
|
||||
result.list.push({
|
||||
title: `${keyword ?? ''}选项${index}`,
|
||||
value: `${index}`,
|
||||
key: `${index}`,
|
||||
children,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/tree/getDemoOptions',
|
||||
timeout: 1000,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
const { keyword } = query;
|
||||
console.log(keyword);
|
||||
return resultSuccess(demoTreeList(keyword));
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
270
mock/sys/menu.ts
Normal file
270
mock/sys/menu.ts
Normal file
@ -0,0 +1,270 @@
|
||||
import { resultSuccess, resultError, getRequestToken, requestParams } from '../_util';
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { createFakeUserList } from './user';
|
||||
|
||||
// single
|
||||
const dashboardRoute = {
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: 'LAYOUT',
|
||||
redirect: '/dashboard/analysis',
|
||||
meta: {
|
||||
title: 'routes.dashboard.dashboard',
|
||||
hideChildrenInMenu: true,
|
||||
icon: 'bx:bx-home',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'analysis',
|
||||
name: 'Analysis',
|
||||
component: '/dashboard/analysis/index',
|
||||
meta: {
|
||||
hideMenu: true,
|
||||
hideBreadcrumb: true,
|
||||
title: 'routes.dashboard.analysis',
|
||||
currentActiveMenu: '/dashboard',
|
||||
icon: 'bx:bx-home',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'workbench',
|
||||
name: 'Workbench',
|
||||
component: '/dashboard/workbench/index',
|
||||
meta: {
|
||||
hideMenu: true,
|
||||
hideBreadcrumb: true,
|
||||
title: 'routes.dashboard.workbench',
|
||||
currentActiveMenu: '/dashboard',
|
||||
icon: 'bx:bx-home',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const backRoute = {
|
||||
path: 'back',
|
||||
name: 'PermissionBackDemo',
|
||||
meta: {
|
||||
title: 'routes.demo.permission.back',
|
||||
},
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'page',
|
||||
name: 'BackAuthPage',
|
||||
component: '/demo/permission/back/index',
|
||||
meta: {
|
||||
title: 'routes.demo.permission.backPage',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'btn',
|
||||
name: 'BackAuthBtn',
|
||||
component: '/demo/permission/back/Btn',
|
||||
meta: {
|
||||
title: 'routes.demo.permission.backBtn',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const authRoute = {
|
||||
path: '/permission',
|
||||
name: 'Permission',
|
||||
component: 'LAYOUT',
|
||||
redirect: '/permission/front/page',
|
||||
meta: {
|
||||
icon: 'carbon:user-role',
|
||||
title: 'routes.demo.permission.permission',
|
||||
},
|
||||
children: [backRoute],
|
||||
};
|
||||
|
||||
const levelRoute = {
|
||||
path: '/level',
|
||||
name: 'Level',
|
||||
component: 'LAYOUT',
|
||||
redirect: '/level/menu1/menu1-1',
|
||||
meta: {
|
||||
icon: 'carbon:user-role',
|
||||
title: 'routes.demo.level.level',
|
||||
},
|
||||
|
||||
children: [
|
||||
{
|
||||
path: 'menu1',
|
||||
name: 'Menu1Demo',
|
||||
meta: {
|
||||
title: 'Menu1',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
meta: {
|
||||
title: 'Menu1-1',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'menu1-1-1',
|
||||
name: 'Menu111Demo',
|
||||
component: '/demo/level/Menu111',
|
||||
meta: {
|
||||
title: 'Menu111',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
name: 'Menu12Demo',
|
||||
component: '/demo/level/Menu12',
|
||||
meta: {
|
||||
title: 'Menu1-2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'menu2',
|
||||
name: 'Menu2Demo',
|
||||
component: '/demo/level/Menu2',
|
||||
meta: {
|
||||
title: 'Menu2',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const sysRoute = {
|
||||
path: '/system',
|
||||
name: 'System',
|
||||
component: 'LAYOUT',
|
||||
redirect: '/system/account',
|
||||
meta: {
|
||||
icon: 'ion:settings-outline',
|
||||
title: 'routes.demo.system.moduleName',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'account',
|
||||
name: 'AccountManagement',
|
||||
meta: {
|
||||
title: 'routes.demo.system.account',
|
||||
ignoreKeepAlive: true,
|
||||
},
|
||||
component: '/demo/system/account/index',
|
||||
},
|
||||
{
|
||||
path: 'account_detail/:id',
|
||||
name: 'AccountDetail',
|
||||
meta: {
|
||||
hideMenu: true,
|
||||
title: 'routes.demo.system.account_detail',
|
||||
ignoreKeepAlive: true,
|
||||
showMenu: false,
|
||||
currentActiveMenu: '/system/account',
|
||||
},
|
||||
component: '/demo/system/account/AccountDetail',
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
name: 'RoleManagement',
|
||||
meta: {
|
||||
title: 'routes.demo.system.role',
|
||||
ignoreKeepAlive: true,
|
||||
},
|
||||
component: '/demo/system/role/index',
|
||||
},
|
||||
|
||||
{
|
||||
path: 'menu',
|
||||
name: 'MenuManagement',
|
||||
meta: {
|
||||
title: 'routes.demo.system.menu',
|
||||
ignoreKeepAlive: true,
|
||||
},
|
||||
component: '/demo/system/menu/index',
|
||||
},
|
||||
{
|
||||
path: 'dept',
|
||||
name: 'DeptManagement',
|
||||
meta: {
|
||||
title: 'routes.demo.system.dept',
|
||||
ignoreKeepAlive: true,
|
||||
},
|
||||
component: '/demo/system/dept/index',
|
||||
},
|
||||
{
|
||||
path: 'changePassword',
|
||||
name: 'ChangePassword',
|
||||
meta: {
|
||||
title: 'routes.demo.system.password',
|
||||
ignoreKeepAlive: true,
|
||||
},
|
||||
component: '/demo/system/password/index',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const linkRoute = {
|
||||
path: '/link',
|
||||
name: 'Link',
|
||||
component: 'LAYOUT',
|
||||
meta: {
|
||||
icon: 'ion:tv-outline',
|
||||
title: 'routes.demo.iframe.frame',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'doc',
|
||||
name: 'Doc',
|
||||
meta: {
|
||||
title: 'routes.demo.iframe.doc',
|
||||
frameSrc: 'https://doc.vvbin.cn/',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'https://doc.vvbin.cn/',
|
||||
name: 'DocExternal',
|
||||
component: 'LAYOUT',
|
||||
meta: {
|
||||
title: 'routes.demo.iframe.docExternal',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/basic-api/getMenuList',
|
||||
timeout: 1000,
|
||||
method: 'get',
|
||||
response: (request: requestParams) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) {
|
||||
return resultError('Invalid token!');
|
||||
}
|
||||
const checkUser = createFakeUserList().find((item) => item.token === token);
|
||||
if (!checkUser) {
|
||||
return resultError('Invalid user token!');
|
||||
}
|
||||
const id = checkUser.userId;
|
||||
let menu: Object[];
|
||||
switch (id) {
|
||||
case '1':
|
||||
dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[0].path;
|
||||
menu = [dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute];
|
||||
break;
|
||||
case '2':
|
||||
dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[1].path;
|
||||
menu = [dashboardRoute, authRoute, levelRoute, linkRoute];
|
||||
break;
|
||||
default:
|
||||
menu = [];
|
||||
}
|
||||
|
||||
return resultSuccess(menu);
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
122
mock/sys/user.ts
Normal file
122
mock/sys/user.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util';
|
||||
|
||||
export function createFakeUserList() {
|
||||
return [
|
||||
{
|
||||
userId: '1',
|
||||
username: 'vben',
|
||||
realName: 'Vben Admin',
|
||||
avatar: '',
|
||||
desc: 'manager',
|
||||
password: '123456',
|
||||
token: 'fakeToken1',
|
||||
homePath: '/dashboard/analysis',
|
||||
roles: [
|
||||
{
|
||||
roleName: 'Super Admin',
|
||||
value: 'super',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
userId: '2',
|
||||
username: 'test',
|
||||
password: '123456',
|
||||
realName: 'test user',
|
||||
avatar: '',
|
||||
desc: 'tester',
|
||||
token: 'fakeToken2',
|
||||
homePath: '/dashboard/workbench',
|
||||
roles: [
|
||||
{
|
||||
roleName: 'Tester',
|
||||
value: 'test',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const fakeCodeList: any = {
|
||||
'1': ['1000', '3000', '5000'],
|
||||
|
||||
'2': ['2000', '4000', '6000'],
|
||||
};
|
||||
export default [
|
||||
// mock user login
|
||||
{
|
||||
url: '/basic-api/login',
|
||||
timeout: 200,
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
const { username, password } = body;
|
||||
const checkUser = createFakeUserList().find(
|
||||
(item) => item.username === username && password === item.password,
|
||||
);
|
||||
if (!checkUser) {
|
||||
return resultError('Incorrect account or password!');
|
||||
}
|
||||
const { userId, username: _username, token, realName, desc, roles } = checkUser;
|
||||
return resultSuccess({
|
||||
roles,
|
||||
userId,
|
||||
username: _username,
|
||||
token,
|
||||
realName,
|
||||
desc,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/getUserInfo',
|
||||
method: 'get',
|
||||
response: (request: requestParams) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) return resultError('Invalid token');
|
||||
const checkUser = createFakeUserList().find((item) => item.token === token);
|
||||
if (!checkUser) {
|
||||
return resultError('The corresponding user information was not obtained!');
|
||||
}
|
||||
return resultSuccess(checkUser);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/getPermCode',
|
||||
timeout: 200,
|
||||
method: 'get',
|
||||
response: (request: requestParams) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) return resultError('Invalid token');
|
||||
const checkUser = createFakeUserList().find((item) => item.token === token);
|
||||
if (!checkUser) {
|
||||
return resultError('Invalid token!');
|
||||
}
|
||||
const codeList = fakeCodeList[checkUser.userId];
|
||||
|
||||
return resultSuccess(codeList);
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/logout',
|
||||
timeout: 200,
|
||||
method: 'get',
|
||||
response: (request: requestParams) => {
|
||||
const token = getRequestToken(request);
|
||||
if (!token) return resultError('Invalid token');
|
||||
const checkUser = createFakeUserList().find((item) => item.token === token);
|
||||
if (!checkUser) {
|
||||
return resultError('Invalid token!');
|
||||
}
|
||||
return resultSuccess(undefined, { message: 'Token has been destroyed' });
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/basic-api/testRetry',
|
||||
statusCode: 405,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return resultError('Error!');
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
35
nginx.conf
Normal file
35
nginx.conf
Normal file
@ -0,0 +1,35 @@
|
||||
#user nobody;
|
||||
worker_processes 1;
|
||||
|
||||
#error_log logs/error.log;
|
||||
#error_log logs/error.log notice;
|
||||
#error_log logs/error.log info;
|
||||
|
||||
#pid logs/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
default_type application/octet-stream;
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
root /usr/share/nginx/html/dist;
|
||||
try_files $uri $uri/ /index.html;
|
||||
index index.html;
|
||||
# Enable CORS
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
|
||||
if ($request_method = 'OPTIONS') {
|
||||
add_header 'Access-Control-Max-Age' 1728000;
|
||||
add_header 'Content-Type' 'text/plain charset=UTF-8';
|
||||
add_header 'Content-Length' 0;
|
||||
return 204;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
146
package.json
Normal file
146
package.json
Normal file
@ -0,0 +1,146 @@
|
||||
{
|
||||
"name": "vben-admin",
|
||||
"version": "2.10.1.build0307",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
"name": "vben",
|
||||
"email": "anncwb@126.com",
|
||||
"url": "https://github.com/anncwb"
|
||||
},
|
||||
"scripts": {
|
||||
"bootstrap": "pnpm install",
|
||||
"build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build && node embedded.js",
|
||||
"build:analyze": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode analyze",
|
||||
"build:docker": "vite build --mode docker",
|
||||
"build:no-cache": "pnpm store prune && npm run build",
|
||||
"build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode test",
|
||||
"commit": "czg",
|
||||
"dev": "pnpm vite",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"postinstall": "turbo run stub",
|
||||
"lint": "turbo run lint",
|
||||
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
||||
"lint:prettier": "prettier --write .",
|
||||
"lint:stylelint": "stylelint \"**/*.{vue,css,less,scss}\" --fix --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"preview": "npm run build && vite preview",
|
||||
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
|
||||
"serve": "npm run dev",
|
||||
"test:gzip": "npx http-server dist --cors --gzip -c-1",
|
||||
"type:check": "vue-tsc --noEmit --skipLibCheck"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
|
||||
"prettier --write--parser json"
|
||||
],
|
||||
"package.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.vue": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"stylelint --fix"
|
||||
],
|
||||
"*.{scss,less,styl,html}": [
|
||||
"prettier --write",
|
||||
"stylelint --fix"
|
||||
],
|
||||
"*.md": [
|
||||
"prettier --write"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-git"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@iconify/iconify": "^3.1.1",
|
||||
"@vben/hooks": "workspace:*",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"@vueuse/shared": "^10.2.1",
|
||||
"@zxcvbn-ts/core": "^3.0.2",
|
||||
"ant-design-vue": "^4.0.3",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^5.65.12",
|
||||
"cropperjs": "^1.5.13",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.9",
|
||||
"echarts": "^5.4.2",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"pinia": "2.1.4",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"showdown": "^2.1.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-json-pretty": "^2.2.4",
|
||||
"vue-router": "^4.2.3",
|
||||
"vue-types": "^5.1.0",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vxe-table": "^4.4.5",
|
||||
"vxe-table-plugin-export-xlsx": "^3.0.4",
|
||||
"xe-utils": "^3.5.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.6.6",
|
||||
"@commitlint/config-conventional": "^17.6.6",
|
||||
"@iconify/json": "^2.2.87",
|
||||
"@purge-icons/generated": "^0.9.0",
|
||||
"@types/codemirror": "^5.60.8",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/intro.js": "^5.1.1",
|
||||
"@types/lodash-es": "^4.17.7",
|
||||
"@types/lodash.get": "^4.4.7",
|
||||
"@types/mockjs": "^1.0.7",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/qrcode": "^1.5.1",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/showdown": "^2.0.1",
|
||||
"@types/sortablejs": "^1.15.1",
|
||||
"@vben/eslint-config": "workspace:*",
|
||||
"@vben/stylelint-config": "workspace:*",
|
||||
"@vben/ts-config": "workspace:*",
|
||||
"@vben/types": "workspace:*",
|
||||
"@vben/vite-config": "workspace:*",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@vue/test-utils": "^2.4.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"cz-git": "^1.6.1",
|
||||
"czg": "^1.6.1",
|
||||
"lint-staged": "13.2.3",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-packagejson": "^2.4.4",
|
||||
"rimraf": "^5.0.1",
|
||||
"turbo": "^1.10.7",
|
||||
"typescript": "^5.1.6",
|
||||
"unbuild": "^1.2.1",
|
||||
"vite": "^4.4.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vue-tsc": "^1.8.4"
|
||||
},
|
||||
"packageManager": "pnpm@9.0.6",
|
||||
"engines": {
|
||||
"node": ">=16.15.1",
|
||||
"pnpm": ">=8.1.0"
|
||||
}
|
||||
}
|
0
packages/.gitkeep
Normal file
0
packages/.gitkeep
Normal file
4
packages/hooks/.eslintrc.js
Normal file
4
packages/hooks/.eslintrc.js
Normal file
@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@vben/eslint-config/strict'],
|
||||
};
|
10
packages/hooks/build.config.ts
Normal file
10
packages/hooks/build.config.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
entries: ['src/index'],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
},
|
||||
});
|
38
packages/hooks/package.json
Normal file
38
packages/hooks/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "@vben/hooks",
|
||||
"version": "1.0.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": {
|
||||
"url": "https://github.com/vbenjs/vue-vben-admin/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "packages/hooks"
|
||||
},
|
||||
"license": "MIT",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"module": "./src/index.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"//build": "pnpm unbuild",
|
||||
"//stub": "pnpm unbuild --stub",
|
||||
"clean": "pnpm rimraf .turbo node_modules dist",
|
||||
"lint": "pnpm eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^10.2.1",
|
||||
"vue": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vben/types": "workspace:*"
|
||||
}
|
||||
}
|
6
packages/hooks/src/index.ts
Normal file
6
packages/hooks/src/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './onMountedOrActivated';
|
||||
export * from './useAttrs';
|
||||
export * from './useRefs';
|
||||
export * from './useScrollTo';
|
||||
export * from './useWindowSizeFn';
|
||||
export { useTimeoutFn } from '@vueuse/core';
|
25
packages/hooks/src/onMountedOrActivated.ts
Normal file
25
packages/hooks/src/onMountedOrActivated.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { type AnyFunction } from '@vben/types';
|
||||
import { nextTick, onActivated, onMounted } from 'vue';
|
||||
|
||||
/**
|
||||
* 在 OnMounted 或者 OnActivated 时触发
|
||||
* @param hook 任何函数(包括异步函数)
|
||||
*/
|
||||
function onMountedOrActivated(hook: AnyFunction) {
|
||||
let mounted: boolean;
|
||||
|
||||
onMounted(() => {
|
||||
hook();
|
||||
nextTick(() => {
|
||||
mounted = true;
|
||||
});
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
if (mounted) {
|
||||
hook();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { onMountedOrActivated };
|
43
packages/hooks/src/useAttrs.ts
Normal file
43
packages/hooks/src/useAttrs.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { type Recordable } from '@vben/types';
|
||||
import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
|
||||
|
||||
interface UseAttrsOptions {
|
||||
excludeListeners?: boolean;
|
||||
excludeKeys?: string[];
|
||||
excludeDefaultKeys?: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
|
||||
const LISTENER_PREFIX = /^on[A-Z]/;
|
||||
|
||||
function entries<T>(obj: Recordable<T>): [string, T][] {
|
||||
return Object.keys(obj).map((key: string) => [key, obj[key]]);
|
||||
}
|
||||
|
||||
function useAttrs(options: UseAttrsOptions = {}): Recordable<any> {
|
||||
const instance = getCurrentInstance();
|
||||
if (!instance) return {};
|
||||
|
||||
const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = options;
|
||||
const attrs = shallowRef({});
|
||||
const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
|
||||
|
||||
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
|
||||
instance.attrs = reactive(instance.attrs);
|
||||
|
||||
watchEffect(() => {
|
||||
const res = entries(instance.attrs).reduce((acm, [key, val]) => {
|
||||
if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) {
|
||||
acm[key] = val;
|
||||
}
|
||||
|
||||
return acm;
|
||||
}, {} as Recordable<any>);
|
||||
|
||||
attrs.value = res;
|
||||
});
|
||||
|
||||
return attrs;
|
||||
}
|
||||
|
||||
export { useAttrs, type UseAttrsOptions };
|
24
packages/hooks/src/useRefs.ts
Normal file
24
packages/hooks/src/useRefs.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import type { ComponentPublicInstance, Ref } from 'vue';
|
||||
import { onBeforeUpdate, shallowRef } from 'vue';
|
||||
|
||||
function useRefs<T = HTMLElement>(): {
|
||||
refs: Ref<T[]>;
|
||||
setRefs: (index: number) => (el: Element | ComponentPublicInstance | null) => void;
|
||||
} {
|
||||
const refs = shallowRef([]) as Ref<T[]>;
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
refs.value = [];
|
||||
});
|
||||
|
||||
const setRefs = (index: number) => (el: Element | ComponentPublicInstance | null) => {
|
||||
refs.value[index] = el as T;
|
||||
};
|
||||
|
||||
return {
|
||||
refs,
|
||||
setRefs,
|
||||
};
|
||||
}
|
||||
|
||||
export { useRefs };
|
60
packages/hooks/src/useScrollTo.ts
Normal file
60
packages/hooks/src/useScrollTo.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { shallowRef, unref } from 'vue';
|
||||
|
||||
interface UseScrollToOptions {
|
||||
el: any;
|
||||
to: number;
|
||||
duration?: number;
|
||||
callback?: () => any;
|
||||
}
|
||||
|
||||
function easeInOutQuad(t: number, b: number, c: number, d: number) {
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (c / 2) * t * t + b;
|
||||
}
|
||||
t--;
|
||||
return (-c / 2) * (t * (t - 2) - 1) + b;
|
||||
}
|
||||
|
||||
function move(el: HTMLElement, amount: number) {
|
||||
el.scrollTop = amount;
|
||||
}
|
||||
|
||||
const position = (el: HTMLElement) => {
|
||||
return el.scrollTop;
|
||||
};
|
||||
function useScrollTo({ el, to, duration = 500, callback }: UseScrollToOptions) {
|
||||
const isActiveRef = shallowRef(false);
|
||||
const start = position(el);
|
||||
const change = to - start;
|
||||
const increment = 20;
|
||||
let currentTime = 0;
|
||||
|
||||
const animateScroll = function () {
|
||||
if (!unref(isActiveRef)) {
|
||||
return;
|
||||
}
|
||||
currentTime += increment;
|
||||
const val = easeInOutQuad(currentTime, start, change, duration);
|
||||
move(el, val);
|
||||
if (currentTime < duration && unref(isActiveRef)) {
|
||||
requestAnimationFrame(animateScroll);
|
||||
} else {
|
||||
if (callback && typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
const run = () => {
|
||||
isActiveRef.value = true;
|
||||
animateScroll();
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
isActiveRef.value = false;
|
||||
};
|
||||
|
||||
return { start: run, stop };
|
||||
}
|
||||
|
||||
export { useScrollTo, type UseScrollToOptions };
|
40
packages/hooks/src/useWindowSizeFn.ts
Normal file
40
packages/hooks/src/useWindowSizeFn.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { type AnyFunction } from '@vben/types';
|
||||
import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core';
|
||||
|
||||
interface UseWindowSizeOptions {
|
||||
wait?: number;
|
||||
once?: boolean;
|
||||
immediate?: boolean;
|
||||
listenerOptions?: AddEventListenerOptions | boolean;
|
||||
}
|
||||
|
||||
function useWindowSizeFn(fn: AnyFunction, options: UseWindowSizeOptions = {}) {
|
||||
const { wait = 150, immediate } = options;
|
||||
let handler = () => {
|
||||
fn();
|
||||
};
|
||||
const handleSize = useDebounceFn(handler, wait);
|
||||
handler = handleSize;
|
||||
|
||||
const start = () => {
|
||||
if (immediate) {
|
||||
handler();
|
||||
}
|
||||
window.addEventListener('resize', handler);
|
||||
};
|
||||
|
||||
const stop = () => {
|
||||
window.removeEventListener('resize', handler);
|
||||
};
|
||||
|
||||
tryOnMounted(() => {
|
||||
start();
|
||||
});
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
stop();
|
||||
});
|
||||
return { start, stop };
|
||||
}
|
||||
|
||||
export { useWindowSizeFn, type UseWindowSizeOptions };
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user