chore(admin): 精简后台模板并隐藏未接入模块

This commit is contained in:
sunlei 2026-05-17 12:32:25 +08:00
parent 1f3f32fd94
commit 3e5a60089b
56 changed files with 189 additions and 5000 deletions

View File

@ -1,5 +0,0 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

View File

@ -1,18 +0,0 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": [
"@changesets/changelog-github",
{ "repo": "vbenjs/vue-vben-admin" }
],
"commit": false,
"fixed": [["@vben-core/*", "@vben/*"]],
"snapshot": {
"prereleaseTemplate": "{tag}-{datetime}"
},
"privatePackages": { "version": true, "tag": true },
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

View File

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

View File

@ -1,6 +0,0 @@
ports:
- port: 5555
onOpen: open-preview
tasks:
- init: npm i -g corepack && pnpm install
command: pnpm run dev:play

View File

@ -1 +1 @@
node scripts/validate-commit-msg.mjs "$1" node internal/commit/validate-commit-msg.mjs "$1"

View File

@ -1,157 +0,0 @@
<div align="center">
<a href="https://github.com/anncwb/vue-vben-admin">
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
</a>
<br>
<br>
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**日本語** | [English](./README.md) | [中文](./README.zh-CN.md)
## 紹介
Vue Vben Adminは、最新の`vue3`、`vite`、`TypeScript`などの主流技術を使用して開発された、無料でオープンソースの中・後端テンプレートです。すぐに使える中・後端のフロントエンドソリューションとして、学習の参考にもなります。
## アップグレード通知
これは最新バージョン `5.0` であり、以前のバージョンとは互換性がありません。新しいプロジェクトを開始する場合は、最新バージョンを使用することをお勧めします。古いバージョンを表示したい場合は、[v2ブランチ](https://github.com/vbenjs/vue-vben-admin/tree/v2)を使用してください。
## 特徴
- **最新技術スタック**Vue 3やViteなどの最先端フロントエンド技術で開発
- **TypeScript**アプリケーション規模のJavaScriptのための言語
- **テーマ**:複数のテーマカラーが利用可能で、カスタマイズオプションも豊富
- **国際化**:完全な内蔵国際化サポート
- **権限管理**:動的ルートベースの権限生成ソリューションを内蔵
## プレビュー
- [Vben Admin](https://vben.pro/) - フルバージョンの中国語サイト
テストアカウントvben/123456
<div 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">
</div>
### Gitpodを使用
GitpodGitHub用の無料オンライン開発環境でプロジェクトを開き、すぐにコーディングを開始します。
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin)
## ドキュメント
[ドキュメント](https://doc.vben.pro/)
## インストールと使用
1. プロジェクトコードを取得
```bash
git clone https://github.com/vbenjs/vue-vben-admin.git
```
2. 依存関係のインストール
```bash
cd vue-vben-admin
npm i -g corepack
pnpm install
```
3. 実行
```bash
pnpm dev
```
4. ビルド
```bash
pnpm build
```
## 変更ログ
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## 貢献方法
ご参加をお待ちしております![Issueを提出](https://github.com/anncwb/vue-vben-admin/issues/new/choose)するか、Pull Requestを送信してください。
**Pull Request プロセス:**
1. コードをフォーク
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` 問題/バグの修正
- `style` コードスタイルに関連し、実行結果に影響しない
- `perf` 最適化/パフォーマンス向上
- `refactor` リファクタリング
- `revert` 変更の取り消し
- `test` テスト関連
- `docs` ドキュメント/注釈
- `chore` 依存関係の更新/スキャフォールディング設定の変更など
- `ci` 継続的インテグレーション
- `types` 型定義ファイルの変更
## ブラウザサポート
ローカル開発には `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>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 |
| :-: | :-: | :-: | :-: |
| 最新2バージョン | 最新2バージョン | 最新2バージョン | 最新2バージョン |
## メンテナー
[@Vben](https://github.com/anncwb)
## スター歴史
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## 寄付
このプロジェクトが役に立つと思われた場合、作者にコーヒーを一杯おごってサポートを示すことができます!
![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
## 貢献者
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
</a>
## Discord
- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions)
## ライセンス
[MIT © Vben-2020](./LICENSE)

198
README.md
View File

@ -1,157 +1,67 @@
<div align="center"> # KT Template Admin
<a href="https://github.com/anncwb/vue-vben-admin">
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
</a>
<br>
<br>
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE) `kt-template-admin` 是 KT 后台管理端项目,基于 Vben 5.6.0 精简后只保留 `antdv-next` 应用,接口统一接入 `kt-template-online-api`,生产发布走 Jenkins 静态构建和 Nginx 反向代理。
<h1>Vue Vben Admin</h1> ## 项目结构
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) [![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml) [![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml) [![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml) [![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml) ```text
apps/web-antdv-next 后台管理端入口
**English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md) internal 构建、Vite、Lint 等内部配置包
packages Vben 运行时依赖的核心包和组件包
## Introduction deploy/nginx-admin.conf Admin 静态站点和后端 /api 反向代理配置
Jenkinsfile Jenkins 静态发布流水线
Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite`, `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.
## Upgrade Notice
This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2).
## Features
- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite
- **TypeScript**: A language for application-scale JavaScript
- **Themes**: Multiple theme colors available with customizable options
- **Internationalization**: Comprehensive built-in internationalization support
- **Permissions**: Built-in solution for dynamic route-based permission generation
## Preview
- [Vben Admin](https://vben.pro/) - Full version Chinese site
Test Account: vben/123456
<div 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">
</div>
### Use Gitpod
Open the project in Gitpod (free online dev environment for GitHub) and start coding immediately.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin)
## Documentation
[Document](https://doc.vben.pro/)
## Install and Use
1. Get the project code
```bash
git clone https://github.com/vbenjs/vue-vben-admin.git
``` ```
2. Install dependencies ## 环境要求
- Node.js `22.22.0`
- pnpm `10.28.2`
建议通过 Corepack 固定 pnpm 版本:
```bash
corepack enable
corepack prepare pnpm@10.28.2 --activate
```
## 常用命令
```bash ```bash
cd vue-vben-admin
npm i -g corepack
pnpm install pnpm install
pnpm run dev
pnpm run verify:commit
pnpm run build:antdv-next
``` ```
3. Run ## 环境变量
```bash 本地开发和 Jenkins 构建主要使用:
pnpm dev
- `VITE_GLOB_API_URL`:后端 API 前缀,本地和生产默认使用 `/api`
- `VITE_BASE`Vite base默认 `/`
- `VITE_ROUTER_HISTORY`:路由模式,可选 `hash``html5`
- `VITE_COMPRESS`:构建压缩方式,可选 `none`、`gzip`、`brotli`
真实环境变量不提交,示例配置以 `.env.example` 为准。
## 部署说明
Jenkins 使用 `Jenkinsfile` 执行:
1. 安装依赖
2. `pnpm run verify:commit`
3. `pnpm run build:antdv-next`
4. 将 `apps/web-antdv-next/dist` 原子发布到 Nginx 挂载的 Admin 静态目录
Nginx 配置见 `deploy/nginx-admin.conf`,默认监听 `5999`,静态根目录为 `/usr/share/nginx/html/admin`,并将浏览器侧 `/api/*` 转发到后端 `192.168.31.224:48085`。配置保留 gzip、静态资源长缓存、入口 HTML 不缓存和 SPA 回退。
## 提交规范
Husky 会在提交前执行 lint 和类型校验,并在 `commit-msg` 阶段校验提交信息格式:
```text
feat(admin): 增加后台菜单配置
fix(api): 修复登录态刷新
``` ```
4. Build 要求使用英文类型前缀,描述部分包含中文。
```bash
pnpm build
```
## Change Log
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## How to Contribute
You are very welcome to join! [Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) or submit a Pull Request.
**Pull Request Process:**
1. Fork the code
2. Create your 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.
- `ci` Continuous integration
- `types` Type definition file changes
## 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>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 |
| :-: | :-: | :-: | :-: |
| last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## Maintainer
[@Vben](https://github.com/anncwb)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## Donate
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
## Contributors
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
</a>
## Discord
- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions)
## License
[MIT © Vben-2020](./LICENSE)

View File

@ -1,157 +0,0 @@
<div align="center">
<a href="https://github.com/anncwb/vue-vben-admin">
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
</a>
<br>
<br>
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**中文** | [English](./README.md) | [日本語](./README.ja-JP.md)
## 简介
Vue Vben Admin 是 Vue Vben Admin 的升级版本。作为一个免费开源的中后台模板,它采用了最新的 Vue 3、Vite、TypeScript 等主流技术开发,开箱即用,可用于中后台前端开发,也适合学习参考。
## 升级提示
该版本为最新版本 `5.0`,与其他版本不兼容,如果你是新项目,建议使用最新版本。如果你想查看旧版本,请使用 [v2 分支](https://github.com/vbenjs/vue-vben-admin/tree/v2)
## 特性
- **最新技术栈**:使用 Vue3/vite 等前端前沿技术开发
- **TypeScript**:应用程序级 JavaScript 的语言
- **主题**:提供多套主题色彩,可配置自定义主题
- **国际化**:内置完善的国际化方案
- **权限**:内置完善的动态路由权限生成方案
## 预览
- [Vben Admin](https://vben.pro/) - 完整版中文站点
测试账号vben/123456
<div 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">
</div>
### 使用 Gitpod
在 Gitpod适用于 GitHub 的免费在线开发环境)中打开项目,并立即开始编码。
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin)
## 文档
[文档地址](https://doc.vben.pro/)
## 安装使用
1. 获取项目代码
```bash
git clone https://github.com/vbenjs/vue-vben-admin.git
```
2. 安装依赖
```bash
cd vue-vben-admin
npm i -g corepack
pnpm install
```
3. 运行
```bash
pnpm dev
```
4. 打包
```bash
pnpm build
```
## 更新日志
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## 如何贡献
非常欢迎你的加入![提一个 Issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 或者提交一个 Pull Request。
**Pull Request 流程:**
1. Fork 代码
2. 创建自己的分支:`git checkout -b feature/xxxx`
3. 提交你的修改:`git commit -am 'feat(function): add xxxxx'`
4. 推送您的分支:`git push origin feature/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` 依赖更新/脚手架配置修改等
- `ci` 持续集成
- `types` 类型定义文件更改
## 浏览器支持
本地开发推荐使用 `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>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 |
| :-: | :-: | :-: | :-: |
| last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## 维护者
[@Vben](https://github.com/anncwb)
## Star 历史
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## 捐赠
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
## 贡献者
<a href="https://openomy.app/github/vbenjs/vue-vben-admin" target="_blank" style="display: block; width: 100%;" align="center">
<img src="https://openomy.app/svg?repo=vbenjs/vue-vben-admin&chart=bubble&latestMonth=3" target="_blank" alt="Contribution Leaderboard" style="display: block; width: 100%;" />
</a>
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img alt="Contributors" src="https://contrib.rocks/image?repo=vbenjs/vue-vben-admin" />
</a>
## Discord
- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions)
## 许可证
[MIT © Vben-2020](./LICENSE)

View File

@ -2,9 +2,52 @@ import type { RouteRecordStringComponent } from '@vben/types';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
const SUPPORTED_ADMIN_MENU_NAMES = new Set([
'System',
'SystemDept',
'SystemDeptCreate',
'SystemDeptDelete',
'SystemDeptEdit',
'SystemMenu',
'SystemMenuCreate',
'SystemMenuDelete',
'SystemMenuEdit',
'SystemRole',
'SystemRoleCreate',
'SystemRoleDelete',
'SystemRoleEdit',
]);
export function isSupportedAdminMenuName(name?: null | string | symbol) {
return typeof name === 'string' && SUPPORTED_ADMIN_MENU_NAMES.has(name);
}
function filterSupportedAdminMenus(
menus: RouteRecordStringComponent[],
): RouteRecordStringComponent[] {
return menus
.map((menu) => {
const children = menu.children
? filterSupportedAdminMenus(menu.children)
: undefined;
return {
...menu,
...(children && children.length > 0 ? { children } : {}),
};
})
.filter(
(menu) => isSupportedAdminMenuName(menu.name) || !!menu.children?.length,
);
}
/** /**
* *
*/ */
export async function getAllMenusApi() { export async function getAllMenusApi() {
return requestClient.get<RouteRecordStringComponent[]>('/menu/all'); const menus =
await requestClient.get<RouteRecordStringComponent[]>('/menu/all');
// 只暴露当前后端真实接口已经支撑的后台菜单,模板演示入口等后续补接口后再放开。
return filterSupportedAdminMenus(menus);
} }

View File

@ -2,6 +2,8 @@ import type { Recordable } from '@vben/types';
import { requestClient } from '#/api/request'; import { requestClient } from '#/api/request';
import { isSupportedAdminMenuName } from '../core/menu';
export namespace SystemMenuApi { export namespace SystemMenuApi {
/** 徽标颜色集合 */ /** 徽标颜色集合 */
export const BadgeVariants = [ export const BadgeVariants = [
@ -90,13 +92,35 @@ export namespace SystemMenuApi {
} }
} }
function filterSupportedSystemMenus(
menus: SystemMenuApi.SystemMenu[],
): SystemMenuApi.SystemMenu[] {
return menus
.map((menu) => {
const children = menu.children
? filterSupportedSystemMenus(menu.children)
: undefined;
return {
...menu,
...(children && children.length > 0 ? { children } : {}),
};
})
.filter(
(menu) => isSupportedAdminMenuName(menu.name) || !!menu.children?.length,
);
}
/** /**
* *
*/ */
async function getMenuList() { async function getMenuList() {
return requestClient.get<Array<SystemMenuApi.SystemMenu>>( const menus =
'/system/menu/list', await requestClient.get<Array<SystemMenuApi.SystemMenu>>(
); '/system/menu/list',
);
return filterSupportedSystemMenus(menus);
} }
async function isMenuNameExists( async function isMenuNameExists(

View File

@ -1,24 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { NotificationItem } from '@vben/layouts'; import { computed, onBeforeMount, watch } from 'vue';
import { computed, onBeforeMount, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui'; import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks'; import { useWatermark } from '@vben/hooks';
import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons'; import { BasicLayout, LockScreen, UserDropdown } from '@vben/layouts';
import {
BasicLayout,
LockScreen,
Notification,
UserDropdown,
} from '@vben/layouts';
import { preferences } from '@vben/preferences'; import { preferences } from '@vben/preferences';
import { useAccessStore, useTabbarStore, useUserStore } from '@vben/stores'; import { useAccessStore, useTabbarStore, useUserStore } from '@vben/stores';
import { openWindow } from '@vben/utils';
import { $t } from '#/locales';
import { useAuthStore } from '#/store'; import { useAuthStore } from '#/store';
import LoginForm from '#/views/_core/authentication/login.vue'; import LoginForm from '#/views/_core/authentication/login.vue';
@ -35,104 +23,10 @@ setMenuList([
'close-all', 'close-all',
]); ]);
const notifications = ref<NotificationItem[]>([
{
id: 1,
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
date: '3小时前',
isRead: true,
message: '描述信息描述信息描述信息',
title: '收到了 14 份新周报',
},
{
id: 2,
avatar: 'https://avatar.vercel.sh/1',
date: '刚刚',
isRead: false,
message: '描述信息描述信息描述信息',
title: '朱偏右 回复了你',
},
{
id: 3,
avatar: 'https://avatar.vercel.sh/1',
date: '2024-01-01',
isRead: false,
message: '描述信息描述信息描述信息',
title: '曲丽丽 评论了你',
},
{
id: 4,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '代办提醒',
},
{
id: 5,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '跳转Workspace示例',
link: '/workspace',
},
{
id: 6,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '跳转外部链接示例',
link: 'https://doc.vben.pro',
},
]);
const router = useRouter();
const userStore = useUserStore(); const userStore = useUserStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark(); const { destroyWatermark, updateWatermark } = useWatermark();
const showDot = computed(() =>
notifications.value.some((item) => !item.isRead),
);
const menus = computed(() => [
{
handler: () => {
router.push({ name: 'Profile' });
},
icon: 'lucide:user',
text: $t('page.auth.profile'),
},
{
handler: () => {
openWindow(VBEN_DOC_URL, {
target: '_blank',
});
},
icon: BookOpenText,
text: $t('ui.widgets.document'),
},
{
handler: () => {
openWindow(VBEN_GITHUB_URL, {
target: '_blank',
});
},
icon: SvgGithubIcon,
text: 'GitHub',
},
{
handler: () => {
openWindow(`${VBEN_GITHUB_URL}/issues`, {
target: '_blank',
});
},
icon: CircleHelp,
text: $t('ui.widgets.qa'),
},
]);
const avatar = computed(() => { const avatar = computed(() => {
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar; return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
@ -142,25 +36,6 @@ async function handleLogout() {
await authStore.logout(false); await authStore.logout(false);
} }
function handleNoticeClear() {
notifications.value = [];
}
function markRead(id: number | string) {
const item = notifications.value.find((item) => item.id === id);
if (item) {
item.isRead = true;
}
}
function remove(id: number | string) {
notifications.value = notifications.value.filter((item) => item.id !== id);
}
function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true));
}
function handleClickLogo() {} function handleClickLogo() {}
watch( watch(
@ -199,24 +74,12 @@ onBeforeMount(() => {
<template #user-dropdown> <template #user-dropdown>
<UserDropdown <UserDropdown
:avatar :avatar
:menus :description="userStore.userInfo?.username"
:text="userStore.userInfo?.realName" :text="userStore.userInfo?.realName"
description="ann.vben@gmail.com"
tag-text="Pro"
trigger="both" trigger="both"
@logout="handleLogout" @logout="handleLogout"
/> />
</template> </template>
<template #notification>
<Notification
:dot="showDot"
:notifications="notifications"
@clear="handleNoticeClear"
@read="(item) => item.id && markRead(item.id)"
@remove="(item) => item.id && remove(item.id)"
@make-all="handleMakeAll"
/>
</template>
<template #extra> <template #extra>
<AuthenticationLoginExpiredModal <AuthenticationLoginExpiredModal
v-model:open="accessStore.loginExpired" v-model:open="accessStore.loginExpired"

View File

@ -8,7 +8,12 @@ import { defineOverridesPreferences } from '@vben/preferences';
export const overridesPreferences = defineOverridesPreferences({ export const overridesPreferences = defineOverridesPreferences({
// overrides // overrides
app: { app: {
accessMode: 'backend',
defaultHomePath: '/system/role',
enableRefreshToken: true, enableRefreshToken: true,
name: import.meta.env.VITE_APP_TITLE, name: import.meta.env.VITE_APP_TITLE,
}, },
widget: {
notification: false,
},
}); });

View File

@ -42,6 +42,7 @@ const componentKeys: string[] = Object.keys(
.map((v) => { .map((v) => {
const path = v.replace('../../views/', '/'); const path = v.replace('../../views/', '/');
return path.endsWith('.vue') ? path.slice(0, -4) : path; return path.endsWith('.vue') ? path.slice(0, -4) : path;
}); })
.filter((path) => path.startsWith('/system/'));
export { accessRoutes, componentKeys, coreRouteNames, routes }; export { accessRoutes, componentKeys, coreRouteNames, routes };

View File

@ -1,81 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"language": "en,en-US",
"allowCompoundWords": true,
"words": [
"acmr",
"antd",
"antdv",
"archiver",
"astro",
"axios",
"brotli",
"cascader",
"clsx",
"defu",
"demi",
"dotenv",
"echarts",
"ependencies",
"esbuild",
"esno",
"etag",
"execa",
"iconify",
"iconoir",
"intlify",
"isequal",
"jspm",
"lockb",
"lucide",
"minh",
"minw",
"mkdist",
"mockjs",
"naiveui",
"napi",
"nocheck",
"nolebase",
"noopener",
"noreferrer",
"nprogress",
"nuxt",
"pinia",
"prefixs",
"publint",
"qrcode",
"reka",
"rollup",
"shadcn",
"sonner",
"sortablejs",
"styl",
"taze",
"tdesign",
"ui-kit",
"uicons",
"unplugin",
"unref",
"vben",
"vbenjs",
"vite",
"vitejs",
"vitepress",
"vitest",
"vnode",
"vueuse",
"yxxx"
],
"ignorePaths": [
"**/*-dist/**",
"**/*.log",
"**/*.spec.ts",
"**/*.test.ts",
"**/__tests__/**",
"**/dist/**",
"**/icons/**",
"**/node_modules/**",
"pnpm-lock.yaml"
]
}

35
deploy/nginx-admin.conf Normal file
View File

@ -0,0 +1,35 @@
server {
listen 5999;
server_name _;
root /usr/share/nginx/html/admin;
index index.html;
gzip on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;
location = /index.html {
add_header Cache-Control "no-store";
try_files $uri =404;
}
location /api/ {
proxy_pass http://192.168.31.224:48085/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}

View File

@ -1,153 +0,0 @@
import { execSync } from 'node:child_process';
import { getPackagesSync } from '@vben/node-utils';
const { packages } = getPackagesSync();
const allowedScopes = [
...packages.map((pkg) => pkg.packageJson.name),
'project',
'style',
'lint',
'ci',
'dev',
'deploy',
'other',
];
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replaceAll(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '');
/**
* @type {import('cz-git').UserConfig}
*/
const userConfig = {
extends: ['@commitlint/config-conventional'],
plugins: ['commitlint-plugin-function-rules'],
prompt: {
/** @use `pnpm commit :f` */
alias: {
b: 'build: bump dependencies',
c: 'chore: update config',
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
},
allowCustomIssuePrefixs: false,
// scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
customScopesAlign: scopeComplete ? 'bottom' : 'top',
defaultScope: scopeComplete,
// English
typesAppend: [
{ name: 'workflow: workflow improvements', value: 'workflow' },
{ name: 'types: type definition file changes', value: 'types' },
],
// 中英文对照版
// messages: {
// type: '选择你要提交的类型 :',
// scope: '选择一个提交范围 (可选):',
// customScope: '请输入自定义的提交范围 :',
// subject: '填写简短精炼的变更描述 :\n',
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
// customFooterPrefixs: '输入自定义issue前缀 :',
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
// confirmCommit: '是否提交或修改commit ?',
// },
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复缺陷' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式' },
// { value: 'refactor', name: 'refactor: 代码重构' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
// { value: 'wip', name: 'wip: 正在开发中' },
// { value: 'workflow', name: 'workflow: 工作流程改进' },
// { value: 'types', name: 'types: 类型定义文件修改' },
// ],
// emptyScopesAlias: 'empty: 不填写',
// customScopesAlias: 'custom: 自定义',
},
rules: {
/**
* type[scope]: [function] description
*
* ^^^^^^^^^^^^^^ empty line.
* - Something here
*/
'body-leading-blank': [2, 'always'],
/**
* type[scope]: [function] description
*
* - something here
*
* ^^^^^^^^^^^^^^
*/
'footer-leading-blank': [1, 'always'],
/**
* type[scope]: [function] description
* ^^^^^
*/
'function-rules/scope-enum': [
2, // level: error
'always',
(parsed) => {
if (!parsed.scope || allowedScopes.includes(parsed.scope)) {
return [true];
}
return [false, `scope must be one of ${allowedScopes.join(', ')}`];
},
],
/**
* type[scope]: [function] description [No more than 108 characters]
* ^^^^^
*/
'header-max-length': [2, 'always', 108],
'scope-enum': [0],
'subject-case': [0],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
/**
* type[scope]: [function] description
* ^^^^
*/
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'types',
'release',
],
],
},
};
export default userConfig;

View File

@ -1,33 +0,0 @@
{
"name": "@vben/commitlint-config",
"version": "5.6.0",
"private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "internal/lint-configs/commitlint-config"
},
"license": "MIT",
"type": "module",
"files": [
"dist"
],
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": {
"import": "./index.mjs",
"default": "./index.mjs"
}
},
"dependencies": {
"@commitlint/cli": "catalog:",
"@commitlint/config-conventional": "catalog:",
"@vben/node-utils": "workspace:*",
"commitlint-plugin-function-rules": "catalog:",
"cz-git": "catalog:",
"czg": "catalog:"
}
}

View File

@ -27,7 +27,6 @@ export async function ignores(): Promise<Linter.Config[]> {
'**/.nuxt', '**/.nuxt',
'**/.next', '**/.next',
'**/.vercel', '**/.vercel',
'**/.changeset',
'**/.idea', '**/.idea',
'**/.cache', '**/.cache',
'**/.output', '**/.output',

View File

@ -48,7 +48,6 @@ export async function jsonc(): Promise<Linter.Config[]> {
}, },
sortTsconfig(), sortTsconfig(),
sortPackageJson(), sortPackageJson(),
sortCspellJson(),
]; ];
} }
@ -131,21 +130,6 @@ function sortPackageJson(): Linter.Config {
}; };
} }
function sortCspellJson(): Linter.Config {
return {
files: ['**/cspell.json', '**/.cspell.json'],
rules: {
'jsonc/sort-array-values': [
'error',
{
order: { type: 'asc' },
pathPattern: '^words$|^ignorePaths$',
},
],
},
};
}
function sortTsconfig(): Linter.Config { function sortTsconfig(): Linter.Config {
return { return {
files: [ files: [

View File

@ -45,10 +45,7 @@ export async function node(): Promise<Linter.Config[]> {
}, },
}, },
{ {
files: [ files: ['internal/**/*.?([cm])[jt]s?(x)'],
'scripts/**/*.?([cm])[jt]s?(x)',
'internal/**/*.?([cm])[jt]s?(x)',
],
rules: { rules: {
'n/prefer-global/process': 'off', 'n/prefer-global/process': 'off',
}, },

View File

@ -33,10 +33,7 @@ export async function unicorn(): Promise<Linter.Config[]> {
}, },
}, },
{ {
files: [ files: ['internal/**/*.?([cm])[jt]s?(x)'],
'scripts/**/*.?([cm])[jt]s?(x)',
'internal/**/*.?([cm])[jt]s?(x)',
],
rules: { rules: {
'unicorn/no-process-exit': 'off', 'unicorn/no-process-exit': 'off',
}, },

View File

@ -158,7 +158,7 @@ const customConfig: Linter.Config[] = [
}, },
}, },
{ {
files: ['internal/**/**', 'scripts/**/**'], files: ['internal/**/**'],
rules: { rules: {
'no-console': 'off', 'no-console': 'off',
}, },

View File

@ -28,7 +28,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@changesets/git": "catalog:",
"@manypkg/get-packages": "catalog:", "@manypkg/get-packages": "catalog:",
"chalk": "catalog:", "chalk": "catalog:",
"consola": "catalog:", "consola": "catalog:",

View File

@ -2,8 +2,6 @@ import path from 'node:path';
import { execa } from 'execa'; import { execa } from 'execa';
export * from '@changesets/git';
/** /**
* *
*/ */

View File

@ -2,7 +2,6 @@ export * from './constants';
export * from './date'; export * from './date';
export * from './fs'; export * from './fs';
export * from './git'; export * from './git';
export { getStagedFiles, add as gitAdd } from './git';
export { generatorContentHash } from './hash'; export { generatorContentHash } from './hash';
export * from './monorepo'; export * from './monorepo';
export { toPosixPath } from './path'; export { toPosixPath } from './path';

View File

@ -1,83 +1,45 @@
{ {
"name": "vben-admin-monorepo", "name": "kt-template-admin",
"version": "5.5.9", "version": "1.0.0",
"private": true, "private": true,
"keywords": [
"monorepo",
"turbo",
"vben",
"vben admin",
"vben pro",
"vue",
"vue admin",
"vue vben admin",
"vue vben admin pro",
"vue3"
],
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": "vbenjs/vue-vben-admin.git",
"license": "MIT", "license": "MIT",
"author": {
"name": "vben",
"email": "ann.vben@gmail.com",
"url": "https://github.com/anncwb"
},
"type": "module", "type": "module",
"scripts": { "scripts": {
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build", "build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
"build:analyze": "turbo build:analyze", "build:analyze": "turbo build:analyze",
"build:antdv-next": "pnpm run build --filter=@vben/web-antdv-next", "build:antdv-next": "pnpm run build --filter=@vben/web-antdv-next",
"build:docker": "./scripts/deploy/build-local-docker-image.sh", "check": "pnpm run check:type",
"changeset": "pnpm exec changeset",
"check": "pnpm run check:circular && pnpm run check:dep && pnpm run check:type && pnpm check:cspell",
"check:circular": "vsh check-circular",
"check:cspell": "cspell lint **/*.ts **/README.md .changeset/*.md --no-progress",
"check:dep": "vsh check-dep",
"check:type": "turbo run typecheck", "check:type": "turbo run typecheck",
"clean": "node ./scripts/clean.mjs", "clean": "rimraf .turbo apps/*/dist apps/*/dist.zip packages/**/dist packages/**/dist.zip internal/**/dist internal/**/dist.zip",
"commit": "czg", "dev": "pnpm run dev:antdv-next",
"dev": "turbo-run dev",
"dev:antdv-next": "pnpm -F @vben/web-antdv-next run dev", "dev:antdv-next": "pnpm -F @vben/web-antdv-next run dev",
"format": "vsh lint --format", "format": "eslint apps/web-antdv-next internal --cache --fix && prettier . --write --cache --log-level warn && stylelint \"**/*.{vue,css,less,scss}\" --cache --fix",
"lint": "vsh lint", "lint": "eslint apps/web-antdv-next internal --cache && prettier . --ignore-unknown --check --cache && stylelint \"**/*.{vue,css,less,scss}\" --cache",
"lint:commit": "eslint apps/web-antdv-next scripts/validate-commit-msg.mjs --cache", "lint:commit": "eslint apps/web-antdv-next internal/commit/validate-commit-msg.mjs --cache",
"postinstall": "pnpm -r run stub --if-present", "postinstall": "pnpm -r run stub --if-present",
"prepare": "husky", "prepare": "husky",
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"preview": "turbo-run preview", "preview": "pnpm -F @vben/web-antdv-next run preview",
"publint": "vsh publint", "reinstall": "pnpm run clean && rimraf pnpm-lock.yaml && pnpm install",
"reinstall": "pnpm clean --del-lock && pnpm install",
"test:unit": "vitest run --dom",
"verify:commit": "pnpm run lint:commit && pnpm -F @vben/web-antdv-next run typecheck", "verify:commit": "pnpm run lint:commit && pnpm -F @vben/web-antdv-next run typecheck",
"test:e2e": "turbo run test:e2e", "test:e2e": "turbo run test:e2e"
"update:deps": "npx taze -r -w",
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile",
"catalog": "pnpx codemod pnpm/catalog"
}, },
"devDependencies": { "devDependencies": {
"@changesets/changelog-github": "catalog:",
"@changesets/cli": "catalog:",
"@playwright/test": "catalog:", "@playwright/test": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"@vben/commitlint-config": "workspace:*",
"@vben/eslint-config": "workspace:*", "@vben/eslint-config": "workspace:*",
"@vben/prettier-config": "workspace:*", "@vben/prettier-config": "workspace:*",
"@vben/stylelint-config": "workspace:*", "@vben/stylelint-config": "workspace:*",
"@vben/tailwind-config": "workspace:*", "@vben/tailwind-config": "workspace:*",
"@vben/tsconfig": "workspace:*", "@vben/tsconfig": "workspace:*",
"@vben/turbo-run": "workspace:*",
"@vben/vite-config": "workspace:*", "@vben/vite-config": "workspace:*",
"@vben/vsh": "workspace:*",
"@vitejs/plugin-vue": "catalog:", "@vitejs/plugin-vue": "catalog:",
"@vitejs/plugin-vue-jsx": "catalog:", "@vitejs/plugin-vue-jsx": "catalog:",
"@vue/test-utils": "catalog:", "@vue/test-utils": "catalog:",
"autoprefixer": "catalog:", "autoprefixer": "catalog:",
"cross-env": "catalog:", "cross-env": "catalog:",
"cspell": "catalog:",
"happy-dom": "catalog:", "happy-dom": "catalog:",
"husky": "catalog:", "husky": "catalog:",
"is-ci": "catalog:",
"playwright": "catalog:", "playwright": "catalog:",
"rimraf": "catalog:", "rimraf": "catalog:",
"tailwindcss": "catalog:", "tailwindcss": "catalog:",

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@ packages:
- packages/effects/* - packages/effects/*
- packages/business/* - packages/business/*
- apps/* - apps/*
- scripts/*
overrides: overrides:
'@ast-grep/napi': 'catalog:' '@ast-grep/napi': 'catalog:'
@ -21,12 +20,6 @@ overrides:
vue: 'catalog:' vue: 'catalog:'
catalog: catalog:
'@ast-grep/napi': ^0.39.9 '@ast-grep/napi': ^0.39.9
'@changesets/changelog-github': ^0.5.2
'@changesets/cli': ^2.29.8
'@changesets/git': ^3.0.4
'@clack/prompts': ^0.11.0
'@commitlint/cli': ^19.8.1
'@commitlint/config-conventional': ^19.8.1
'@ctrl/tinycolor': ^4.2.0 '@ctrl/tinycolor': ^4.2.0
'@eslint/js': ^9.39.2 '@eslint/js': ^9.39.2
'@faker-js/faker': ^9.9.0 '@faker-js/faker': ^9.9.0
@ -71,22 +64,15 @@ catalog:
autoprefixer: ^10.4.24 autoprefixer: ^10.4.24
axios: ^1.13.4 axios: ^1.13.4
axios-mock-adapter: ^2.1.0 axios-mock-adapter: ^2.1.0
cac: ^6.7.14
chalk: ^5.6.2 chalk: ^5.6.2
cheerio: ^1.2.0 cheerio: ^1.2.0
circular-dependency-scanner: ^2.3.0
class-variance-authority: ^0.7.1 class-variance-authority: ^0.7.1
clsx: ^2.1.1 clsx: ^2.1.1
commitlint-plugin-function-rules: ^4.3.1
consola: ^3.4.2 consola: ^3.4.2
cross-env: ^10.1.0 cross-env: ^10.1.0
cspell: ^9.6.4
cssnano: ^7.1.2 cssnano: ^7.1.2
cz-git: ^1.12.0
czg: ^1.12.0
dayjs: ^1.11.19 dayjs: ^1.11.19
defu: ^6.1.4 defu: ^6.1.4
depcheck: ^1.4.7
dotenv: ^16.6.1 dotenv: ^16.6.1
echarts: ^6.0.0 echarts: ^6.0.0
es-toolkit: ^1.44.0 es-toolkit: ^1.44.0
@ -117,7 +103,6 @@ catalog:
happy-dom: ^17.6.3 happy-dom: ^17.6.3
husky: ^9.1.7 husky: ^9.1.7
html-minifier-terser: ^7.2.0 html-minifier-terser: ^7.2.0
is-ci: ^4.1.0
jiti: ^2.6.1 jiti: ^2.6.1
json-bigint: ^1.0.0 json-bigint: ^1.0.0
jsonc-eslint-parser: ^2.4.2 jsonc-eslint-parser: ^2.4.2
@ -140,7 +125,6 @@ catalog:
postcss-scss: ^4.0.9 postcss-scss: ^4.0.9
prettier: ^3.8.1 prettier: ^3.8.1
prettier-plugin-tailwindcss: ^0.7.2 prettier-plugin-tailwindcss: ^0.7.2
publint: ^0.3.17
qrcode: ^1.5.4 qrcode: ^1.5.4
qs: ^6.14.1 qs: ^6.14.1
reka-ui: ^2.7.0 reka-ui: ^2.7.0

View File

@ -1,141 +0,0 @@
import { promises as fs } from 'node:fs';
import { join, normalize } from 'node:path';
const rootDir = process.cwd();
// 控制并发数量,避免创建过多的并发任务
const CONCURRENCY_LIMIT = 10;
// 需要跳过的目录,避免进入这些目录进行清理
const SKIP_DIRS = new Set(['.DS_Store', '.git', '.idea', '.vscode']);
/**
* 处理单个文件/目录项
* @param {string} currentDir - 当前目录路径
* @param {string} item - 文件/目录名
* @param {string[]} targets - 要删除的目标列表
* @param {number} _depth - 当前递归深度
* @returns {Promise<boolean>} - 是否需要进一步递归处理
*/
async function processItem(currentDir, item, targets, _depth) {
// 跳过特殊目录
if (SKIP_DIRS.has(item)) {
return false;
}
try {
const itemPath = normalize(join(currentDir, item));
if (targets.includes(item)) {
// 匹配到目标目录或文件时直接删除
await fs.rm(itemPath, { force: true, recursive: true });
console.log(`✅ Deleted: ${itemPath}`);
return false; // 已删除,无需递归
}
// 使用 readdir 的 withFileTypes 选项,避免额外的 lstat 调用
return true; // 可能需要递归,由调用方决定
} catch (error) {
// 更详细的错误信息
if (error.code === 'ENOENT') {
// 文件不存在,可能已被删除,这是正常情况
return false;
} else if (error.code === 'EPERM' || error.code === 'EACCES') {
console.error(`❌ Permission denied: ${item} in ${currentDir}`);
} else {
console.error(
`❌ Error handling item ${item} in ${currentDir}: ${error.message}`,
);
}
return false;
}
}
/**
* 递归查找并删除目标目录并发优化版本
* @param {string} currentDir - 当前遍历的目录路径
* @param {string[]} targets - 要删除的目标列表
* @param {number} depth - 当前递归深度避免过深递归
*/
async function cleanTargetsRecursively(currentDir, targets, depth = 0) {
// 限制递归深度,避免无限递归
if (depth > 10) {
console.warn(`Max recursion depth reached at: ${currentDir}`);
return;
}
let dirents;
try {
// 使用 withFileTypes 选项,一次性获取文件类型信息,避免后续 lstat 调用
dirents = await fs.readdir(currentDir, { withFileTypes: true });
} catch (error) {
// 如果无法读取目录,可能已被删除或权限不足
console.warn(`Cannot read directory ${currentDir}: ${error.message}`);
return;
}
// 分批处理,控制并发数量
for (let i = 0; i < dirents.length; i += CONCURRENCY_LIMIT) {
const batch = dirents.slice(i, i + CONCURRENCY_LIMIT);
const tasks = batch.map(async (dirent) => {
const item = dirent.name;
const shouldRecurse = await processItem(currentDir, item, targets, depth);
// 如果是目录且没有被删除,则递归处理
if (shouldRecurse && dirent.isDirectory()) {
const itemPath = normalize(join(currentDir, item));
return cleanTargetsRecursively(itemPath, targets, depth + 1);
}
return null;
});
// 并发执行当前批次的任务
const results = await Promise.allSettled(tasks);
// 检查是否有失败的任务(可选:用于调试)
const failedTasks = results.filter(
(result) => result.status === 'rejected',
);
if (failedTasks.length > 0) {
console.warn(
`${failedTasks.length} tasks failed in batch starting at index ${i} in directory: ${currentDir}`,
);
}
}
}
(async function startCleanup() {
// 要删除的目录及文件名称
const targets = ['node_modules', 'dist', '.turbo', 'dist.zip'];
const deleteLockFile = process.argv.includes('--del-lock');
const cleanupTargets = [...targets];
if (deleteLockFile) {
cleanupTargets.push('pnpm-lock.yaml');
}
console.log(
`🚀 Starting cleanup of targets: ${cleanupTargets.join(', ')} from root: ${rootDir}`,
);
const startTime = Date.now();
try {
// 先统计要删除的目标数量
console.log('📊 Scanning for cleanup targets...');
await cleanTargetsRecursively(rootDir, cleanupTargets);
const endTime = Date.now();
const duration = (endTime - startTime) / 1000;
console.log(
`✨ Cleanup process completed successfully in ${duration.toFixed(2)}s`,
);
} catch (error) {
console.error(`💥 Unexpected error during cleanup: ${error.message}`);
process.exit(1);
}
})();

View File

@ -1,37 +0,0 @@
FROM node:22-slim AS builder
# --max-old-space-size
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV NODE_OPTIONS=--max-old-space-size=8192
ENV TZ=Asia/Shanghai
RUN npm i -g corepack
WORKDIR /app
# copy package.json and pnpm-lock.yaml to workspace
COPY . /app
# 安装依赖
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build --filter=\!./docs
RUN echo "Builder Success 🎉"
FROM nginx:stable-alpine AS production
# 配置 nginx
RUN echo "types { application/javascript js mjs; }" > /etc/nginx/conf.d/mjs.conf \
&& rm -rf /etc/nginx/conf.d/default.conf
# 复制构建产物
COPY --from=builder /app/playground/dist /usr/share/nginx/html
# 复制 nginx 配置
COPY --from=builder /app/scripts/deploy/nginx.conf /etc/nginx/nginx.conf
EXPOSE 8080
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]

View File

@ -1,55 +0,0 @@
#!/bin/bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
LOG_FILE=${SCRIPT_DIR}/build-local-docker-image.log
ERROR=""
IMAGE_NAME="vben-admin-local"
function stop_and_remove_container() {
# Stop and remove the existing container
docker stop ${IMAGE_NAME} >/dev/null 2>&1
docker rm ${IMAGE_NAME} >/dev/null 2>&1
}
function remove_image() {
# Remove the existing image
docker rmi vben-admin-pro >/dev/null 2>&1
}
function install_dependencies() {
# Install all dependencies
cd ${SCRIPT_DIR}
pnpm install || ERROR="install_dependencies failed"
}
function build_image() {
# build docker
docker build ../../ -f Dockerfile -t ${IMAGE_NAME} || ERROR="build_image failed"
}
function log_message() {
if [[ ${ERROR} != "" ]];
then
>&2 echo "build failed, Please check build-local-docker-image.log for more details"
>&2 echo "ERROR: ${ERROR}"
exit 1
else
echo "docker image with tag '${IMAGE_NAME}' built sussessfully. Use below sample command to run the container"
echo ""
echo "docker run -d -p 8010:8080 --name ${IMAGE_NAME} ${IMAGE_NAME}"
fi
}
echo "Info: Stopping and removing existing container and image" | tee ${LOG_FILE}
stop_and_remove_container
remove_image
echo "Info: Installing dependencies" | tee -a ${LOG_FILE}
install_dependencies 1>> ${LOG_FILE} 2>> ${LOG_FILE}
if [[ ${ERROR} == "" ]]; then
echo "Info: Building docker image" | tee -a ${LOG_FILE}
build_image 1>> ${LOG_FILE} 2>> ${LOG_FILE}
fi
log_message | tee -a ${LOG_FILE}

View File

@ -1,75 +0,0 @@
#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;
types {
application/javascript js mjs;
text/css css;
text/html html;
}
sendfile on;
# tcp_nopush on;
#keepalive_timeout 0;
# keepalive_timeout 65;
# gzip on;
# gzip_buffers 32 16k;
# gzip_comp_level 6;
# gzip_min_length 1k;
# gzip_static on;
# gzip_types text/plain
# text/css
# application/javascript
# application/json
# application/x-javascript
# text/xml
# application/xml
# application/xml+rss
# text/javascript; #设置压缩的文件类型
# gzip_vary on;
server {
listen 8080;
server_name localhost;
location / {
root /usr/share/nginx/html;
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;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

View File

@ -1,59 +0,0 @@
# @vben/turbo-run
`turbo-run` 是一个命令行工具,允许你在多个包中并行运行命令。它提供了一个交互式的界面,让你可以选择要运行命令的包。
## 特性
- 🚀 交互式选择要运行的包
- 📦 支持 monorepo 项目结构
- 🔍 自动检测可用的命令
- 🎯 精确过滤目标包
## 安装
```bash
pnpm add -D @vben/turbo-run
```
## 使用方法
基本语法:
```bash
turbo-run [script]
```
例如,如果你想运行 `dev` 命令:
```bash
turbo-run dev
```
工具会自动检测哪些包有 `dev` 命令,并提供一个交互式界面让你选择要运行的包。
## 示例
假设你的项目中有以下包:
- `@vben/app`
- `@vben/admin`
- `@vben/website`
当你运行:
```bash
turbo-run dev
```
工具会:
1. 检测哪些包有 `dev` 命令
2. 显示一个交互式选择界面
3. 让你选择要运行命令的包
4. 使用 `pnpm --filter` 在选定的包中运行命令
## 注意事项
- 确保你的项目使用 pnpm 作为包管理器
- 确保目标包在 `package.json` 中定义了相应的脚本命令
- 该工具需要在 monorepo 项目的根目录下运行

View File

@ -1,3 +0,0 @@
#!/usr/bin/env node
import('../dist/index.mjs');

View File

@ -1,7 +0,0 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@ -1,29 +0,0 @@
{
"name": "@vben/turbo-run",
"version": "5.6.0",
"private": true,
"license": "MIT",
"type": "module",
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"bin": {
"turbo-run": "./bin/turbo-run.mjs"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
"dependencies": {
"@clack/prompts": "catalog:",
"@vben/node-utils": "workspace:*",
"cac": "catalog:"
}
}

View File

@ -1,29 +0,0 @@
import { colors, consola } from '@vben/node-utils';
import { cac } from 'cac';
import { run } from './run';
try {
const turboRun = cac('turbo-run');
turboRun
.command('[script]')
.usage(`Run turbo interactively.`)
.action(async (command: string) => {
run({ command });
});
// Invalid command
turboRun.on('command:*', () => {
consola.error(colors.red('Invalid command!'));
process.exit(1);
});
turboRun.usage('turbo-run');
turboRun.help();
turboRun.parse();
} catch (error) {
consola.error(error);
process.exit(1);
}

View File

@ -1,67 +0,0 @@
import { execaCommand, getPackages } from '@vben/node-utils';
import { cancel, isCancel, select } from '@clack/prompts';
interface RunOptions {
command?: string;
}
export async function run(options: RunOptions) {
const { command } = options;
if (!command) {
console.error('Please enter the command to run');
process.exit(1);
}
const { packages } = await getPackages();
// const appPkgs = await findApps(process.cwd(), packages);
// const websitePkg = packages.find(
// (item) => item.packageJson.name === '@vben/website',
// );
// 只显示有对应命令的包
const selectPkgs = packages.filter((pkg) => {
return (pkg?.packageJson as Record<string, any>)?.scripts?.[command];
});
let selectPkg: string | symbol;
if (selectPkgs.length > 1) {
selectPkg = await select<string>({
message: `Select the app you need to run [${command}]:`,
options: selectPkgs.map((item) => ({
label: item?.packageJson.name,
value: item?.packageJson.name,
})),
});
if (isCancel(selectPkg) || !selectPkg) {
cancel('👋 Has cancelled');
process.exit(0);
}
} else {
selectPkg = selectPkgs[0]?.packageJson?.name ?? '';
}
if (!selectPkg) {
console.error('No app found');
process.exit(1);
}
execaCommand(`pnpm --filter=${selectPkg} run ${command}`, {
stdio: 'inherit',
});
}
/**
* app包
* @param root
* @param packages
*/
// async function findApps(root: string, packages: Package[]) {
// // apps内的
// const appPackages = packages.filter((pkg) => {
// const viteConfigExists = fs.existsSync(join(pkg.dir, 'vite.config.mts'));
// return pkg.dir.startsWith(join(root, 'apps')) && viteConfigExists;
// });
// return appPackages;
// }

View File

@ -1,6 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -1,56 +0,0 @@
# @vben/vsh
一个 Shell 脚本工具集合,用于 Vue Vben Admin 项目的开发和管理。
## 功能特性
- 🚀 基于 Node.js 的现代化 Shell 工具
- 📦 支持模块化开发和按需加载
- 🔍 提供依赖检查和分析功能
- 🔄 支持循环依赖扫描
- 📝 提供包发布检查功能
## 安装
```bash
# 使用 pnpm 安装
pnpm add -D @vben/vsh
# 或者使用 npm
npm install -D @vben/vsh
# 或者使用 yarn
yarn add -D @vben/vsh
```
## 使用方法
### 全局安装
```bash
# 全局安装
pnpm add -g @vben/vsh
# 使用 vsh 命令
vsh [command]
```
### 本地使用
```bash
# 在 package.json 中添加脚本
{
"scripts": {
"vsh": "vsh"
}
}
# 运行命令
pnpm vsh [command]
```
## 命令列表
- `vsh check-deps`: 检查项目依赖
- `vsh scan-circular`: 扫描循环依赖
- `vsh publish-check`: 检查包发布配置

View File

@ -1,3 +0,0 @@
#!/usr/bin/env node
import('../dist/index.mjs');

View File

@ -1,7 +0,0 @@
import { defineBuildConfig } from 'unbuild';
export default defineBuildConfig({
clean: true,
declaration: true,
entries: ['src/index'],
});

View File

@ -1,31 +0,0 @@
{
"name": "@vben/vsh",
"version": "5.6.0",
"private": true,
"license": "MIT",
"type": "module",
"scripts": {
"stub": "pnpm unbuild --stub"
},
"files": [
"dist"
],
"bin": {
"vsh": "./bin/vsh.mjs"
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
"dependencies": {
"@vben/node-utils": "workspace:*",
"cac": "catalog:",
"circular-dependency-scanner": "catalog:",
"depcheck": "catalog:",
"publint": "catalog:"
}
}

View File

@ -1,170 +0,0 @@
import type { CAC } from 'cac';
import { extname } from 'node:path';
import { getStagedFiles } from '@vben/node-utils';
import { circularDepsDetect } from 'circular-dependency-scanner';
// 默认配置
const DEFAULT_CONFIG = {
allowedExtensions: ['.cjs', '.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue'],
ignoreDirs: [
'dist',
'.turbo',
'output',
'.cache',
'scripts',
'internal',
'packages/effects/request/src/',
'packages/@core/ui-kit/menu-ui/src/',
'packages/@core/ui-kit/popup-ui/src/',
],
threshold: 0, // 循环依赖的阈值
} as const;
// 类型定义
type CircularDependencyResult = string[];
interface CheckCircularConfig {
allowedExtensions?: string[];
ignoreDirs?: string[];
threshold?: number;
}
interface CommandOptions {
config?: CheckCircularConfig;
staged: boolean;
verbose: boolean;
}
// 缓存机制
const cache = new Map<string, CircularDependencyResult[]>();
/**
*
* @param circles -
*/
function formatCircles(circles: CircularDependencyResult[]): void {
if (circles.length === 0) {
console.log('✅ No circular dependencies found');
return;
}
console.log('⚠️ Circular dependencies found:');
circles.forEach((circle, index) => {
console.log(`\nCircular dependency #${index + 1}:`);
circle.forEach((file) => console.log(`${file}`));
});
}
/**
*
* @param options -
* @param options.staged -
* @param options.verbose -
* @param options.config -
* @returns Promise<void>
*/
async function checkCircular({
config = {},
staged,
verbose,
}: CommandOptions): Promise<void> {
try {
// 合并配置
const finalConfig = {
...DEFAULT_CONFIG,
...config,
};
// 生成忽略模式
const ignorePattern = `**/{${finalConfig.ignoreDirs.join(',')}}/**`;
// 检查缓存
const cacheKey = `${staged}-${process.cwd()}-${ignorePattern}`;
if (cache.has(cacheKey)) {
const cachedResults = cache.get(cacheKey);
if (cachedResults) {
verbose && formatCircles(cachedResults);
}
return;
}
// 检测循环依赖
const results = await circularDepsDetect({
absolute: staged,
cwd: process.cwd(),
ignore: [ignorePattern],
});
if (staged) {
let files = await getStagedFiles();
const allowedExtensions = new Set(finalConfig.allowedExtensions);
// 过滤文件列表
files = files.filter((file) => allowedExtensions.has(extname(file)));
const circularFiles: CircularDependencyResult[] = [];
for (const file of files) {
for (const result of results) {
const resultFiles = result.flat();
if (resultFiles.includes(file)) {
circularFiles.push(result);
}
}
}
// 更新缓存
cache.set(cacheKey, circularFiles);
verbose && formatCircles(circularFiles);
} else {
// 更新缓存
cache.set(cacheKey, results);
verbose && formatCircles(results);
}
// 如果发现循环依赖,只输出警告信息
if (results.length > 0) {
console.log(
'\n⚠ Warning: Circular dependencies found, please check and fix',
);
}
} catch (error) {
console.error(
'❌ Error checking circular dependencies:',
error instanceof Error ? error.message : error,
);
}
}
/**
*
* @param cac - CAC实例
*/
function defineCheckCircularCommand(cac: CAC): void {
cac
.command('check-circular')
.option('--staged', 'Only check staged files')
.option('--verbose', 'Show detailed information')
.option('--threshold <number>', 'Threshold for circular dependencies', {
default: 0,
})
.option('--ignore-dirs <dirs>', 'Directories to ignore, comma separated')
.usage('Analyze project circular dependencies')
.action(async ({ ignoreDirs, staged, threshold, verbose }) => {
const config: CheckCircularConfig = {
threshold: Number(threshold),
...(ignoreDirs && { ignoreDirs: ignoreDirs.split(',') }),
};
await checkCircular({
config,
staged,
verbose: verbose ?? true,
});
});
}
export { type CheckCircularConfig, defineCheckCircularCommand };

View File

@ -1,193 +0,0 @@
import type { CAC } from 'cac';
import { getPackages } from '@vben/node-utils';
import depcheck from 'depcheck';
// 默认配置
const DEFAULT_CONFIG = {
// 需要忽略的依赖匹配
ignoreMatches: [
'vite',
'vitest',
'unbuild',
'@vben/tsconfig',
'@vben/vite-config',
'@vben/tailwind-config',
'@types/*',
'@vben-core/design',
],
// 需要忽略的包
ignorePackages: [
'@vben/commitlint-config',
'@vben/eslint-config',
'@vben/node-utils',
'@vben/prettier-config',
'@vben/stylelint-config',
'@vben/tailwind-config',
'@vben/tsconfig',
'@vben/vite-config',
'@vben/vsh',
],
// 需要忽略的文件模式
ignorePatterns: ['dist', 'node_modules', 'public'],
};
interface DepcheckResult {
dependencies: string[];
devDependencies: string[];
missing: Record<string, string[]>;
}
interface DepcheckConfig {
ignoreMatches?: string[];
ignorePackages?: string[];
ignorePatterns?: string[];
}
interface PackageInfo {
dir: string;
packageJson: {
name: string;
};
}
/**
*
* @param unused -
*/
function cleanDepcheckResult(unused: DepcheckResult): void {
// 删除file:前缀的依赖提示,该依赖是本地依赖
Reflect.deleteProperty(unused.missing, 'file:');
// 清理路径依赖
Object.keys(unused.missing).forEach((key) => {
unused.missing[key] = (unused.missing[key] || []).filter(
(item: string) => !item.startsWith('/'),
);
if (unused.missing[key].length === 0) {
Reflect.deleteProperty(unused.missing, key);
}
});
}
/**
*
* @param pkgName -
* @param unused -
*/
function formatDepcheckResult(pkgName: string, unused: DepcheckResult): void {
const hasIssues =
Object.keys(unused.missing).length > 0 ||
unused.dependencies.length > 0 ||
unused.devDependencies.length > 0;
if (!hasIssues) {
return;
}
console.log('\n📦 Package:', pkgName);
if (Object.keys(unused.missing).length > 0) {
console.log('❌ Missing dependencies:');
Object.entries(unused.missing).forEach(([dep, files]) => {
console.log(` - ${dep}:`);
files.forEach((file) => console.log(`${file}`));
});
}
if (unused.dependencies.length > 0) {
console.log('⚠️ Unused dependencies:');
unused.dependencies.forEach((dep) => console.log(` - ${dep}`));
}
if (unused.devDependencies.length > 0) {
console.log('⚠️ Unused devDependencies:');
unused.devDependencies.forEach((dep) => console.log(` - ${dep}`));
}
}
/**
*
* @param config -
*/
async function runDepcheck(config: DepcheckConfig = {}): Promise<void> {
try {
const finalConfig = {
...DEFAULT_CONFIG,
...config,
};
const { packages } = await getPackages();
let hasIssues = false;
await Promise.all(
packages.map(async (pkg: PackageInfo) => {
// 跳过需要忽略的包
if (finalConfig.ignorePackages.includes(pkg.packageJson.name)) {
return;
}
const unused = await depcheck(pkg.dir, {
ignoreMatches: finalConfig.ignoreMatches,
ignorePatterns: finalConfig.ignorePatterns,
});
cleanDepcheckResult(unused);
const pkgHasIssues =
Object.keys(unused.missing).length > 0 ||
unused.dependencies.length > 0 ||
unused.devDependencies.length > 0;
if (pkgHasIssues) {
hasIssues = true;
formatDepcheckResult(pkg.packageJson.name, unused);
}
}),
);
if (!hasIssues) {
console.log('\n✅ Dependency check completed, no issues found');
}
} catch (error) {
console.error(
'❌ Dependency check failed:',
error instanceof Error ? error.message : error,
);
}
}
/**
*
* @param cac - CAC实例
*/
function defineDepcheckCommand(cac: CAC): void {
cac
.command('check-dep')
.option(
'--ignore-packages <packages>',
'Packages to ignore, comma separated',
)
.option(
'--ignore-matches <matches>',
'Dependency patterns to ignore, comma separated',
)
.option(
'--ignore-patterns <patterns>',
'File patterns to ignore, comma separated',
)
.usage('Analyze project dependencies')
.action(async ({ ignoreMatches, ignorePackages, ignorePatterns }) => {
const config: DepcheckConfig = {
...(ignorePackages && { ignorePackages: ignorePackages.split(',') }),
...(ignoreMatches && { ignoreMatches: ignoreMatches.split(',') }),
...(ignorePatterns && { ignorePatterns: ignorePatterns.split(',') }),
};
await runDepcheck(config);
});
}
export { defineDepcheckCommand, type DepcheckConfig };

View File

@ -1,78 +0,0 @@
import type { CAC } from 'cac';
import { join, relative } from 'node:path';
import {
colors,
consola,
findMonorepoRoot,
getPackages,
gitAdd,
outputJSON,
prettierFormat,
toPosixPath,
} from '@vben/node-utils';
const CODE_WORKSPACE_FILE = join('vben-admin.code-workspace');
interface CodeWorkspaceCommandOptions {
autoCommit?: boolean;
spaces?: number;
}
async function createCodeWorkspace({
autoCommit = false,
spaces = 2,
}: CodeWorkspaceCommandOptions) {
const { packages, rootDir } = await getPackages();
let folders = packages.map((pkg) => {
const { dir, packageJson } = pkg;
return {
name: packageJson.name,
path: toPosixPath(relative(rootDir, dir)),
};
});
folders = folders.filter(Boolean);
const monorepoRoot = findMonorepoRoot();
const outputPath = join(monorepoRoot, CODE_WORKSPACE_FILE);
await outputJSON(outputPath, { folders }, spaces);
await prettierFormat(outputPath);
if (autoCommit) {
await gitAdd(CODE_WORKSPACE_FILE, monorepoRoot);
}
}
async function runCodeWorkspace({
autoCommit,
spaces,
}: CodeWorkspaceCommandOptions) {
await createCodeWorkspace({
autoCommit,
spaces,
});
if (autoCommit) {
return;
}
consola.log('');
consola.success(colors.green(`${CODE_WORKSPACE_FILE} is updated!`));
consola.log('');
}
function defineCodeWorkspaceCommand(cac: CAC) {
cac
.command('code-workspace')
.usage('Update the `.code-workspace` file')
.option('--spaces [number]', '.code-workspace JSON file spaces.', {
default: 2,
})
.option('--auto-commit', 'auto commit .code-workspace JSON file.', {
default: false,
})
.action(runCodeWorkspace);
}
export { defineCodeWorkspaceCommand };

View File

@ -1,74 +0,0 @@
import { colors, consola } from '@vben/node-utils';
import { cac } from 'cac';
import { version } from '../package.json';
import { defineCheckCircularCommand } from './check-circular';
import { defineDepcheckCommand } from './check-dep';
import { defineCodeWorkspaceCommand } from './code-workspace';
import { defineLintCommand } from './lint';
import { definePubLintCommand } from './publint';
// 命令描述
const COMMAND_DESCRIPTIONS = {
'check-circular': 'Check for circular dependencies',
'check-dep': 'Check for unused dependencies',
'code-workspace': 'Manage VS Code workspace settings',
lint: 'Run linting on the project',
publint: 'Check package.json files for publishing standards',
} as const;
/**
* Initialize and run the CLI
*/
async function main(): Promise<void> {
try {
const vsh = cac('vsh');
// Register commands
defineLintCommand(vsh);
definePubLintCommand(vsh);
defineCodeWorkspaceCommand(vsh);
defineCheckCircularCommand(vsh);
defineDepcheckCommand(vsh);
// Handle invalid commands
vsh.on('command:*', ([cmd]) => {
consola.error(
colors.red(`Invalid command: ${cmd}`),
'\n',
colors.yellow('Available commands:'),
'\n',
Object.entries(COMMAND_DESCRIPTIONS)
.map(([cmd, desc]) => ` ${colors.cyan(cmd)} - ${desc}`)
.join('\n'),
);
process.exit(1);
});
// Set up CLI
vsh.usage('vsh <command> [options]');
vsh.help();
vsh.version(version);
// Parse arguments
vsh.parse();
} catch (error) {
consola.error(
colors.red('An unexpected error occurred:'),
'\n',
error instanceof Error ? error.message : error,
);
process.exit(1);
}
}
// Run the CLI
main().catch((error) => {
consola.error(
colors.red('Failed to start CLI:'),
'\n',
error instanceof Error ? error.message : error,
);
process.exit(1);
});

View File

@ -1,48 +0,0 @@
import type { CAC } from 'cac';
import { execaCommand } from '@vben/node-utils';
interface LintCommandOptions {
/**
* Format lint problem.
*/
format?: boolean;
}
async function runLint({ format }: LintCommandOptions) {
// process.env.FORCE_COLOR = '3';
if (format) {
await execaCommand(`stylelint "**/*.{vue,css,less,scss}" --cache --fix`, {
stdio: 'inherit',
});
await execaCommand(`eslint . --cache --fix`, {
stdio: 'inherit',
});
await execaCommand(`prettier . --write --cache --log-level warn`, {
stdio: 'inherit',
});
return;
}
await Promise.all([
execaCommand(`eslint . --cache`, {
stdio: 'inherit',
}),
execaCommand(`prettier . --ignore-unknown --check --cache`, {
stdio: 'inherit',
}),
execaCommand(`stylelint "**/*.{vue,css,less,scss}" --cache`, {
stdio: 'inherit',
}),
]);
}
function defineLintCommand(cac: CAC) {
cac
.command('lint')
.usage('Batch execute project lint check.')
.option('--format', 'Format lint problem.')
.action(runLint);
}
export { defineLintCommand };

View File

@ -1,185 +0,0 @@
import type { CAC } from 'cac';
import type { Result } from 'publint';
import { basename, dirname, join } from 'node:path';
import {
colors,
consola,
ensureFile,
findMonorepoRoot,
generatorContentHash,
getPackages,
outputJSON,
readJSON,
UNICODE,
} from '@vben/node-utils';
import { publint } from 'publint';
import { formatMessage } from 'publint/utils';
const CACHE_FILE = join(
'node_modules',
'.cache',
'publint',
'.pkglintcache.json',
);
interface PubLintCommandOptions {
/**
* Only errors are checked, no program exit is performed
*/
check?: boolean;
}
/**
* Get files that require lint
* @param files
*/
async function getLintFiles(files: string[] = []) {
const lintFiles: string[] = [];
if (files?.length > 0) {
return files.filter((file) => basename(file) === 'package.json');
}
const { packages } = await getPackages();
for (const { dir } of packages) {
lintFiles.push(join(dir, 'package.json'));
}
return lintFiles;
}
function getCacheFile() {
const root = findMonorepoRoot();
return join(root, CACHE_FILE);
}
async function readCache(cacheFile: string) {
try {
await ensureFile(cacheFile);
return await readJSON(cacheFile);
} catch {
return {};
}
}
async function runPublint(files: string[], { check }: PubLintCommandOptions) {
const lintFiles = await getLintFiles(files);
const cacheFile = getCacheFile();
const cacheData = await readCache(cacheFile);
const cache: Record<string, { hash: string; result: Result }> = cacheData;
const results = await Promise.all(
lintFiles.map(async (file) => {
try {
const pkgJson = await readJSON(file);
if (pkgJson.private) {
return null;
}
Reflect.deleteProperty(pkgJson, 'dependencies');
Reflect.deleteProperty(pkgJson, 'devDependencies');
Reflect.deleteProperty(pkgJson, 'peerDependencies');
const content = JSON.stringify(pkgJson);
const hash = generatorContentHash(content);
const publintResult: Result =
cache?.[file]?.hash === hash
? (cache?.[file]?.result ?? [])
: await publint({
level: 'suggestion',
pkgDir: dirname(file),
strict: true,
});
cache[file] = {
hash,
result: publintResult,
};
return { pkgJson, pkgPath: file, publintResult };
} catch {
return null;
}
}),
);
await outputJSON(cacheFile, cache);
printResult(results, check);
}
function printResult(
results: Array<null | {
pkgJson: Record<string, number | string>;
pkgPath: string;
publintResult: Result;
}>,
check?: boolean,
) {
let errorCount = 0;
let warningCount = 0;
let suggestionsCount = 0;
for (const result of results) {
if (!result) {
continue;
}
const { pkgJson, pkgPath, publintResult } = result;
const messages = publintResult?.messages ?? [];
if (messages?.length < 1) {
continue;
}
consola.log('');
consola.log(pkgPath);
for (const message of messages) {
switch (message.type) {
case 'error': {
errorCount++;
break;
}
case 'suggestion': {
suggestionsCount++;
break;
}
case 'warning': {
warningCount++;
break;
}
// No default
}
const ruleUrl = `https://publint.dev/rules#${message.code.toLocaleLowerCase()}`;
consola.log(
` ${formatMessage(message, pkgJson)}${colors.dim(` ${ruleUrl}`)}`,
);
}
}
const totalCount = warningCount + errorCount + suggestionsCount;
if (totalCount > 0) {
consola.error(
colors.red(
`${UNICODE.FAILURE} ${totalCount} problem (${errorCount} errors, ${warningCount} warnings, ${suggestionsCount} suggestions)`,
),
);
!check && process.exit(1);
} else {
consola.log(colors.green(`${UNICODE.SUCCESS} No problem`));
}
}
function definePubLintCommand(cac: CAC) {
cac
.command('publint [...files]')
.usage('Check if the monorepo package conforms to the publint standard.')
.option('--check', 'Only errors are checked, no program exit is performed.')
.action(runPublint);
}
export { definePubLintCommand };

View File

@ -1,6 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@ -293,5 +293,5 @@ import { useAuthStore } from '#/store';
- **常用功能**: `references/features/features.md` - 水印、缓存、动态标题等 - **常用功能**: `references/features/features.md` - 水印、缓存、动态标题等
### 构建部署 (references/deployment/) ### 构建部署 (references/deployment/)
- **构建部署**: `references/deployment/deploy.md` - 构建配置、Nginx、Docker - **构建部署**: `references/deployment/deploy.md` - 构建配置、Jenkins、Nginx
- **常见问题**: `references/deployment/faq.md` - 依赖安装、打包部署、错误排查 - **常见问题**: `references/deployment/faq.md` - 依赖安装、打包部署、错误排查

View File

@ -97,40 +97,6 @@ server {
} }
``` ```
### Docker 部署
```dockerfile
# Dockerfile
FROM node:20-alpine as builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build:antd
FROM nginx:alpine
COPY --from=builder /app/apps/web-antd/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
```yaml
# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "80:80"
depends_on:
- backend
backend:
image: your-backend-image
ports:
- "8080:8080"
```
## 动态配置 ## 动态配置
打包后可通过修改 `_app.config.js` 动态修改配置: 打包后可通过修改 `_app.config.js` 动态修改配置:

View File

@ -1,6 +0,0 @@
# https://tea.xyz/what-is-this-file
---
version: 1.0.0
codeOwners:
- '0xB33cc732DFc15Cd39eF50Fb165c876E24417E48f'
quorum: 1

View File

@ -8,9 +8,7 @@
"internal/node-utils/src/**/*.ts", "internal/node-utils/src/**/*.ts",
"internal/tailwind-config/src/**/*.ts", "internal/tailwind-config/src/**/*.ts",
"internal/vite-config/*.json", "internal/vite-config/*.json",
"internal/vite-config/src/**/*.ts", "internal/vite-config/src/**/*.ts"
"scripts/*/src/**/*.ts",
"scripts/*/src/**/*.json"
], ],
"globalEnv": ["NODE_ENV"], "globalEnv": ["NODE_ENV"],
"tasks": { "tasks": {

View File

@ -1,152 +0,0 @@
{
"folders": [
{
"name": "@vben/web-antdv-next",
"path": "apps/web-antdv-next",
},
{
"name": "@vben/commitlint-config",
"path": "internal/lint-configs/commitlint-config",
},
{
"name": "@vben/eslint-config",
"path": "internal/lint-configs/eslint-config",
},
{
"name": "@vben/prettier-config",
"path": "internal/lint-configs/prettier-config",
},
{
"name": "@vben/stylelint-config",
"path": "internal/lint-configs/stylelint-config",
},
{
"name": "@vben/node-utils",
"path": "internal/node-utils",
},
{
"name": "@vben/tailwind-config",
"path": "internal/tailwind-config",
},
{
"name": "@vben/tsconfig",
"path": "internal/tsconfig",
},
{
"name": "@vben/vite-config",
"path": "internal/vite-config",
},
{
"name": "@vben-core/design",
"path": "packages/@core/base/design",
},
{
"name": "@vben-core/icons",
"path": "packages/@core/base/icons",
},
{
"name": "@vben-core/shared",
"path": "packages/@core/base/shared",
},
{
"name": "@vben-core/typings",
"path": "packages/@core/base/typings",
},
{
"name": "@vben-core/composables",
"path": "packages/@core/composables",
},
{
"name": "@vben-core/preferences",
"path": "packages/@core/preferences",
},
{
"name": "@vben-core/form-ui",
"path": "packages/@core/ui-kit/form-ui",
},
{
"name": "@vben-core/layout-ui",
"path": "packages/@core/ui-kit/layout-ui",
},
{
"name": "@vben-core/menu-ui",
"path": "packages/@core/ui-kit/menu-ui",
},
{
"name": "@vben-core/popup-ui",
"path": "packages/@core/ui-kit/popup-ui",
},
{
"name": "@vben-core/shadcn-ui",
"path": "packages/@core/ui-kit/shadcn-ui",
},
{
"name": "@vben-core/tabs-ui",
"path": "packages/@core/ui-kit/tabs-ui",
},
{
"name": "@vben/constants",
"path": "packages/constants",
},
{
"name": "@vben/access",
"path": "packages/effects/access",
},
{
"name": "@vben/common-ui",
"path": "packages/effects/common-ui",
},
{
"name": "@vben/hooks",
"path": "packages/effects/hooks",
},
{
"name": "@vben/layouts",
"path": "packages/effects/layouts",
},
{
"name": "@vben/plugins",
"path": "packages/effects/plugins",
},
{
"name": "@vben/request",
"path": "packages/effects/request",
},
{
"name": "@vben/icons",
"path": "packages/icons",
},
{
"name": "@vben/locales",
"path": "packages/locales",
},
{
"name": "@vben/preferences",
"path": "packages/preferences",
},
{
"name": "@vben/stores",
"path": "packages/stores",
},
{
"name": "@vben/styles",
"path": "packages/styles",
},
{
"name": "@vben/types",
"path": "packages/types",
},
{
"name": "@vben/utils",
"path": "packages/utils",
},
{
"name": "@vben/turbo-run",
"path": "scripts/turbo-run",
},
{
"name": "@vben/vsh",
"path": "scripts/vsh",
},
],
}