feat: Feature/pro docs (#70)
* chore: merge main * feat: update docs * feat: remove coze-assistant * feat: add watermark plugin * feat: update preferences * feat: update docs --------- Co-authored-by: vince <vince292007@gmail.com>
This commit is contained in:
251
website/src/guide/essentials/build.md
Normal file
251
website/src/guide/essentials/build.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# 构建与部署
|
||||
|
||||
::: tip 前言
|
||||
|
||||
由于是展示项目,所以打包后相对较大,如果项目中没有用到的插件,可以删除对应的文件或者路由,不引用即可,没有引用就不会打包。
|
||||
|
||||
:::
|
||||
|
||||
## 构建
|
||||
|
||||
项目开发完成之后,执行以下命令进行构建:
|
||||
|
||||
**注意:** 请在项目跟目录下执行以下命令
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
构建打包成功之后,会在根目录生成对应的应用下的 `dist` 文件夹,里面就是构建打包好的文件,例如: `apps/web-antd/dist/`
|
||||
|
||||
## 预览
|
||||
|
||||
发布之前可以在本地进行预览,有多种方式,这里介绍两种:
|
||||
|
||||
- 使用项目自定的命令进行预览(推荐)
|
||||
|
||||
**注意:** 请在项目跟目录下执行以下命令
|
||||
|
||||
```bash
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
等待构建成功后,访问 `http://localhost:4173` 即可查看效果。
|
||||
|
||||
- 本地服务器预览
|
||||
|
||||
可以在电脑全局安装 `serve` 服务,如 `live-server`,
|
||||
|
||||
```bash
|
||||
npm i -g live-server
|
||||
|
||||
```
|
||||
|
||||
然后在 `dist` 目录下执行 `live-server` 命令,即可在本地查看效果。
|
||||
|
||||
```bash
|
||||
|
||||
cd apps/web-antd/dist
|
||||
# 本地预览,默认端口8080
|
||||
live-server
|
||||
# 指定端口
|
||||
live-server --port 9000
|
||||
|
||||
```
|
||||
|
||||
## 压缩
|
||||
|
||||
### 开启 `gzip` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=gzip
|
||||
|
||||
```
|
||||
|
||||
### 开启 `brotli` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=brotli
|
||||
|
||||
```
|
||||
|
||||
### 同时开启 `gzip` 和 `brotli` 压缩
|
||||
|
||||
需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_COMPRESS=gzip,brotli
|
||||
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
`gzip` 和 `brotli` 都需要安装特定模块才能使用。
|
||||
|
||||
:::
|
||||
|
||||
::: details gzip 与 brotli 在 nginx 内的配置
|
||||
|
||||
```bash
|
||||
http {
|
||||
# 开启gzip
|
||||
gzip on;
|
||||
# 开启gzip_static
|
||||
# gzip_static 开启后可能会报错,需要安装相应的模块, 具体安装方式可以自行查询
|
||||
# 只有这个开启,vue文件打包的.gz文件才会有效果,否则不需要开启gzip进行打包
|
||||
gzip_static on;
|
||||
gzip_proxied any;
|
||||
gzip_min_length 1k;
|
||||
gzip_buffers 4 16k;
|
||||
#如果nginx中使用了多层代理 必须设置这个才可以开启gzip。
|
||||
gzip_http_version 1.0;
|
||||
gzip_comp_level 2;
|
||||
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
|
||||
gzip_vary off;
|
||||
gzip_disable "MSIE [1-6]\.";
|
||||
|
||||
# 开启 brotli压缩
|
||||
# 需要安装对应的nginx模块,具体安装方式可以自行查询
|
||||
# 可以与gzip共存不会冲突
|
||||
brotli on;
|
||||
brotli_comp_level 6;
|
||||
brotli_buffers 16 8k;
|
||||
brotli_min_length 20;
|
||||
brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 构建分析
|
||||
|
||||
如果你的构建文件很大,可以通过项目内置 [rollup-plugin-analyzer](https://github.com/doesdev/rollup-plugin-analyzer) 插件进行代码体积分析,从而优化你的代码。只需要在`根目录`下执行以下命令:
|
||||
|
||||
```bash
|
||||
pnpm run build:analyze
|
||||
```
|
||||
|
||||
运行之后,在自动打开的页面可以看到具体的体积分布,以分析哪些依赖有问题。
|
||||
|
||||

|
||||
|
||||
## 部署
|
||||
|
||||
简单的部署只需要将最终生成的静态文件,dist 文件夹的静态文件发布到你的 cdn 或者静态服务器即可,需要注意的是其中的 index.html 通常会是你后台服务的入口页面,在确定了 js 和 css 的静态之后可能需要改变页面的引入路径。
|
||||
|
||||
例如上传到 nginx 服务器,可以将 dist 文件夹下的文件上传到服务器的 `/srv/www/project/index.html` 目录下,然后访问配置好的域名即可。
|
||||
|
||||
```bash
|
||||
# nginx配置
|
||||
location / {
|
||||
# 不缓存html,防止程序更新后缓存继续生效
|
||||
if ($request_filename ~* .*\.(?:htm|html)$) {
|
||||
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
|
||||
access_log on;
|
||||
}
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
root /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
}
|
||||
```
|
||||
|
||||
部署时可能会发现资源路径不对,只需要修改`.env.production`文件即可。
|
||||
|
||||
```bash
|
||||
# 根据自己路径来配置更改
|
||||
# 注意需要以 / 开头和结尾
|
||||
VITE_BASE=/
|
||||
VITE_BASE=/xxx/
|
||||
```
|
||||
|
||||
### 前端路由与服务端的结合
|
||||
|
||||
项目前端路由使用的是 vue-router,所以你可以选择两种方式:history 和 hash。
|
||||
|
||||
- `hash` 默认会在 url 后面拼接`#`
|
||||
- `history` 则不会,不过 `history` 需要服务器配合
|
||||
|
||||
可在 `.env.production` 内进行 mode 修改
|
||||
|
||||
```bash
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
|
||||
```
|
||||
|
||||
### history 路由模式下服务端配置
|
||||
|
||||
开启 `history` 模式需要服务器配置,更多的服务器配置详情可以看 [history-mode](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode)
|
||||
|
||||
这里以 `nginx` 配置为例:
|
||||
|
||||
#### 部署到根目录
|
||||
|
||||
```bash {5}
|
||||
server {
|
||||
listen 80;
|
||||
location / {
|
||||
# 用于配合 History 使用
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 部署到非根目录
|
||||
|
||||
- 首先需要在打包的时候更改`.env.production`配置:
|
||||
|
||||
```bash
|
||||
VITE_BASE = /sub/
|
||||
```
|
||||
|
||||
- 然后在 nginx 配置文件中配置
|
||||
|
||||
```bash {8}
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location /sub/ {
|
||||
# 这里是vue打包文件dist内的文件的存放路径
|
||||
alias /srv/www/project/;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /sub/index.html;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 跨域处理
|
||||
|
||||
使用 nginx 处理项目部署后的跨域问题
|
||||
|
||||
1. 配置前端项目接口地址,在项目目录下的``.env.production`文件中配置:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=/api
|
||||
```
|
||||
|
||||
2. 在 nginx 配置请求转发到后台
|
||||
|
||||
```bash {10-11}
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
# 接口代理,用于解决跨域问题
|
||||
location /api {
|
||||
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_pass http://110.110.1.1:8080/api;
|
||||
rewrite "^/api/(.*)$" /$1 break;
|
||||
proxy_redirect default;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Access-Control-Allow-Headers X-Requested-With;
|
||||
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
|
||||
}
|
||||
}
|
||||
```
|
21
website/src/guide/essentials/concept.md
Normal file
21
website/src/guide/essentials/concept.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 基础概念
|
||||
|
||||
新版本中,整体工程进行了重构,现在我们将会介绍一些基础名词概念,以便于你更好的理解整个文档。请务必仔先阅读这一部分。
|
||||
|
||||
## 大仓
|
||||
|
||||
大仓指的是整个项目的仓库,包含了所有的代码、包、应用、规范、文档、配置等,也就是一整个 `Monorepo` 目录的所有内容。
|
||||
|
||||
## 应用
|
||||
|
||||
应用指的是一个完整的项目,一个项目可以包含多个应用,这些项目可以复用大仓内的代码、包、规范等。应用都被放置在 `apps` 目录下。每个应用都是独立的,可以单独运行、构建、测试、部署,可以引入不同的组件库等等。
|
||||
|
||||
::: tip
|
||||
|
||||
应用不限于前端应用,也可以是后端应用、移动端应用等,例如 `apps/backend-mock`就是一个后端服务。
|
||||
|
||||
:::
|
||||
|
||||
## 包
|
||||
|
||||
包指的是一个独立的模块,可以是一个组件、一个工具、一个库等。包可以被多个应用引用,也可以被其他包引用。包都被放置在 `packages` 目录下。
|
136
website/src/guide/essentials/development.md
Normal file
136
website/src/guide/essentials/development.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 本地开发 {#development}
|
||||
|
||||
::: tip 代码获取
|
||||
|
||||
如果你还没有获取代码,可以先从 [快速开始](../introduction/quick-start.md) 处开始阅读文档。
|
||||
|
||||
:::
|
||||
|
||||
## 前置准备
|
||||
|
||||
为了更好的开发体验,我们提供了一些工具配置、项目说明,以便于您更好的开发。
|
||||
|
||||
### 需要掌握的基础知识
|
||||
|
||||
本项目需要一定前端基础知识,请确保掌握 Vue 的基础知识,以便能处理一些常见的问题。建议在开发前先学一下以下内容,提前了解和学习这些知识,会对项目理解非常有帮助:
|
||||
|
||||
- [Vue3](https://vuejs.org/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
- [Vue Router](https://router.vuejs.org/)
|
||||
- [Vitejs](https://vitejs.dev/)
|
||||
- [Pnpm](https://pnpm.io/)
|
||||
- [Turbo](https://turbo.build/)
|
||||
|
||||
### 工具配置
|
||||
|
||||
如果您使用的 IDE 是[vscode](https://code.visualstudio.com/)(推荐)的话,可以安装以下工具来提高开发效率及代码格式化:
|
||||
|
||||
- [Vue - Official](https://marketplace.visualstudio.com/items?itemName=Vue.volar) - Vue 官方插件(必备)。
|
||||
- [Tailwind CSS](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) - Tailwindcss 提示插件。
|
||||
- [CSS Variable Autocomplete](https://marketplace.visualstudio.com/items?itemName=bradlc.vunguyentuan.vscode-css-variables) - Css 变量提示插件。
|
||||
- [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) - Iconify 图标插件
|
||||
- [i18n Ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) - i18n 插件
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - 脚本代码检查
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - 代码格式化
|
||||
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) - css 格式化
|
||||
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - 单词语法检查
|
||||
- [DotENV](https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv) - .env 文件 高亮
|
||||
|
||||
## Npm Scripts
|
||||
|
||||
npm 脚本是项目常见的配置,用于执行一些常见的任务,比如启动项目、打包项目等。以下的脚本都可以在项目根目录的 `package.json` 文件中找到。
|
||||
|
||||
执行方式为:`pnpm run [script]` 或 `npm run [script]`。
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
// 安装依赖
|
||||
"bootstrap": "pnpm install",
|
||||
// 构建项目
|
||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=8192 turbo build",
|
||||
// 构建docker镜像
|
||||
"build:docker": "./build-local-docker-image.sh",
|
||||
// changeset 版本管理
|
||||
"changeset": "pnpm exec changeset",
|
||||
// 检查项目各种问题
|
||||
"check": "pnpm run check:dep && pnpm run check:circular && pnpm run check:type && pnpm run 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",
|
||||
// 清理项目(删除node_modules、dist、.turbo)等目录
|
||||
"clean": "vsh clean",
|
||||
// 提交代码
|
||||
"commit": "czg",
|
||||
// 启动项目(默认会运行整个仓库所有包的dev脚本)
|
||||
"dev": "cross-env TURBO_UI=1 turbo run dev",
|
||||
// 启动文档
|
||||
"dev:docs": "pnpm -F @vben/website run docs:dev",
|
||||
// 格式化代码
|
||||
"format": "vsh lint --format",
|
||||
// lint 代码
|
||||
"lint": "vsh lint",
|
||||
// 依赖安装完成之后,执行所有包的stub脚本
|
||||
"postinstall": "turbo run stub",
|
||||
// 只允许使用pnpm
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
// husky的安装
|
||||
"prepare": "is-ci || husky",
|
||||
// 包规范检查
|
||||
"publint": "vsh publint",
|
||||
// 删除所有的node_modules、yarn.lock、package.lock.json,重新安装依赖
|
||||
"reinstall": "pnpm clean --del-lock && pnpm bootstrap",
|
||||
// 运行 vitest 单元测试
|
||||
"test:unit": "vitest",
|
||||
// 更新项目依赖
|
||||
"update:deps": " pnpm update --latest --recursive",
|
||||
// changeset生成提交集
|
||||
"version": "pnpm exec changeset version && pnpm install --no-frozen-lockfile"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 本地运行项目
|
||||
|
||||
如需本地运行文档,并进行调整,可以执行以下命令:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
## DevTools
|
||||
|
||||
项目内置了 [Vue DevTools](https://github.com/vuejs/devtools-next) 插件,可以在开发过程中使用。默认关闭,可在`.env.development` 内开启,并重新运行项目即可:
|
||||
|
||||
```bash
|
||||
VITE_DEVTOOLS=true
|
||||
```
|
||||
|
||||
开启后,项目运行会在页面底部显示一个 Vue DevTools 的图标,点击即可打开 DevTools。
|
||||
|
||||

|
||||
|
||||
## 本地运行文档
|
||||
|
||||
如需本地运行文档,并进行调整,可以执行以下命令:
|
||||
|
||||
```bash
|
||||
pnpm dev:docs
|
||||
```
|
||||
|
||||
## 问题解决
|
||||
|
||||
如果你在使用过程中遇到依赖相关的问题,可以尝试以下重新安装依赖:
|
||||
|
||||
```bash
|
||||
# 请在项目根目录下执行
|
||||
# 该命令会删除整个仓库所有的 node_modules、yarn.lock、package.lock.json后
|
||||
# 再进行依赖重新安装(安装速度会明显变慢)。
|
||||
pnpm reinstall
|
||||
```
|
58
website/src/guide/essentials/external-module.md
Normal file
58
website/src/guide/essentials/external-module.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# 外部模块
|
||||
|
||||
除了项目默认引入的外部模块,有时我们还需要引入其他外部模块。我们以 [ant-design-vue](https://antdv.com/components/overview) 为例:
|
||||
|
||||
## 安装依赖
|
||||
|
||||
::: tip 安装依赖到指定包
|
||||
|
||||
- 由于项目采用了 [pnpm](https://pnpm.io/) 作为包管理工具,所以我们需要使用 `pnpm` 命令来安装依赖。
|
||||
- 通过采用了 Monorepo 模块来管理项目,所以我们需要在指定包下安装依赖。安装依赖前请确保已经进入到指定包目录下。
|
||||
|
||||
:::
|
||||
|
||||
```bash
|
||||
# cd /path/to/your/package
|
||||
pnpm add ant-design-vue
|
||||
```
|
||||
|
||||
## 使用
|
||||
|
||||
### 全局引入
|
||||
|
||||
```ts
|
||||
import { createApp } from 'vue';
|
||||
import Antd from 'ant-design-vue';
|
||||
import App from './App';
|
||||
import 'ant-design-vue/dist/reset.css';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(Antd).mount('#app');
|
||||
```
|
||||
|
||||
#### 使用
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<a-button>text</a-button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 局部引入
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { Button } from 'ant-design-vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button>text</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
::: warning 注意
|
||||
|
||||
- 如果组件有依赖样式,则需要再引入样式文件
|
||||
|
||||
:::
|
80
website/src/guide/essentials/icons.md
Normal file
80
website/src/guide/essentials/icons.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 图标
|
||||
|
||||
::: tip 关于图标的管理
|
||||
|
||||
- 项目的图标主要由`@vben/icons`包提供,建议统一在该包内部管理,以便于统一管理和维护。
|
||||
- 如果你使用的是 `Vscode`,推荐安装 [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) 插件,可以方便的查找和使用图标。
|
||||
|
||||
:::
|
||||
|
||||
项目中有以下多种图标使用方式,可以根据实际情况选择使用:
|
||||
|
||||
## Iconify 图标 <Badge text="推荐" type="tip"/>
|
||||
|
||||
集成了 [iconify](https://github.com/iconify/iconify) 图标库
|
||||
|
||||
### 新增
|
||||
|
||||
可在 `packages/icons/src/iconify` 目录下新增图标:
|
||||
|
||||
```ts
|
||||
// packages/icons/src/iconify/index.ts
|
||||
import { createIconifyIcon } from '@vben-core/icons';
|
||||
|
||||
export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc');
|
||||
```
|
||||
|
||||
### 使用
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { MdiKeyboardEsc } from '@vben/icons';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 一个宽高为20px的图标 -->
|
||||
<MdiKeyboardEsc class="size-5" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## Svg 图标 <Badge text="推荐" type="tip"/>
|
||||
|
||||
没有采用 Svg Sprite 的方式,而是直接引入 Svg 图标,
|
||||
|
||||
### 新增
|
||||
|
||||
可以在 `packages/icons/src/svg/icons` 目录下新增图标文件`test.svg`, 然后在 `packages/icons/src/svg/index.ts` 中引入:
|
||||
|
||||
```ts
|
||||
// packages/icons/src/svg/index.ts
|
||||
import { createIconifyIcon } from '@vben-core/icons';
|
||||
|
||||
const SvgTestIcon = createIconifyIcon('svg:test');
|
||||
|
||||
export { SvgTestIcon };
|
||||
```
|
||||
|
||||
### 使用
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { SvgTestIcon } from '@vben/icons';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 一个宽高为20px的图标 -->
|
||||
<SvgTestIcon class="size-5" />
|
||||
</template>
|
||||
```
|
||||
|
||||
## Tailwind CSS 图标 <Badge text="不推荐" type="danger"/>
|
||||
|
||||
### 使用
|
||||
|
||||
直接添加 Tailwind CSS 的图标类名即可使用,图标类名可查看 [iconify](https://github.com/iconify/iconify) :
|
||||
|
||||
```vue
|
||||
|
||||
<span class="<span class="icon-[mdi--ab-testing]"></span>"></span>
|
||||
|
||||
```
|
559
website/src/guide/essentials/route.md
Normal file
559
website/src/guide/essentials/route.md
Normal file
@@ -0,0 +1,559 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 路由和菜单
|
||||
|
||||
在项目中,框架提供了一套基础的路由系统,并**根据路由文件自动生成对应的菜单结构**。
|
||||
|
||||
## 路由类型
|
||||
|
||||
路由分为静态路由和动态路由,静态路由是在项目启动时就已经确定的路由。动态路由一般是在用户登录后,根据用户的权限动态生成的路由。
|
||||
|
||||
### 静态路由
|
||||
|
||||
如果你的页面项目不需要权限控制,可以直接使用静态路由,静态路由的配置在应用下 `src/router/routes/index` 目录下,打开注释的文件内容:
|
||||
|
||||
```ts
|
||||
// 有需要可以自行打开注释,并创建文件夹
|
||||
// const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); // [!code --]
|
||||
const staticRouteFiles = import.meta.glob('./static/**/*.ts', { eager: true }); // [!code ++]
|
||||
/** 动态路由 */
|
||||
const dynamicRoutes: RouteRecordRaw[] = mergeRouteModules(dynamicRouteFiles);
|
||||
|
||||
/** 静态路由列表,访问这些页面可以不需要权限 */
|
||||
// const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); // [!code --]
|
||||
const staticRoutes: RouteRecordRaw[] = []; // [!code --]
|
||||
const staticRoutes: RouteRecordRaw[] = mergeRouteModules(staticRouteFiles); // [!code ++]
|
||||
```
|
||||
|
||||
### 动态路由
|
||||
|
||||
动态路由的配置在对应应用 `src/router/routes/modules` 目录下,这个目录下存放了所有的路由文件。每个文件的内容格式如下,与 Vue Router 的路由配置格式一致,以下为二级路由和多级路由的配置。
|
||||
|
||||
## 路由定义
|
||||
|
||||
静态路由与动态路由的配置方式一致,以下为二级路由和多级路由的配置:
|
||||
|
||||
### 二级路由
|
||||
|
||||
::: details 二级路由示例代码
|
||||
|
||||
```ts
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { VBEN_LOGO_URL } from '@vben/constants';
|
||||
|
||||
import { BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
badgeVariants: 'destructive',
|
||||
icon: VBEN_LOGO_URL,
|
||||
order: 9999,
|
||||
title: $t('page.vben.title'),
|
||||
},
|
||||
name: 'VbenProject',
|
||||
path: '/vben-admin',
|
||||
redirect: '/vben-admin/about',
|
||||
children: [
|
||||
{
|
||||
name: 'VbenAbout',
|
||||
path: '/vben-admin/about',
|
||||
component: () => import('#/views/_core/vben/about/index.vue'),
|
||||
meta: {
|
||||
badgeType: 'dot',
|
||||
badgeVariants: 'destructive',
|
||||
icon: 'lucide:copyright',
|
||||
title: $t('page.vben.about'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### 多级路由
|
||||
|
||||
::: tip
|
||||
|
||||
- 多级路由的父级路由无需设置 `component` 属性,只需设置 `children` 属性即可。除非你真的需要在父级路由嵌套下显示内容。
|
||||
- 如果没有特殊情况,父级路由的 `redirect` 属性,不需要指定,默认会指向第一个子路由。
|
||||
|
||||
:::
|
||||
|
||||
::: details 多级路由示例代码
|
||||
|
||||
```ts
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
icon: 'ic:baseline-view-in-ar',
|
||||
keepAlive: true,
|
||||
order: 1000,
|
||||
title: $t('page.demos.title'),
|
||||
},
|
||||
name: 'Demos',
|
||||
path: '/demos',
|
||||
redirect: '/demos/access',
|
||||
children: [
|
||||
// 嵌套菜单
|
||||
{
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('page.demos.nested.title'),
|
||||
},
|
||||
name: 'NestedDemos',
|
||||
path: '/demos/nested',
|
||||
redirect: '/demos/nested/menu1',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu1Demo',
|
||||
path: '/demos/nested/menu1',
|
||||
component: () => import('#/views/demos/nested/menu-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('page.demos.nested.menu1'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu2Demo',
|
||||
path: '/demos/nested/menu2',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('page.demos.nested.menu2'),
|
||||
},
|
||||
redirect: '/demos/nested/menu2/menu2-1',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu21Demo',
|
||||
path: '/demos/nested/menu2/menu2-1',
|
||||
component: () => import('#/views/demos/nested/menu-2-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('page.demos.nested.menu2_1'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Menu3Demo',
|
||||
path: '/demos/nested/menu3',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('page.demos.nested.menu3'),
|
||||
},
|
||||
redirect: '/demos/nested/menu3/menu3-1',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu31Demo',
|
||||
path: 'menu3-1',
|
||||
component: () => import('#/views/demos/nested/menu-3-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('page.demos.nested.menu3_1'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Menu32Demo',
|
||||
path: 'menu3-2',
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
title: $t('page.demos.nested.menu3_2'),
|
||||
},
|
||||
redirect: '/demos/nested/menu3/menu3-2/menu3-2-1',
|
||||
children: [
|
||||
{
|
||||
name: 'Menu321Demo',
|
||||
path: '/demos/nested/menu3/menu3-2/menu3-2-1',
|
||||
component: () =>
|
||||
import('#/views/demos/nested/menu-3-2-1.vue'),
|
||||
meta: {
|
||||
icon: 'ic:round-menu',
|
||||
keepAlive: true,
|
||||
title: $t('page.demos.nested.menu3_2_1'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 新增页面
|
||||
|
||||
新增一个页面,你只需要添加一个路由及对应的页面组件即可。
|
||||
|
||||
### 添加路由
|
||||
|
||||
在对应的路由文件中添加一个路由对象,如下:
|
||||
|
||||
```ts
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { VBEN_LOGO_URL } from '@vben/constants';
|
||||
|
||||
import { BasicLayout } from '#/layouts';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
component: BasicLayout,
|
||||
meta: {
|
||||
icon: 'mdi:home',
|
||||
title: $t('page.home.title'),
|
||||
},
|
||||
name: 'Home',
|
||||
path: '/home',
|
||||
redirect: '/home/index',
|
||||
children: [
|
||||
{
|
||||
name: 'HomeIndex',
|
||||
path: '/home/index',
|
||||
component: () => import('#/views/home/index.vue'),
|
||||
meta: {
|
||||
icon: 'mdi:home',
|
||||
title: $t('page.home.index'),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
```
|
||||
|
||||
### 添加页面组件
|
||||
|
||||
在`#/views/home/`下,新增一个`index.vue`文件,如下:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h1>home page</h1>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 验证
|
||||
|
||||
到这里页面已添加完成,访问 `http://localhost:5555/home/index` 出现对应的页面即可。
|
||||
|
||||
## 路由配置
|
||||
|
||||
路由配置项主要在对象路由的 `meta` 属性中,以下为常用的配置项:
|
||||
|
||||
```ts {5-8}
|
||||
const routes = [
|
||||
{
|
||||
name: 'HomeIndex',
|
||||
path: '/home/index',
|
||||
meta: {
|
||||
icon: 'mdi:home',
|
||||
title: $t('page.home.index'),
|
||||
},
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
::: details 路由Meta配置类型定义
|
||||
|
||||
```ts
|
||||
interface RouteMeta {
|
||||
/**
|
||||
* 激活图标(菜单)
|
||||
*/
|
||||
activeIcon?: string;
|
||||
/**
|
||||
* 当前激活的菜单,有时候不想激活现有菜单,需要激活父级菜单时使用
|
||||
* @default false
|
||||
*/
|
||||
activePath?: string;
|
||||
/**
|
||||
* 是否固定标签页
|
||||
* @default false
|
||||
*/
|
||||
affixTab?: boolean;
|
||||
/**
|
||||
* 固定标签页的顺序
|
||||
* @default 0
|
||||
*/
|
||||
affixTabOrder?: number;
|
||||
/**
|
||||
* 需要特定的角色标识才可以访问
|
||||
* @default []
|
||||
*/
|
||||
authority?: string[];
|
||||
/**
|
||||
* 徽标
|
||||
*/
|
||||
badge?: string;
|
||||
/**
|
||||
* 徽标类型
|
||||
*/
|
||||
badgeType?: 'dot' | 'normal';
|
||||
/**
|
||||
* 徽标颜色
|
||||
*/
|
||||
badgeVariants?:
|
||||
| 'default'
|
||||
| 'destructive'
|
||||
| 'primary'
|
||||
| 'success'
|
||||
| 'warning'
|
||||
| string;
|
||||
/**
|
||||
* 当前路由的子级在菜单中不展现
|
||||
* @default false
|
||||
*/
|
||||
hideChildrenInMenu?: boolean;
|
||||
/**
|
||||
* 当前路由在面包屑中不展现
|
||||
* @default false
|
||||
*/
|
||||
hideInBreadcrumb?: boolean;
|
||||
/**
|
||||
* 当前路由在菜单中不展现
|
||||
* @default false
|
||||
*/
|
||||
hideInMenu?: boolean;
|
||||
/**
|
||||
* 当前路由在标签页不展现
|
||||
* @default false
|
||||
*/
|
||||
hideInTab?: boolean;
|
||||
/**
|
||||
* 图标(菜单/tab)
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* iframe 地址
|
||||
*/
|
||||
iframeSrc?: string;
|
||||
/**
|
||||
* 忽略权限,直接可以访问
|
||||
* @default false
|
||||
*/
|
||||
ignoreAccess?: boolean;
|
||||
/**
|
||||
* 开启KeepAlive缓存
|
||||
*/
|
||||
keepAlive?: boolean;
|
||||
/**
|
||||
* 外链-跳转路径
|
||||
*/
|
||||
link?: string;
|
||||
/**
|
||||
* 路由是否已经加载过
|
||||
*/
|
||||
loaded?: boolean;
|
||||
/**
|
||||
* 标签页最大打开数量
|
||||
* @default false
|
||||
*/
|
||||
maxNumOfOpenTab?: number;
|
||||
/**
|
||||
* 菜单可以看到,但是访问会被重定向到403
|
||||
*/
|
||||
menuVisibleWithForbidden?: boolean;
|
||||
/**
|
||||
* 用于路由->菜单排序
|
||||
*/
|
||||
order?: number;
|
||||
/**
|
||||
* 标题名称
|
||||
*/
|
||||
title: string;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### title
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置页面的标题,会在菜单和标签页中显示。一般会配合国际化使用。
|
||||
|
||||
### icon
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置页面的图标,会在菜单和标签页中显示。一般会配合图标库使用,如果是`http`链接,会自动加载图片。
|
||||
|
||||
### activeIcon
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置页面的激活图标,会在菜单中显示。一般会配合图标库使用,如果是`http`链接,会自动加载图片。
|
||||
|
||||
### keepAlive
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否开启缓存,开启后页面会缓存,不会重新加载,仅在标签页启用时有效。
|
||||
|
||||
### hideInMenu
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否在菜单中隐藏,隐藏后页面不会在菜单中显示。
|
||||
|
||||
### hideInTab
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否在标签页中隐藏,隐藏后页面不会在标签页中显示。
|
||||
|
||||
### hideInBreadcrumb
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否在面包屑中隐藏,隐藏后页面不会在面包屑中显示。
|
||||
|
||||
### hideChildrenInMenu
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面的子页面是否在菜单中隐藏,隐藏后子页面不会在菜单中显示。
|
||||
|
||||
### authority
|
||||
|
||||
- 类型:`string[]`
|
||||
- 默认值:`[]`
|
||||
|
||||
用于配置页面的权限,只有拥有对应权限的用户才能访问页面,不配置则不需要权限。
|
||||
|
||||
### badge
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置页面的徽标,会在菜单显示。
|
||||
|
||||
### badgeType
|
||||
|
||||
- 类型:`'dot' | 'normal'`
|
||||
- 默认值:`'normal'`
|
||||
|
||||
用于配置页面的徽标类型,`dot` 为小红点,`normal` 为文本。
|
||||
|
||||
### badgeVariants
|
||||
|
||||
- 类型:`'default' | 'destructive' | 'primary' | 'success' | 'warning' | string`
|
||||
- 默认值:`'success'`
|
||||
|
||||
用于配置页面的徽标颜色。
|
||||
|
||||
### activePath
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置当前激活的菜单,有时候页面没有显示在菜单内,需要激活父级菜单时使用。
|
||||
|
||||
### affixTab
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否固定标签页,固定后页面不可关闭。
|
||||
|
||||
### affixTabOrder
|
||||
|
||||
- 类型:`number`
|
||||
- 默认值:`0`
|
||||
|
||||
用于配置页面固定标签页的排序, 采用升序排序。
|
||||
|
||||
### iframeSrc
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置内嵌页面的 `iframe` 地址,设置后会在当前页面内嵌对应的页面。
|
||||
|
||||
### ignoreAccess
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面是否忽略权限,直接可以访问。
|
||||
|
||||
### link
|
||||
|
||||
- 类型:`string`
|
||||
- 默认值:`''`
|
||||
|
||||
用于配置外链跳转路径,会在新窗口打开。
|
||||
|
||||
### maxNumOfOpenTab
|
||||
|
||||
- 类型:`number`
|
||||
- 默认值:`-1`
|
||||
|
||||
用于配置标签页最大打开数量,设置后会在打开新标签页时自动关闭最早打开的标签页(仅在打开同名标签页时生效)。
|
||||
|
||||
### menuVisibleWithForbidden
|
||||
|
||||
- 类型:`boolean`
|
||||
- 默认值:`false`
|
||||
|
||||
用于配置页面在菜单可以看到,但是访问会被重定向到403。
|
||||
|
||||
### order
|
||||
|
||||
- 类型:`number`
|
||||
- 默认值:`0`
|
||||
|
||||
用于配置页面的排序,用于路由到菜单排序。
|
||||
|
||||
## 路由刷新
|
||||
|
||||
路由刷新方式如下:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { useRefresh } from '@vben/hooks';
|
||||
|
||||
const { refresh } = useRefresh();
|
||||
|
||||
// 刷新当前路由
|
||||
refresh();
|
||||
</script>
|
||||
```
|
271
website/src/guide/essentials/server.md
Normal file
271
website/src/guide/essentials/server.md
Normal file
@@ -0,0 +1,271 @@
|
||||
# 服务端交互与数据Mock
|
||||
|
||||
::: tip 说明
|
||||
|
||||
本文档介绍如何在开发环境下使用 Mock 数据和与服务端进行交互,涉及到的技术有:
|
||||
|
||||
- [Nitro](https://nitro.unjs.io/) 轻量级后端服务器,可部署在任何地方,项目用作于 Mock 服务器。
|
||||
- [axios](https://axios-http.com/docs/intro) 用于发送 HTTP 请求与服务端进行交互。
|
||||
|
||||
:::
|
||||
|
||||
## 开发环境交互
|
||||
|
||||
如果前端应用和后端接口服务器没有运行在同一个主机上,你需要在开发环境下将接口请求代理到接口服务器。如果是同一个主机,可以直接请求具体的接口地址。
|
||||
|
||||
### 本地开发跨域配置
|
||||
|
||||
::: tip 提示
|
||||
|
||||
本地开发跨域配置项目已经配置好了,如有其他需求,可以自行增加或者调整配置。
|
||||
|
||||
:::
|
||||
|
||||
#### 配置本地开发接口地址
|
||||
|
||||
在项目根目录下的 `.env.development` 文件中配置接口地址,这里配置为 `/api`:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=/api
|
||||
```
|
||||
|
||||
#### 配置开发服务器代理
|
||||
|
||||
开发环境时候,如果需要处理跨域,接口地址在对应的应用目录下的 `vite.config.mts` 文件中配置:
|
||||
|
||||
```ts{8-16}
|
||||
// apps/web-antd/vite.config.mts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
vite: {
|
||||
server: {
|
||||
proxy: {// [!code focus:11]
|
||||
'/api': {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
// mock代理目标地址
|
||||
target: 'http://localhost:5320/api',
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
#### 接口请求
|
||||
|
||||
根据上面的配置,我们可以在前端项目中使用 `/api` 作为接口请求的前缀,例如:
|
||||
|
||||
```ts
|
||||
import axios from 'axios';
|
||||
|
||||
axios.get('/api/user').then((res) => {
|
||||
console.log(res);
|
||||
});
|
||||
```
|
||||
|
||||
此时,请求会被代理到 `http://localhost:5320/api/user`。
|
||||
|
||||
::: warning 注意
|
||||
|
||||
从浏览器控制台的 Network 看,请求是 `http://localhost:5555/api/user`, 这是因为 proxy 配置不会改变本地请求的 url。
|
||||
|
||||
:::
|
||||
|
||||
### 没有跨域时的配置
|
||||
|
||||
如果没有跨域问题,可以直接忽略 [配置开发服务器代理](./server.md#配置开发服务器代理) 配置,直接将接口地址设置在 `VITE_GLOB_API_URL`
|
||||
|
||||
在项目根目录下的 `.env.development` 文件中配置接口地址:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
|
||||
```
|
||||
|
||||
## 生产环境交互
|
||||
|
||||
### 接口地址配置
|
||||
|
||||
在项目根目录下的 `.env.production` 文件中配置接口地址:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
|
||||
```
|
||||
|
||||
::: tip 打包如何动态修改接口地址
|
||||
|
||||
`.env` 文件内的 `VITE_GLOB_*` 开头的变量会在打包的时候注入 `_app.config.js` 文件内。在 `dist/_app.config.js` 修改相应的接口地址后刷新页面即可,不需要在根据不同环境打包多次,一次打包可以用于多个不同接口环境的部署。
|
||||
|
||||
:::
|
||||
|
||||
### 跨域处理
|
||||
|
||||
生产环境如果出现跨域问题,可以使用 `nginx` 代理接口地址 或者后台开启 `cors` 进行处理即可(可参考mock服务)。
|
||||
|
||||
## 接口请求配置
|
||||
|
||||
项目中默认自带了基于 `axios` 封装的基础的请求配置,核心由 `@vben/request` 包提供。项目没有过多的封装,只是简单的封装了一些常用的配置,如有其他需求,可以自行增加或者调整配置。针对不同的app,可能是用到了不同的组件库以及`store`,所以在应用目录下的`src/api/request.ts`文件夹下,有对应的请求配置文件,如`web-antd`项目下的`src/api/request.ts`文件,可以根据自己的需求进行配置。
|
||||
|
||||
### 请求示例
|
||||
|
||||
#### GET 请求
|
||||
|
||||
```ts
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export async function getUserInfo() {
|
||||
return requestClient.get<UserInfo>('/user/info');
|
||||
}
|
||||
```
|
||||
|
||||
#### POST/PUT 请求
|
||||
|
||||
```ts
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export async function saveUser(user: UserInfo) {
|
||||
return requestClient.post<UserInfo>('/user', user);
|
||||
}
|
||||
|
||||
export async function saveUser(user: UserInfo) {
|
||||
return requestClient.put<UserInfo>('/user', user);
|
||||
}
|
||||
|
||||
export async function saveUser(user: UserInfo) {
|
||||
const url = user.id ? `/user/${user.id}` : '/user/';
|
||||
return requestClient.request<UserInfo>(url, {
|
||||
data: user,
|
||||
// 或者 PUT
|
||||
method: user.id ? 'PUT' : 'POST',
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### DELETE 请求
|
||||
|
||||
```ts
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export async function deleteUser(user: UserInfo) {
|
||||
return requestClient.delete<boolean>(`/user/${user.id}`, user);
|
||||
}
|
||||
```
|
||||
|
||||
### 请求配置
|
||||
|
||||
应用内的`src/api/request.ts`可以根据自己应用的情况的需求进行配置:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
import type { HttpResponse } from '@vben/request';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { RequestClient } from '@vben/request';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useAccessStore } from '#/store';
|
||||
|
||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
|
||||
function createRequestClient(baseURL: string) {
|
||||
const client = new RequestClient({
|
||||
baseURL,
|
||||
// 为每个请求携带 Authorization
|
||||
makeAuthorization: () => {
|
||||
return {
|
||||
// 默认
|
||||
key: 'Authorization',
|
||||
tokenHandler: () => {
|
||||
const accessStore = useAccessStore();
|
||||
return {
|
||||
refreshToken: `${accessStore.refreshToken}`,
|
||||
token: `${accessStore.accessToken}`,
|
||||
};
|
||||
},
|
||||
unAuthorizedHandler: async () => {
|
||||
const accessStore = useAccessStore();
|
||||
accessStore.setAccessToken(null);
|
||||
|
||||
if (preferences.app.loginExpiredMode === 'modal') {
|
||||
accessStore.openLoginExpiredModal = true;
|
||||
} else {
|
||||
// 退出登录
|
||||
await accessStore.logout();
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
makeErrorMessage: (msg) => message.error(msg),
|
||||
|
||||
makeRequestHeaders: () => {
|
||||
return {
|
||||
// 为每个请求携带 Accept-Language
|
||||
'Accept-Language': preferences.app.locale,
|
||||
};
|
||||
},
|
||||
});
|
||||
client.addResponseInterceptor<HttpResponse>((response) => {
|
||||
const { data: responseData, status } = response;
|
||||
|
||||
const { code, data, message: msg } = responseData;
|
||||
if (status >= 200 && status < 400 && code === 0) {
|
||||
return data;
|
||||
}
|
||||
throw new Error(msg);
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
export const requestClient = createRequestClient(apiURL);
|
||||
```
|
||||
|
||||
### 多个接口地址
|
||||
|
||||
只需要创建多个 `requestClient` 即可,如:
|
||||
|
||||
```ts
|
||||
const { apiURL, otherApiURL } = useAppConfig(
|
||||
import.meta.env,
|
||||
import.meta.env.PROD,
|
||||
);
|
||||
|
||||
export const requestClient = createRequestClient(apiURL);
|
||||
|
||||
export const otherRequestClient = createRequestClient(otherApiURL);
|
||||
```
|
||||
|
||||
## 数据 Mock
|
||||
|
||||
::: tip 生产环境 Mock
|
||||
|
||||
新版本不再支持生产环境 mock,请使用真实接口。
|
||||
|
||||
:::
|
||||
|
||||
Mock 数据是前端开发过程中必不可少的一环,是分离前后端开发的关键链路。通过预先跟服务器端约定好的接口,模拟请求数据甚至逻辑,能够让前端开发独立自主,不会被服务端的开发进程所阻塞。
|
||||
|
||||
项目使用 [Nitro](https://nitro.unjs.io/) 来进行本地 mock 数据处理。其原理是本地额外启动一个后端服务,是一个真实的后端服务,可以处理请求,返回数据。
|
||||
|
||||
### Nitro 使用
|
||||
|
||||
Mock 服务代码位于`apps/backend-mock`目录下,无需手动启动,已经集成在项目中,只需要在项目根目录下运行`pnpm dev`即可,运行成功之后,控制台会打印 `http://localhost:5320/api`, 访问该地址即可查看 mock 服务。
|
||||
|
||||
[Nitro](https://nitro.unjs.io/) 语法简单,可以根据自己的需求进行配置及开发,具体配置可以查看 [Nitro 文档](https://nitro.unjs.io/)。
|
||||
|
||||
## 关闭 Mock 服务
|
||||
|
||||
mock的本质是一个真实的后端服务,如果不需要 mock 服务,可以在项目根目录下的 `.env.development` 文件中配置 `VITE_NITRO_MOCK=false` 即可关闭 mock 服务。
|
||||
|
||||
```bash
|
||||
# .env.development
|
||||
VITE_NITRO_MOCK=false
|
||||
|
||||
```
|
526
website/src/guide/essentials/settings.md
Normal file
526
website/src/guide/essentials/settings.md
Normal file
@@ -0,0 +1,526 @@
|
||||
# 配置
|
||||
|
||||
## 环境变量配置
|
||||
|
||||
项目的环境变量配置位于应用目录下的 `.env`、`.env.development`、`.env.production`。
|
||||
|
||||
规则与 [Vite Env Variables and Modes](https://vitejs.dev/guide/env-and-mode.html) 一致。格式如下:
|
||||
|
||||
```bash
|
||||
.env # 在所有的环境中被载入
|
||||
.env.local # 在所有的环境中被载入,但会被 git 忽略
|
||||
.env.[mode] # 只在指定的模式中被载入
|
||||
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
|
||||
```
|
||||
|
||||
::: tip
|
||||
|
||||
- 只有以 `VITE_` 开头的变量会被嵌入到客户端侧的包中,你可以在项目代码中这样访问它们:
|
||||
|
||||
```ts
|
||||
console.log(import.meta.env.VITE_PROT);
|
||||
```
|
||||
|
||||
- 以 `VITE_GLOB_*` 开头的的变量,在打包的时候,会被加入 `_app.config.js`配置文件当中. :::
|
||||
|
||||
:::
|
||||
|
||||
## 环境配置说明
|
||||
|
||||
::: code-group
|
||||
|
||||
```bash [.env]
|
||||
# 应用标题
|
||||
VITE_GLOB_APP_TITLE=Vben Admin
|
||||
|
||||
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||
VITE_APP_NAMESPACE=vben-web-antd
|
||||
```
|
||||
|
||||
```bash [.env.development]
|
||||
# 端口号
|
||||
VITE_PORT=5555
|
||||
|
||||
# 资源公共路径,需要以 / 开头和结尾
|
||||
VITE_BASE=/
|
||||
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=/api
|
||||
|
||||
# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
|
||||
VITE_NITRO_MOCK=true
|
||||
|
||||
# 是否打开 devtools,true 为打开,false 为关闭
|
||||
VITE_DEVTOOLS=true
|
||||
|
||||
# 是否注入全局loading
|
||||
VITE_INJECT_APP_LOADING=true
|
||||
```
|
||||
|
||||
```bash [.env.production]
|
||||
# 资源公共路径,需要以 / 开头和结尾
|
||||
VITE_BASE=/
|
||||
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
|
||||
|
||||
# 是否开启压缩,可以设置为 none, brotli, gzip
|
||||
VITE_COMPRESS=gzip
|
||||
|
||||
# 是否开启 PWA
|
||||
VITE_PWA=false
|
||||
|
||||
# vue-router 的模式
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
|
||||
# 是否注入全局loading
|
||||
VITE_INJECT_APP_LOADING=true
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 生产环境动态配置
|
||||
|
||||
当在大仓根目录下,执行 `pnpm build`构建项目之后,会自动在对应的应用下生成 `dist/_app.config.js`文件并插入 `index.html`。
|
||||
|
||||
`_app.config.js` 是一个动态配置文件,可以在项目构建之后,根据不同的环境动态修改配置。内容如下:
|
||||
|
||||
```ts
|
||||
window._VBEN_ADMIN_PRO_APP_CONF_ = {
|
||||
VITE_GLOB_API_URL: 'https://mock-napi.vben.pro/api',
|
||||
VITE_GLOB_APP_TITLE: 'Vben Admin',
|
||||
};
|
||||
Object.freeze(window._VBEN_ADMIN_PRO_APP_CONF_);
|
||||
Object.defineProperty(window, '_VBEN_ADMIN_PRO_APP_CONF_', {
|
||||
configurable: false,
|
||||
writable: false,
|
||||
});
|
||||
```
|
||||
|
||||
### 作用
|
||||
|
||||
`_app.config.js` 用于项目在打包后,需要动态修改配置的需求,如接口地址。不用重新进行打包,可在打包后修改 /`dist/_app.config.js` 内的变量,刷新即可更新代码内的局部变量。这里使用`js`文件,是为了确保配置文件加载顺序保持在前面。
|
||||
|
||||
### 使用
|
||||
|
||||
想要获取 `_app.config.js` 内的变量,需要使用`@vben/hooks`提供的 `useAppConfig`方法。
|
||||
|
||||
```ts
|
||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
```
|
||||
|
||||
### 新增
|
||||
|
||||
新增一个可动态修改的配置项,只需要按照如下步骤即可:
|
||||
|
||||
- 首先在 `.env` 或者对应的开发环境配置文件内,新增需要可动态配置的变量,需要以 `VITE_GLOB_*` 开头的变量,如:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_OTHER_API_URL=https://mock-napi.vben.pro/other-api
|
||||
```
|
||||
|
||||
- 在 `packages/types/global.d.ts`,新增对应的类型定义,如:
|
||||
|
||||
```ts
|
||||
export interface VbenAdminProAppConfigRaw {
|
||||
VITE_GLOB_API_URL: string;
|
||||
VITE_GLOB_APP_TITLE: string;
|
||||
VITE_GLOB_OTHER_API_URL: string; // [!code ++]
|
||||
}
|
||||
|
||||
export interface ApplicationConfig {
|
||||
apiURL: string;
|
||||
appTitle: string;
|
||||
otherApiURL: string; // [!code ++]
|
||||
}
|
||||
```
|
||||
|
||||
到这里,就可以在项目内使用 `useAppConfig`方法获取到新增的配置项了。
|
||||
|
||||
```ts
|
||||
const { otherApiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
```
|
||||
|
||||
::: warning 注意
|
||||
|
||||
`useAppConfig`方法只能在应用内使用,不要耦合到包内部去使用。这里传入 `import.meta.env`和`import.meta.env.PROD`是为了避免这种情况,一个纯粹的包,应避免使用特定构建工具的变量。
|
||||
|
||||
:::
|
||||
|
||||
## 偏好设置
|
||||
|
||||
项目提供了非常丰富的偏好设置,用于动态配置项目的各种功能:
|
||||
|
||||

|
||||
|
||||
如果你找不到文档说明,可以尝试自己配置好以后,点击`复制偏好设置`,覆盖项目默认即可。配置文件位于应用目录下的`preferences.ts`,在这里,你可以覆盖框架默认的配置,实现自定义配置。
|
||||
|
||||
```ts
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
const { appTitle } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
|
||||
/**
|
||||
* @description 项目配置文件
|
||||
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
|
||||
*/
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
name: appTitle,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 框架默认配置
|
||||
|
||||
::: details 查看框架默认配置
|
||||
|
||||
```ts
|
||||
const defaultPreferences: Preferences = {
|
||||
app: {
|
||||
accessMode: 'frontend',
|
||||
authPageLayout: 'panel-right',
|
||||
colorGrayMode: false,
|
||||
colorWeakMode: false,
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
defaultAvatar:
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.5/source/avatar-v1.webp',
|
||||
dynamicTitle: true,
|
||||
enablePreferences: true,
|
||||
isMobile: false,
|
||||
layout: 'sidebar-nav',
|
||||
locale: 'zh-CN',
|
||||
loginExpiredMode: 'page',
|
||||
name: 'Vben Admin',
|
||||
watermark: false,
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
hideOnlyOne: false,
|
||||
showHome: false,
|
||||
showIcon: true,
|
||||
styleType: 'normal',
|
||||
},
|
||||
copyright: {
|
||||
companyName: 'Vben Admin',
|
||||
companySiteLink: 'https://www.vben.pro',
|
||||
date: '2024',
|
||||
enable: true,
|
||||
icp: '',
|
||||
icpLink: '',
|
||||
},
|
||||
footer: {
|
||||
enable: true,
|
||||
fixed: false,
|
||||
},
|
||||
header: {
|
||||
enable: true,
|
||||
hidden: false,
|
||||
mode: 'fixed',
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.5/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
accordion: true,
|
||||
split: true,
|
||||
styleType: 'rounded',
|
||||
},
|
||||
shortcutKeys: {
|
||||
enable: true,
|
||||
globalLockScreen: true,
|
||||
globalLogout: true,
|
||||
globalPreferences: true,
|
||||
globalSearch: true,
|
||||
},
|
||||
sidebar: {
|
||||
collapsed: false,
|
||||
collapsedShowTitle: false,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: true,
|
||||
hidden: false,
|
||||
width: 230,
|
||||
},
|
||||
tabbar: {
|
||||
dragable: true,
|
||||
enable: true,
|
||||
height: 36,
|
||||
keepAlive: true,
|
||||
persist: true,
|
||||
showIcon: true,
|
||||
showMaximize: true,
|
||||
showMore: true,
|
||||
showRefresh: true,
|
||||
styleType: 'chrome',
|
||||
},
|
||||
theme: {
|
||||
builtinType: 'default',
|
||||
colorDestructive: 'hsl(348 100% 61%)',
|
||||
colorPrimary: 'hsl(231 98% 65%)',
|
||||
colorSuccess: 'hsl(144 57% 58%)',
|
||||
colorWarning: 'hsl(42 84% 61%)',
|
||||
mode: 'dark',
|
||||
radius: '0.5',
|
||||
semiDarkMenu: true,
|
||||
},
|
||||
transition: {
|
||||
enable: true,
|
||||
loading: true,
|
||||
name: 'fade-slide',
|
||||
progress: true,
|
||||
},
|
||||
widget: {
|
||||
fullscreen: true,
|
||||
globalSearch: true,
|
||||
languageToggle: true,
|
||||
lockScreen: true,
|
||||
notification: true,
|
||||
sidebarToggle: true,
|
||||
themeToggle: true,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: details 查看框架默认配置类型
|
||||
|
||||
```ts
|
||||
interface AppPreferences {
|
||||
/** 权限模式 */
|
||||
accessMode: AccessModeType;
|
||||
/** 登录注册页面布局 */
|
||||
authPageLayout: AuthPageLayoutType;
|
||||
/** 是否开启灰色模式 */
|
||||
colorGrayMode: boolean;
|
||||
/** 是否开启色弱模式 */
|
||||
colorWeakMode: boolean;
|
||||
/** 是否开启紧凑模式 */
|
||||
compact: boolean;
|
||||
/** 是否开启内容紧凑模式 */
|
||||
contentCompact: ContentCompactType;
|
||||
// /** 应用默认头像 */
|
||||
defaultAvatar: string;
|
||||
// /** 开启动态标题 */
|
||||
dynamicTitle: boolean;
|
||||
/** 是否显示偏好设置 */
|
||||
enablePreferences: boolean;
|
||||
/** 是否移动端 */
|
||||
isMobile: boolean;
|
||||
/** 布局方式 */
|
||||
layout: LayoutType;
|
||||
/** 支持的语言 */
|
||||
locale: SupportedLanguagesType;
|
||||
/** 登录过期模式 */
|
||||
loginExpiredMode: LoginExpiredModeType;
|
||||
/** 应用名 */
|
||||
name: string;
|
||||
/**
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
watermark: boolean;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
/** 面包屑是否启用 */
|
||||
enable: boolean;
|
||||
/** 面包屑是否只有一个时隐藏 */
|
||||
hideOnlyOne: boolean;
|
||||
/** 面包屑首页图标是否可见 */
|
||||
showHome: boolean;
|
||||
/** 面包屑图标是否可见 */
|
||||
showIcon: boolean;
|
||||
/** 面包屑风格 */
|
||||
styleType: BreadcrumbStyleType;
|
||||
}
|
||||
|
||||
interface CopyrightPreferences {
|
||||
/** 版权公司名 */
|
||||
companyName: string;
|
||||
/** 版权公司名链接 */
|
||||
companySiteLink: string;
|
||||
/** 版权日期 */
|
||||
date: string;
|
||||
/** 版权是否可见 */
|
||||
enable: boolean;
|
||||
/** 备案号 */
|
||||
icp: string;
|
||||
/** 备案号链接 */
|
||||
icpLink: string;
|
||||
}
|
||||
|
||||
interface FooterPreferences {
|
||||
/** 底栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 底栏是否固定 */
|
||||
fixed: boolean;
|
||||
}
|
||||
|
||||
interface HeaderPreferences {
|
||||
/** 顶栏是否启用 */
|
||||
enable: boolean;
|
||||
/** 顶栏是否隐藏,css-隐藏 */
|
||||
hidden: boolean;
|
||||
/** header显示模式 */
|
||||
mode: LayoutHeaderModeType;
|
||||
}
|
||||
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface NavigationPreferences {
|
||||
/** 导航菜单手风琴模式 */
|
||||
accordion: boolean;
|
||||
/** 导航菜单是否切割,只在 layout=mixed-nav 生效 */
|
||||
split: boolean;
|
||||
/** 导航菜单风格 */
|
||||
styleType: NavigationStyleType;
|
||||
}
|
||||
|
||||
interface SidebarPreferences {
|
||||
/** 侧边栏是否折叠 */
|
||||
collapsed: boolean;
|
||||
/** 侧边栏折叠时,是否显示title */
|
||||
collapsedShowTitle: boolean;
|
||||
/** 侧边栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 菜单自动展开状态 */
|
||||
expandOnHover: boolean;
|
||||
/** 侧边栏扩展区域是否折叠 */
|
||||
extraCollapse: boolean;
|
||||
/** 侧边栏是否隐藏 - css */
|
||||
hidden: boolean;
|
||||
/** 侧边栏宽度 */
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface ShortcutKeyPreferences {
|
||||
/** 是否启用快捷键-全局 */
|
||||
enable: boolean;
|
||||
/** 是否启用全局锁屏快捷键 */
|
||||
globalLockScreen: boolean;
|
||||
/** 是否启用全局注销快捷键 */
|
||||
globalLogout: boolean;
|
||||
/** 是否启用全局偏好设置快捷键 */
|
||||
globalPreferences: boolean;
|
||||
/** 是否启用全局搜索快捷键 */
|
||||
globalSearch: boolean;
|
||||
}
|
||||
|
||||
interface TabbarPreferences {
|
||||
/** 是否开启多标签页拖拽 */
|
||||
dragable: boolean;
|
||||
/** 是否开启多标签页 */
|
||||
enable: boolean;
|
||||
/** 标签页高度 */
|
||||
height: number;
|
||||
/** 开启标签页缓存功能 */
|
||||
keepAlive: boolean;
|
||||
/** 是否持久化标签 */
|
||||
persist: boolean;
|
||||
/** 是否开启多标签页图标 */
|
||||
showIcon: boolean;
|
||||
/** 显示最大化按钮 */
|
||||
showMaximize: boolean;
|
||||
/** 显示更多按钮 */
|
||||
showMore: boolean;
|
||||
/** 显示刷新按钮 */
|
||||
showRefresh: boolean;
|
||||
/** 标签页风格 */
|
||||
styleType: TabsStyleType;
|
||||
}
|
||||
|
||||
interface ThemePreferences {
|
||||
/** 内置主题名 */
|
||||
builtinType: BuiltinThemeType;
|
||||
/** 错误色 */
|
||||
colorDestructive: string;
|
||||
/** 主题色 */
|
||||
colorPrimary: string;
|
||||
/** 成功色 */
|
||||
colorSuccess: string;
|
||||
/** 警告色 */
|
||||
colorWarning: string;
|
||||
/** 当前主题 */
|
||||
mode: ThemeModeType;
|
||||
/** 圆角 */
|
||||
radius: string;
|
||||
/** 是否开启半深色菜单(只在theme='light'时生效) */
|
||||
semiDarkMenu: boolean;
|
||||
}
|
||||
|
||||
interface TransitionPreferences {
|
||||
/** 页面切换动画是否启用 */
|
||||
enable: boolean;
|
||||
// /** 是否开启页面加载loading */
|
||||
loading: boolean;
|
||||
/** 页面切换动画 */
|
||||
name: PageTransitionType | string;
|
||||
/** 是否开启页面加载进度动画 */
|
||||
progress: boolean;
|
||||
}
|
||||
|
||||
interface WidgetPreferences {
|
||||
/** 是否启用全屏部件 */
|
||||
fullscreen: boolean;
|
||||
/** 是否启用全局搜索部件 */
|
||||
globalSearch: boolean;
|
||||
/** 是否启用语言切换部件 */
|
||||
languageToggle: boolean;
|
||||
/** 是否开启锁屏功能 */
|
||||
lockScreen: boolean;
|
||||
/** 是否显示通知部件 */
|
||||
notification: boolean;
|
||||
/** 是否显示侧边栏显示/隐藏部件 */
|
||||
sidebarToggle: boolean;
|
||||
/** 是否显示主题切换部件 */
|
||||
themeToggle: boolean;
|
||||
}
|
||||
|
||||
interface Preferences {
|
||||
/** 全局配置 */
|
||||
app: AppPreferences;
|
||||
/** 顶栏配置 */
|
||||
breadcrumb: BreadcrumbPreferences;
|
||||
/** 版权配置 */
|
||||
copyright: CopyrightPreferences;
|
||||
/** 底栏配置 */
|
||||
footer: FooterPreferences;
|
||||
/** 面包屑配置 */
|
||||
header: HeaderPreferences;
|
||||
/** logo配置 */
|
||||
logo: LogoPreferences;
|
||||
/** 导航配置 */
|
||||
navigation: NavigationPreferences;
|
||||
/** 快捷键配置 */
|
||||
shortcutKeys: ShortcutKeyPreferences;
|
||||
/** 侧边栏配置 */
|
||||
sidebar: SidebarPreferences;
|
||||
/** 标签页配置 */
|
||||
tabbar: TabbarPreferences;
|
||||
/** 主题配置 */
|
||||
theme: ThemePreferences;
|
||||
/** 动画配置 */
|
||||
transition: TransitionPreferences;
|
||||
/** 功能配置 */
|
||||
widget: WidgetPreferences;
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: warning 注意
|
||||
|
||||
- `overridesPreferences`方法只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置。
|
||||
- 任何配置项都可以覆盖,只需要在`overridesPreferences`方法内覆盖即可,不要修改默认配置文件。
|
||||
|
||||
:::
|
106
website/src/guide/essentials/styles.md
Normal file
106
website/src/guide/essentials/styles.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 样式
|
||||
|
||||
::: tip 前言
|
||||
|
||||
对于 vue 项目,[官方文档](https://vuejs.org/api/sfc-css-features.html#deep-selectors) 对语法已经有比较详细的介绍,这里主要是介绍项目中的样式文件结构和使用。
|
||||
|
||||
:::
|
||||
|
||||
## 项目结构
|
||||
|
||||
项目中的样式文件存放在 `@vben/styles`,包含一些全局样式,如重置样式、全局变量等,它继承了 `@vben-core/design` 的样式和能力,可以根据项目需求进行覆盖。
|
||||
|
||||
## Scss
|
||||
|
||||
项目中使用 `scss` 作为样式预处理器,可以在项目中使用 `scss` 的特性,如变量、函数、混合等。
|
||||
|
||||
```vue
|
||||
<style lang="scss" scoped>
|
||||
$font-size: 30px;
|
||||
|
||||
.box {
|
||||
.title {
|
||||
color: green;
|
||||
font-size: $font-size;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## Postcss
|
||||
|
||||
如果你不习惯使用 `scss`,也可以使用 `postcss`,它是一个更加强大的样式处理器,可以使用更多的插件,项目内置了 [postcss-nested](https://github.com/postcss/postcss-nested) 插件,配置 `Css Variables`,完全可以取代 `scss`。
|
||||
|
||||
```vue
|
||||
<style scoped>
|
||||
.box {
|
||||
--font-size: 30px;
|
||||
.title {
|
||||
color: green;
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## Tailwind CSS
|
||||
|
||||
项目中集成了 [Tailwind CSS](https://tailwindcss.com/),可以在项目中使用 `tailwindcss` 的类名,快速构建页面。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="bg-white p-4">
|
||||
<p class="text-green">hello world</p>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
## BEM 规范
|
||||
|
||||
样式冲突的另一种选择,是使用 `BEM` 规范。如果选择 `scss` ,建议使用 `BEM` 命名规范,可以更好的管理样式。项目默认提供了`useNamespace`函数,可以方便的生成命名空间。
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { useNamespace } from '@vben-core/hooks';
|
||||
|
||||
const { b, e, is } = useNamespace('menu');
|
||||
</script>
|
||||
<template>
|
||||
<div :class="[b()]">
|
||||
<div :class="[e('item'), is('active', true)]">item1</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
// 如果你在应用内使用,这行代码可以省略,已经在所有的应用内全局引入了
|
||||
@import (reference) '@vben/styles/global';
|
||||
@include b('menu') {
|
||||
color: black;
|
||||
|
||||
@include e('item') {
|
||||
background-color: black;
|
||||
|
||||
@include is('active') {
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## CSS Modules
|
||||
|
||||
针对样式冲突问题,还有一种方案是使用 `CSS Modules` 模块化方案。使用方式如下。
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<p :class="$style.red">This should be red</p>
|
||||
</template>
|
||||
|
||||
<style module>
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
更多用法可以见 [CSS Modules 官方文档](https://vuejs.org/api/sfc-css-features.html#css-modules)。
|
276
website/src/guide/in-depth/access.md
Normal file
276
website/src/guide/in-depth/access.md
Normal file
@@ -0,0 +1,276 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 权限
|
||||
|
||||
框架内置了两种权限控制方式:
|
||||
|
||||
- 通过用户角色来判断菜单或者按钮是否可以访问
|
||||
- 通过接口来判断菜单或者按钮是否可以访问
|
||||
|
||||
## 前端访问控制
|
||||
|
||||
**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoutes` 添加到路由实例,实现权限的过滤。
|
||||
|
||||
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
|
||||
|
||||
### 步骤
|
||||
|
||||
- 确保当前模式为前端访问控制模式
|
||||
|
||||
调整对应应用目录下的`preferences.ts`,确保`accessMode='frontend'`。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
// 默认值,可不填
|
||||
accessMode: 'frontend',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- 配置路由权限
|
||||
|
||||
**如果不配置,默认可见**
|
||||
|
||||
```ts {3}
|
||||
{
|
||||
meta: {
|
||||
authority: ['super'],
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
- 确保接口返回的角色和路由表的权限匹配
|
||||
|
||||
可查看应用下的 `src/store/modules/access`,找到下面代码,
|
||||
|
||||
```ts
|
||||
// 设置登录用户信息,需要确保 userInfo.roles 是一个数组,且包含路由表中的权限
|
||||
// 例如:userInfo.roles=['super', 'admin']
|
||||
coreStoreAccess.setUserInfo(userInfo);
|
||||
```
|
||||
|
||||
到这里,就已经配置完成,你需要确保登录后,接口返回的角色和路由表的权限匹配,否则无法访问。
|
||||
|
||||
### 菜单可见,但禁止访问
|
||||
|
||||
有时候,我们需要菜单可见,但是禁止访问,可以通过下面的方式实现,设置 `menuVisibleWithForbidden` 为 `true`,此时菜单可见,但是禁止访问,会跳转403页面。
|
||||
|
||||
```ts
|
||||
{
|
||||
meta: {
|
||||
menuVisibleWithForbidden: true,
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
## 后端访问控制
|
||||
|
||||
**实现原理**: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 router.addRoutes 添加到路由实例,实现权限的动态生成。
|
||||
|
||||
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
|
||||
|
||||
### 步骤
|
||||
|
||||
- 确保当前模式为前端访问控制模式
|
||||
|
||||
调整对应应用目录下的`preferences.ts`,确保`accessMode='backend'`。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
accessMode: 'backend',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- 确保接口返回的菜单数据结构正确
|
||||
|
||||
可查看应用下的 `src/router/access.ts`,找到下面代码,
|
||||
|
||||
```ts {5}
|
||||
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
fetchMenuListAsync: async () => {
|
||||
// 这个接口为后端返回的菜单数据
|
||||
return await getAllMenus();
|
||||
},
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- 接口返回菜单数据,可看注释说明
|
||||
|
||||
::: details 接口返回菜单数据示例
|
||||
|
||||
```ts
|
||||
const dashboardMenus = [
|
||||
{
|
||||
// 这里固定写死 BasicLayout,不可更改
|
||||
component: 'BasicLayout',
|
||||
meta: {
|
||||
order: -1,
|
||||
title: 'page.dashboard.title',
|
||||
},
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
redirect: '/analytics',
|
||||
children: [
|
||||
{
|
||||
name: 'Analytics',
|
||||
path: '/analytics',
|
||||
// 这里为页面的路径,需要去掉 views/ 和 .vue
|
||||
component: '/dashboard/analytics/index',
|
||||
meta: {
|
||||
affixTab: true,
|
||||
title: 'page.dashboard.analytics',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Workspace',
|
||||
path: '/workspace',
|
||||
component: '/dashboard/workspace/index',
|
||||
meta: {
|
||||
title: 'page.dashboard.workspace',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
到这里,就已经配置完成,你需要确保登录后,接口返回的菜单格式正确,否则无法访问。
|
||||
|
||||
## 按钮细粒度控制
|
||||
|
||||
在某些情况下,我们需要对按钮进行细粒度的控制,我们可以借助接口或者角色来控制按钮的显示。
|
||||
|
||||
### 权限码
|
||||
|
||||
权限码为接口返回的权限码,通过权限码来判断按钮是否显示,逻辑在`src/store/modules/access`下:
|
||||
|
||||
```ts
|
||||
// 获取用户信息并存储到 accessStore 中
|
||||
// Get user information and store it in accessStore
|
||||
const [fetchUserInfoResult, accessCodes] = await Promise.all([
|
||||
fetchUserInfo(),
|
||||
getAccessCodes(),
|
||||
]);
|
||||
|
||||
userInfo = fetchUserInfoResult;
|
||||
coreStoreAccess.setUserInfo(userInfo);
|
||||
coreStoreAccess.setAccessCodes(accessCodes);
|
||||
```
|
||||
|
||||
找到 `getAccessCodes` 对应的接口,可根据业务逻辑进行调整。
|
||||
|
||||
权限吗返回的数据结构为字符串数组,例如:`['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010']`
|
||||
|
||||
有了权限码,就可以使用 `@vben/access` 提供的`AccessControl`组件及API来进行按钮的显示与隐藏。
|
||||
|
||||
#### 组件方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl, useAccess } from '@vben/access';
|
||||
|
||||
const { accessMode, hasAccessByCodes } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 需要指明 type="code" -->
|
||||
<AccessControl :codes="['AC_100100']" type="code">
|
||||
<Button> Super 账号可见 ["AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_100030']" type="code">
|
||||
<Button> Admin 账号可见 ["AC_100010"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_1000001']" type="code">
|
||||
<Button> User 账号可见 ["AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['AC_100100', 'AC_100010']" type="code">
|
||||
<Button> Super & Admin 账号可见 ["AC_100100","AC_1000001"] </Button>
|
||||
</AccessControl>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### API方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl, useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByCodes } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button v-if="hasAccessByCodes(['AC_100100'])">
|
||||
Super 账号可见 ["AC_1000001"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_100030'])">
|
||||
Admin 账号可见 ["AC_100010"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_1000001'])">
|
||||
User 账号可见 ["AC_1000001"]
|
||||
</Button>
|
||||
<Button v-if="hasAccessByCodes(['AC_100100', 'AC_1000001'])">
|
||||
Super & Admin 账号可见 ["AC_100100","AC_1000001"]
|
||||
</Button>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 角色
|
||||
|
||||
角色判断方式不需要接口返回的权限码,直接通过角色来判断按钮是否显示。
|
||||
|
||||
#### 组件方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { AccessControl } from '@vben/access';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccessControl :codes="['super']">
|
||||
<Button> Super 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['admin']">
|
||||
<Button> Admin 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['user']">
|
||||
<Button> User 角色可见 </Button>
|
||||
</AccessControl>
|
||||
<AccessControl :codes="['super', 'admin']">
|
||||
<Button> Super & Admin 角色可见 </Button>
|
||||
</AccessControl>
|
||||
</template>
|
||||
```
|
||||
|
||||
#### API方式
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { useAccess } from '@vben/access';
|
||||
|
||||
const { hasAccessByRoles } = useAccess();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Button v-if="hasAccessByRoles(['super'])"> Super 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['admin'])"> Admin 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['user'])"> User 账号可见 </Button>
|
||||
<Button v-if="hasAccessByRoles(['super', 'admin'])">
|
||||
Super & Admin 账号可见
|
||||
</Button>
|
||||
</template>
|
||||
```
|
84
website/src/guide/in-depth/features.md
Normal file
84
website/src/guide/in-depth/features.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# 功能
|
||||
|
||||
一些常用的功能合集。
|
||||
|
||||
## 登录认证过期
|
||||
|
||||
当接口返回401状态码时,框架会认为登录认证过期,登录超时会跳转到登录页或者打开登录弹窗。在应用目录下的`preferences.ts`可以配置:
|
||||
|
||||
### 跳转登录页面
|
||||
|
||||
登录超时会跳转到登录页
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
loginExpiredMode: 'page',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 打开登录弹窗
|
||||
|
||||
登录超时会打开登录弹窗
|
||||
|
||||

|
||||
|
||||
配置:
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
loginExpiredMode: 'model',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 动态标题
|
||||
|
||||
- 默认值:`true`
|
||||
|
||||
开启后网页标题随着路由的`title`而变化。在应用目录下的`preferences.ts`,开启或者关闭即可。
|
||||
|
||||
```ts
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
dynamicTitle: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 页面水印
|
||||
|
||||
- 默认值:`false`
|
||||
|
||||
开启后网页会显示水印。在应用目录下的`preferences.ts`,开启或者关闭即可。
|
||||
|
||||
```ts
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
watermark: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
如果你想更新水印的内容,可以这么做,参数可以参考 [watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/):
|
||||
|
||||
```ts
|
||||
import { useWatermark } from '@vben/hooks';
|
||||
|
||||
const { destroyWatermark, updateWatermark } = useWatermark();
|
||||
|
||||
await updateWatermark({
|
||||
// 水印内容
|
||||
content: 'hello my watermark',
|
||||
});
|
||||
```
|
1
website/src/guide/in-depth/layout.md
Normal file
1
website/src/guide/in-depth/layout.md
Normal file
@@ -0,0 +1 @@
|
||||
# 布局
|
37
website/src/guide/in-depth/loading.md
Normal file
37
website/src/guide/in-depth/loading.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 全局loading
|
||||
|
||||
全局 loading 指的是页面刷新时出现的加载效果,通常是一个旋转的图标:
|
||||
|
||||

|
||||
|
||||
## 原理
|
||||
|
||||
由 `vite-plugin-inject-app-loading` 插件实现,插件会在每个页面的注入一个全局的 loading html。
|
||||
|
||||
## 关闭
|
||||
|
||||
如果你不需要全局 loading,可以在 `.env` 文件中关闭:
|
||||
|
||||
```bash
|
||||
VITE_INJECT_APP_LOADING=false
|
||||
```
|
||||
|
||||
## 自定义
|
||||
|
||||
如果你想要自定义全局 loading,可以在应用目录下,与`index.html`同级,创建一个`loading.html`文件,插件会自动读取并注入。这个html可以自行定义样式和动画。
|
||||
|
||||
::: tip
|
||||
|
||||
- 你可以使用跟`index.html`一样的语法,比如`VITE_GLOB_APP_TITLE`变量,来获取应用的标题。
|
||||
- 必须保证有一个`id="__app-loading__"`的元素,插件会在这个元素下注入内容。
|
||||
- 必须保证有一个`style[data-app-loading="inject-css"]`的元素,插件会在这个元素下注入样式。
|
||||
|
||||
```html
|
||||
<style data-app-loading="inject-css">
|
||||
/* ... */
|
||||
</style>
|
||||
<div id="__app-loading__">
|
||||
<!-- ... -->
|
||||
<div class="title"><%= VITE_GLOB_APP_TITLE %></div>
|
||||
</div>
|
||||
```
|
223
website/src/guide/in-depth/locale.md
Normal file
223
website/src/guide/in-depth/locale.md
Normal file
@@ -0,0 +1,223 @@
|
||||
# 国际化
|
||||
|
||||
项目已经集成了 [Vue i18n](https://kazupon.github.io/vue-i18n/),并且已经配置好了中文和英文的语言包。
|
||||
|
||||
## IDE 插件
|
||||
|
||||
如果你使用的 vscode 开发工具,则推荐安装 [i18n Ally](https://marketplace.visualstudio.com/items?itemName=Lokalise.i18n-ally) 这个插件。它可以帮助你更方便的管理国际化的文案,安装了该插件后,你的代码内可以实时看到对应的语言内容:
|
||||
|
||||

|
||||
|
||||
## 配置默认语言
|
||||
|
||||
只需要覆盖默认的偏好设置即可,在对应的应用内,找到 `src/preferences.ts` 文件,修改 `locale` 的值即可:
|
||||
|
||||
```ts {3}
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 动态切换语言
|
||||
|
||||
切换语言有两部分组成:
|
||||
|
||||
- 更新偏好设置
|
||||
- 加载对应的语言包
|
||||
|
||||
```ts
|
||||
import type { SupportedLanguagesType } from '@vben/locales';
|
||||
import { loadLocaleMessages } from '@vben/locales';
|
||||
import { updatePreferences } from '@vben/preferences';
|
||||
|
||||
async function updateLocale(value: string) {
|
||||
// 1. 更新偏好设置
|
||||
const locale = value as SupportedLanguagesType;
|
||||
updatePreferences({
|
||||
app: {
|
||||
locale,
|
||||
},
|
||||
});
|
||||
// 2. 加载对应的语言包
|
||||
await loadLocaleMessages(locale);
|
||||
}
|
||||
|
||||
updateLocale('en-US');
|
||||
```
|
||||
|
||||
## 新增翻译文本
|
||||
|
||||
::: warning 注意
|
||||
|
||||
- 请不要将业务翻译文本放到 `@vben/locales` 内,这样可以更好的管理业务和通用的翻译文本。
|
||||
- 有多个语言包的情况下,新增翻译文本时,需要在所有语言包内新增对应的文本。
|
||||
|
||||
:::
|
||||
|
||||
新增翻译文本,只需要在对应的应用内,找到 `src/locales/langs/`,新增对应的文本即可,例:
|
||||
|
||||
**src/locales/langs/zh-CN.ts**
|
||||
|
||||
````ts
|
||||
```json
|
||||
{
|
||||
"about": {
|
||||
"desc": "Vben Admin 是一个现代的管理模版。"
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
**src/locales/langs/en-US.ts**
|
||||
|
||||
````ts
|
||||
```json
|
||||
{
|
||||
"about": {
|
||||
"desc": "Vben Admin is a modern management template."
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
## 使用翻译文本
|
||||
|
||||
通过 `@vben/locales`,你可以很方便的使用翻译文本:
|
||||
|
||||
### 在代码中使用
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
const items = computed(() => [{ title: $t('about.desc') }]);
|
||||
</script>
|
||||
<template>
|
||||
<div>{{ $t('about.desc') }}</div>
|
||||
<template v-for="item in items.value">
|
||||
<div>{{ item.title }}</div>
|
||||
</template>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 新增语言包
|
||||
|
||||
如果你需要新增语言包,需要按照以下步骤进行:
|
||||
|
||||
- 在 `packages/locales/langs` 目录下新增对应的语言包文件,例:`zh-TW.json`,并翻译对应的文本。
|
||||
- 在对应的应用内,找到 `src/locales/langs` 文件,新增对应的语言包 `zh-TW.json`
|
||||
- 在 `packages/constants/src/core.ts`内,新增对应的语言:
|
||||
|
||||
```ts
|
||||
export interface LanguageOption {
|
||||
label: string;
|
||||
value: 'en-US' | 'zh-CN'; // [!code --]
|
||||
value: 'en-US' | 'zh-CN' | 'zh-TW'; // [!code ++]
|
||||
}
|
||||
export const SUPPORT_LANGUAGES: LanguageOption[] = [
|
||||
{
|
||||
label: '简体中文',
|
||||
value: 'zh-CN',
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en-US',
|
||||
},
|
||||
{
|
||||
label: '繁体中文', // [!code ++]
|
||||
value: 'zh-TW', // [!code ++]
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
- 在 `packages/locales/typing.ts`内,新增 Typescript 类型:
|
||||
|
||||
```ts
|
||||
export type SupportedLanguagesType = 'en-US' | 'zh-CN'; // [!code --]
|
||||
export type SupportedLanguagesType = 'en-US' | 'zh-CN' | 'zh-TW'; // [!code ++]
|
||||
```
|
||||
|
||||
到这里,你就可以在项目内使用新增的语言包了。
|
||||
|
||||
## 界面切换语言功能
|
||||
|
||||
如果你想关闭界面上的语言切换显示按钮,在对应的应用内,找到 `src/preferences.ts` 文件,修改 `locale` 的值即可:
|
||||
|
||||
```ts {3}
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
widget: {
|
||||
languageToggle: false,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## 远程加载语言包
|
||||
|
||||
::: tip 提示
|
||||
|
||||
通过项目自带的`request`工具进行接口请求时,默认请求头里会带上 [Accept-Language](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Language) ,服务端可根据请求头进行动态数据国际化处理。
|
||||
|
||||
:::
|
||||
|
||||
每个应用都有一个独立的语言包,它可以覆盖通用的语言配置,你可以通过远程加载的方式来获取对应的语言包,只需要在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadMessages` 方法即可:
|
||||
|
||||
```ts {3-4}
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
// 这里修改为远程接口加载数据即可
|
||||
localesMap[lang](),
|
||||
loadThirdPartyMessage(lang),
|
||||
]);
|
||||
return appLocaleMessages.default;
|
||||
}
|
||||
```
|
||||
|
||||
## 第三方语言包
|
||||
|
||||
不同应用内使用的第三方组件库或者插件国际化方式可能不一致,所以需要差别处理。 如果你需要引入第三方的语言包,你可以在对应的应用内,找到 `src/locales/index.ts` 文件,修改 `loadThirdPartyMessage` 方法即可:
|
||||
|
||||
```ts
|
||||
/**
|
||||
* 加载dayjs的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||
let locale;
|
||||
switch (lang) {
|
||||
case 'zh-CN': {
|
||||
locale = await import('dayjs/locale/zh-cn');
|
||||
break;
|
||||
}
|
||||
case 'en-US': {
|
||||
locale = await import('dayjs/locale/en');
|
||||
break;
|
||||
}
|
||||
// 默认使用英语
|
||||
default: {
|
||||
locale = await import('dayjs/locale/en');
|
||||
}
|
||||
}
|
||||
dayjs.locale(locale);
|
||||
}
|
||||
```
|
||||
|
||||
## 移除国际化
|
||||
|
||||
首先,不是很建议移除国际化,因为国际化是一个很好的开发习惯,但是如果你真的需要移除国际化,你可以直接使用中文文案,然后保留项目自带的语言包即可,整体开发体验不会影响。移除国际化的步骤如下:
|
||||
|
||||
- 隐藏界面上的语言切换按钮,见:[界面切换语言功能](#界面切换语言功能)
|
||||
- 修改默认语言,见:[配置默认语言](#配置默认语言)
|
||||
- 关闭 `vue-i18n`的警告提示,在`src/locales/index.ts`文件内,修改`missingWarn`为`false`即可:
|
||||
|
||||
```ts
|
||||
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
|
||||
await coreSetup(app, {
|
||||
defaultLocale: preferences.app.locale,
|
||||
loadMessages,
|
||||
missingWarn: !import.meta.env.PROD, // [!code --]
|
||||
missingWarn: false, // [!code ++]
|
||||
...options,
|
||||
});
|
||||
}
|
||||
```
|
1243
website/src/guide/in-depth/theme.md
Normal file
1243
website/src/guide/in-depth/theme.md
Normal file
File diff suppressed because it is too large
Load Diff
3
website/src/guide/introduction/changelog.md
Normal file
3
website/src/guide/introduction/changelog.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 更新日志
|
||||
|
||||
TODO
|
84
website/src/guide/introduction/quick-start.md
Normal file
84
website/src/guide/introduction/quick-start.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# 快速开始 {#quick-start}
|
||||
|
||||
## 前置准备
|
||||
|
||||
::: info 环境要求
|
||||
|
||||
在启动项目前,你需要确保你的环境满足以下要求:
|
||||
|
||||
- [Node.js](https://nodejs.org/en) 20 及以上版本,推荐使用 [fnm](https://github.com/Schniz/fnm) 或者 [mvn](https://github.com/nvm-sh/nvm) 进行版本管理。
|
||||
- [Git](https://git-scm.com/) 任意版本。
|
||||
|
||||
验证你的环境是否满足以上要求,你可以通过以下命令查看版本:
|
||||
|
||||
```bash
|
||||
# 出现相应 node LTS版本即可
|
||||
node -v
|
||||
# 出现相应 git 版本即可
|
||||
git -v
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## 启动项目
|
||||
|
||||
### 获取源码
|
||||
|
||||
::: code-group
|
||||
|
||||
```bash [GitHub]
|
||||
# clone 代码
|
||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||
```
|
||||
|
||||
```bash [Gitee]
|
||||
# clone 代码
|
||||
# Gitee 的代码可能不是最新的
|
||||
git clone https://gitee.com/annsion/vue-vben-admin.git
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: danger 注意
|
||||
|
||||
注意存放代码的目录及所有父级目录不能存在中文、韩文、日文以及空格,否则安装依赖后启动会出错。
|
||||
|
||||
:::
|
||||
|
||||
### 安装依赖
|
||||
|
||||
在你的代码目录内打开终端,并执行以下命令:
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd vue-vben-admin
|
||||
|
||||
# 使用项目指定的pnpm版本进行依赖安装
|
||||
corepack enable
|
||||
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
```
|
||||
|
||||
::: tip 注意
|
||||
|
||||
项目只支持使用 `pnpm` 进行依赖安装,默认会使用 `corepack` 来安装指定版本的 `pnpm`。:
|
||||
|
||||
:::
|
||||
|
||||
### 运行项目
|
||||
|
||||
执行以下命令即可运行项目:
|
||||
|
||||
```bash
|
||||
# 启动项目
|
||||
pnpm dev
|
||||
|
||||
```
|
||||
|
||||
现在,你可以在浏览器访问 [http://localhost:5555](http://localhost:5555) 查看项目。
|
3
website/src/guide/introduction/roadmap.md
Normal file
3
website/src/guide/introduction/roadmap.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# 路线图
|
||||
|
||||
TODO:
|
51
website/src/guide/introduction/vben.md
Normal file
51
website/src/guide/introduction/vben.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 关于 Vben Admin
|
||||
|
||||
::: info 你正在阅读的是 [Vben Admin](https://github.com/vbenjs/vue-vben-admin) 的文档!
|
||||
|
||||
- Vben Admin 2.x 目前已经存档,只修复一些严重的问题。
|
||||
- 新版本与旧版本不兼容,如果你使用的是旧版本,请查看 [Vue Vben Admin 2.x 文档](https://doc.vvbin.cn)
|
||||
- 如发现文档有误,欢迎提提交 Issue 帮助我们改进。
|
||||
- 如果你只是想体验一下,你可以查看 [快速开始](./quick-start.md)。
|
||||
|
||||
:::
|
||||
|
||||
[Vben Admin](https://github.com/vbenjs/vue-vben-admin) 是一个基于 [Vue3.0](https://github.com/vuejs/core)、[Vite](https://github.com/vitejs/vite)、 [TypeScript](https://www.typescriptlang.org/) 的后台解决方案,目标是为开发中大型项目提供开箱即用的解决方案。包括二次封装组件、utils、hooks、动态菜单、权限校验、多主题配置、按钮级别权限控制等功能。项目会使用前端较新的技术栈,可以作为项目的启动模版,以帮助你快速搭建企业级中后台产品原型。也可以作为一个示例,用于学习 `vue3`、`vite`、`ts` 等主流技术。该项目会持续跟进最新技术,并将其应用在项目中。
|
||||
|
||||
## 特点
|
||||
|
||||
- **最新技术栈**:使用 `Vue3`、`Vite`、`TypeScript` 等前端前沿技术开发。
|
||||
- **国际化**:内置完善的国际化方案,支持多语言切换。
|
||||
- **权限验证**:完善的权限验证方案,按钮级别权限控制。
|
||||
- **多主题**:内置多种主题配置&黑暗某事,满足个性化需求。
|
||||
- **动态菜单**:支持动态菜单,可以根据权限配置显示菜单。
|
||||
- **Mock 数据**:基于 Nitro 的本地高性能 Mock 数据方案。
|
||||
- **组件丰富**:提供了丰富的组件,可以满足大部分的业务需求。
|
||||
- **规范**:代码规范,使用 `ESLint`、`Prettier`、`Stylelint`、`Publint`、`cspell` 等工具保证代码质量。
|
||||
- **工程化**:使用 `Pnpm Monorepo`、`TurboRepo`、`Changeset` 等工具,提高开发效率。
|
||||
- **多UI库支持**:支持 `Ant Design Vue`、`Element Plus`、`Vuetify` 等主流 UI 库,不再限制于特定框架。
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
**本地开发**推荐使用`Chrome 最新版`浏览器,**不支持**`Chrome 80`以下版本。
|
||||
|
||||
**生产环境**支持现代浏览器,不支持 IE。
|
||||
|
||||
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/archive/internet-explorer_9-11/internet-explorer_9-11_48x48.png" alt="IE" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)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/)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/)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/)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/)Safari |
|
||||
| :-: | :-: | :-: | :-: | :-: |
|
||||
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||
|
||||
## 贡献
|
||||
|
||||
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) 还在持续更新中,本项目欢迎您的参与,共同维护,逐步完善,打造更好的中后台解决方案。
|
||||
- 如果你想加入我们,可以多提供一些好的建议或者提交 pr,我们会根据你的活跃度邀请你加入。
|
||||
|
||||
::: info 加入我们
|
||||
|
||||
如果你想加入我们,你可以从以下几个方面开始,我们会根据你的活跃度邀请你加入:
|
||||
|
||||
- 长期提交 `PR`。
|
||||
- 提供一些好的建议。
|
||||
- 参与讨论,帮助解决一些 `issue`。
|
||||
- 共同维护文档。
|
||||
|
||||
:::
|
9
website/src/guide/introduction/why.md
Normal file
9
website/src/guide/introduction/why.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 为什么选择我们?
|
||||
|
||||
首先,我们不会去和其他框架做比较。我们认为每个框架都有自己的特点,适合不同的场景。我们的目标是提供一个简单、易用的框架,让开发者可以快速上手,专注于业务逻辑的开发。所以我们只会不断完善和优化我们的框架,提供更好的体验。
|
||||
|
||||
## 框架历程
|
||||
|
||||
从 Vue Vben Admin 1.x 版本开始,框架经历了许多迭代和优化。从一开始使用 `Vite 0.x` 版本,没有现成的插件,开发了很多自定义插件来弥合 Webpack 和 Vite 之间的差异。虽然很多现在已经被代替,但是我们的初衷一直没有变,就是提供一个简单、易用的框架。
|
||||
|
||||
虽然中间有段时间由社区维护,但我们一直密切关注 Vue Vben Admin 的发展。见证了许多开发者使用 Vben Admin,并提供了许多宝贵的建议和反馈。非常感谢大家的支持和贡献,这些都是我们持续改进 Vben Admin 的动力。新版本中,我们持续收集用户反馈,重新开始,不断优化框架,以提供更好的用户体验。我们的目标是让开发者能够快速上手,专注于业务逻辑的开发。
|
137
website/src/guide/other/faq.md
Normal file
137
website/src/guide/other/faq.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# 常见问题 #{faq}
|
||||
|
||||
::: tip 列举了一些常见的问题
|
||||
|
||||
有问题可以先来这里寻找,如果没有可以在 [GitHub Issue](https://github.com/vbenjs/vue-vben-admin/issues) 搜索或者提交你的问题, 如果是讨论性的问题可以在 [GitHub Discussions](https://github.com/vbenjs/vue-vben-admin/discussions)
|
||||
|
||||
:::
|
||||
|
||||
## 说明
|
||||
|
||||
遇到问题,可以先从以下几个方面查找
|
||||
|
||||
1. 对应模块的 GitHub 仓库 [issue](https://github.com/vbenjs/vue-vben-admin/issues) 搜索
|
||||
2. 从[google](https://www.google.com)搜索问题
|
||||
3. 从[百度](https://www.baidu.com)搜索问题
|
||||
4. 在下面列表找不到问题可以到 issue 提问 [issues](https://github.com/vbenjs/vue-vben-admin/issues)
|
||||
5. 如果不是问题类型的,需要讨论的,请到 [discussions](https://github.com/vbenjs/vue-vben-admin/discussions) 讨论
|
||||
|
||||
## 依赖安装问题
|
||||
|
||||
在 `Monorepo` 项目下,需要养成每次 `git pull`代码都要执行`pnpm install`的习惯,因为经常会有新的依赖包加入,项目在`.husky/git-merge`已经配置了自动执行`pnpm install`,但是有时候会出现问题,如果没有自动执行,建议手动执行一次。
|
||||
|
||||
## 关于缓存更新问题
|
||||
|
||||
项目配置默认是缓存在 `localStorage` 内,所以版本更新后可能有些配置没改变。
|
||||
|
||||
解决方式是每次更新代码的时候修改 `package.json` 内的 `version` 版本号. 因为 localStorage 的 key 是根据版本号来的。所以更新后版本不同前面的配置会失效。重新登录即可
|
||||
|
||||
`VUE_VBEN_ADMIN__DEVELOPMENT__2.0.3__COMMON__LOCAL__KEY__` key 的组成是 [项目名]+[开发环境]+[版本号]+[key]
|
||||
|
||||
## 关于修改配置文件的问题
|
||||
|
||||
当修改 `.env` 等环境文件以及 `vite.config.ts` 文件时,vite 会自动重启服务。
|
||||
|
||||
自动重启有几率出现问题,请重新运行项目即可解决.
|
||||
|
||||
## 本地运行报错
|
||||
|
||||
由于 vite 在本地没有转换代码,且代码中用到了可选链等比较新的语法。所以本地开发需要使用版本较高的浏览器(`Chrome 90+`)进行开发
|
||||
|
||||
## 页面切换后页面空白
|
||||
|
||||
这是由于开启了路由切换动画,且对应的页面组件存在多个根节点导致的,在页面最外层添加`<div></div>`即可
|
||||
|
||||
**错误示例**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<!-- 注释也算一个节点 -->
|
||||
<h1>text h1</h1>
|
||||
<h2>text h2</h2>
|
||||
</template>
|
||||
```
|
||||
|
||||
**正确示例**
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div>
|
||||
<h1>text h1</h1>
|
||||
<h2>text h2</h2>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
- 如果想使用多个根标签,可以禁用路由切换动画
|
||||
- template 下面的根注释节点也算一个节点
|
||||
|
||||
:::
|
||||
|
||||
## 我的代码本地开发可以,打包就不行了
|
||||
|
||||
目前发现这个原因可能有以下,可以从以下原因来排查,如果还有别的可能,欢迎补充
|
||||
|
||||
- 使用了 ctx 这个变量,ctx 本身未暴露出在实例类型内,Vue官方也是说了不要用这个属性。这个属性只是用于内部使用。
|
||||
|
||||
```ts
|
||||
import { getCurrentInstance } from 'vue';
|
||||
getCurrentInstance().ctx.xxxx;
|
||||
```
|
||||
|
||||
## 依赖安装问题
|
||||
|
||||
- 如果依赖安装不了或者启动报错可以尝试执行`pnpm run resintall`。
|
||||
- 如果依赖安装不了或者报错,可以尝试切换手机热点来进行依赖安装。
|
||||
- 如果还是不行,可以自行配置国内镜像安装。
|
||||
- 也可以在项目根目录创建 `.npmrc` 文件,内容如下
|
||||
|
||||
```bash
|
||||
# .npmrc
|
||||
registry = https://registry.npmmirror.com/
|
||||
```
|
||||
|
||||
## 打包文件过大
|
||||
|
||||
- 首先,完整版由于引用了比较多的库文件,所以打包会比较大。可以使用精简版来进行开发
|
||||
|
||||
- 其次建议开启 gzip,使用之后体积会只有原先 1/3 左右。gzip 可以由服务器直接开启。如果是这样,前端不需要构建 `.gz` 格式的文件,如果前端构建了 `.gz` 文件,以 nginx 为例,nginx 需要开启 `gzip_static: on` 这个选项。
|
||||
|
||||
- 开启 gzip 的同时还可以同时开启 `brotli`,比 gzip 更好的压缩。两者可以共存
|
||||
|
||||
**注意**
|
||||
|
||||
- gzip_static: 这个模块需要 nginx 另外安装,默认的 nginx 没有安装这个模块。
|
||||
|
||||
- 开启 `brotli` 也需要 nginx 另外安装模块
|
||||
|
||||
## 运行错误
|
||||
|
||||
如果出现类似以下错误,请检查项目全路径(包含所有父级路径)不能出现中文、日文、韩文。否则将会出现路径访问 404 导致以下问题
|
||||
|
||||
```ts
|
||||
[vite] Failed to resolve module import "ant-design-vue/dist/antd.css-vben-adminode_modulesant-design-vuedistantd.css". (imported by /@/setup/ant-design-vue/index.ts)
|
||||
```
|
||||
|
||||
## 控制台路由警告问题
|
||||
|
||||
如果看到控制台有如下警告,且页面**能正常打开** 可以忽略该警告。
|
||||
|
||||
后续 `vue-router` 可能会提供配置项来关闭警告
|
||||
|
||||
```ts
|
||||
[Vue Router warn]: No match found for location with path "xxxx"
|
||||
```
|
||||
|
||||
## 启动报错
|
||||
|
||||
当出现以下错误信息时,请检查你的 nodejs 版本号是否符合要求
|
||||
|
||||
```bash
|
||||
TypeError: str.matchAll is not a function
|
||||
at Object.extractor (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:146:27)
|
||||
at Extract (vue-vben-admin-main\node_modules@purge-icons\core\dist\index.js:173:54)
|
||||
|
||||
```
|
55
website/src/guide/other/project-update.md
Normal file
55
website/src/guide/other/project-update.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# 项目更新
|
||||
|
||||
## 为什么无法像 npm 插件一样更新
|
||||
|
||||
因为项目是一个完整的项目模版,不是一个插件或者安装包,无法像插件一样更新,你使用代码后,会根据业务需求,进行二次开发,需要自行手动合并升级。
|
||||
|
||||
## 我需要怎么做
|
||||
|
||||
项目采用了 `Monorepo` 的方式进行管理,并将一些比较核心的代码进行了抽离,比如 `packages/@core`、`packages/effects`,只要业务代码没有修改这部分代码,那么你可以直接拉取最新代码,然后合并到你的分支上,只需要简单的处理部分冲突即可。其余文件夹只会进行一些小的调整,不会对业务代码产生影响。
|
||||
|
||||
::: tip 推荐
|
||||
|
||||
建议关注仓库动态,积极去合并,不要长时间积累,否则将会导致合并冲突过多,增加合并难度。
|
||||
|
||||
:::
|
||||
|
||||
## 使用 Git 更新代码
|
||||
|
||||
1. 克隆代码
|
||||
|
||||
```bash
|
||||
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||
```
|
||||
|
||||
2. 添加自己的公司 git 源地址
|
||||
|
||||
```bash
|
||||
# up 为源名称,可以随意设置
|
||||
# gitUrl为开源最新代码
|
||||
git remote add up gitUrl;
|
||||
```
|
||||
|
||||
3. 提交代码到自己公司 git
|
||||
|
||||
```bash
|
||||
# 提交代码到自己公司
|
||||
# main为分支名 需要自行根据情况修改
|
||||
git push up main
|
||||
|
||||
# 同步公司的代码
|
||||
# main为分支名 需要自行根据情况修改
|
||||
git pull up main
|
||||
```
|
||||
|
||||
4. 如何同步开源最新代码
|
||||
|
||||
```bash
|
||||
git pull origin main
|
||||
```
|
||||
|
||||
::: tip 提示
|
||||
|
||||
同步代码的时候会出现冲突。只需要把冲突解决即可
|
||||
|
||||
:::
|
18
website/src/guide/other/remove-code.md
Normal file
18
website/src/guide/other/remove-code.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 移除代码
|
||||
|
||||
## 移除百度统计代码
|
||||
|
||||
在对应应用的 `index.html` 文件中,找到如下代码,删除即可:
|
||||
|
||||
```html
|
||||
<!-- apps/web-antd -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement('script');
|
||||
hm.src = 'https://hm.baidu.com/hm.js?d20a01273820422b6aa2ee41b6c9414d';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
```
|
25
website/src/guide/project/changeset.md
Normal file
25
website/src/guide/project/changeset.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Changeset
|
||||
|
||||
项目内置了 [changeset](https://github.com/changesets/changesets) 作为版本管理工具。Changeset 是一个版本管理工具,它可以帮助我们更好的管理版本,生成 changelog,以及自动发布。
|
||||
|
||||
详细使用方式可查看官方文档,这里不再阐述。如果你不需要它,可以直接忽略。
|
||||
|
||||
## 命令行
|
||||
|
||||
changeset 命令在项目中已经内置:
|
||||
|
||||
### 交互式填写变更集
|
||||
|
||||
```bash
|
||||
|
||||
pnpm run changeset
|
||||
|
||||
```
|
||||
|
||||
### 统一提升版本号
|
||||
|
||||
```bash
|
||||
|
||||
pnpm run version
|
||||
|
||||
```
|
108
website/src/guide/project/cli.md
Normal file
108
website/src/guide/project/cli.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# CLI
|
||||
|
||||
项目中,提供了一些命令行工具,用于一些常用的操作,代码位于 `scrips/vsh` 内。
|
||||
|
||||
## 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh [command] [options]
|
||||
|
||||
```
|
||||
|
||||
## vsh check-circular
|
||||
|
||||
检查整个项目循环引用,如果有循环引用,会在控制台输出循环引用的模块。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh check-circular
|
||||
|
||||
```
|
||||
|
||||
### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------- | ----------------------------------- |
|
||||
| `--staged` | 只检查git暂存区内的文件,默认`false` |
|
||||
|
||||
## vsh check-dep
|
||||
|
||||
检查整个项目依赖情况,并在控制台输出`未使用的依赖`、`未安装的依赖`信息
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh check-dep
|
||||
|
||||
```
|
||||
|
||||
## vsh clean
|
||||
|
||||
删除项目的`node_modules`、`dist`、`.turbo`等目录,清理项目。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh clean
|
||||
|
||||
```
|
||||
|
||||
### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------------- | --------------------------------------- |
|
||||
| `-r,--recursive` | 递归删除整个项目,默认`true` |
|
||||
| `--del-lock` | 是否删除`pnpm-lock.yaml`文件,默认`true` |
|
||||
|
||||
## vsh lint
|
||||
|
||||
对项目进行lint检查,检查项目中的代码是否符合规范。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh lint
|
||||
|
||||
```
|
||||
|
||||
### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| ---------- | ------------------------------ |
|
||||
| `--format` | 检查并尝试修复错误,默认`false` |
|
||||
|
||||
## vsh publint
|
||||
|
||||
对 `Monorepo` 项目进行包规范检查,检查项目中的包是否符合规范。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh publint
|
||||
|
||||
```
|
||||
|
||||
### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| --------- | ---------------------- |
|
||||
| `--check` | 仅执行检查,默认`false` |
|
||||
|
||||
## vsh code-workspace
|
||||
|
||||
生成 `vben-admin.code-workspace` 文件,目前不需要手动执行,会在代码提交时自动执行。
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pnpm vsh code-workspace
|
||||
|
||||
```
|
||||
|
||||
### 选项
|
||||
|
||||
| 选项 | 说明 |
|
||||
| --------------- | -------------------------------------- |
|
||||
| `--auto-commit` | `git commit`时候,自动提交,默认`false` |
|
||||
| `--spaces` | 缩进格式,默认 `2`个缩进 |
|
165
website/src/guide/project/standard.md
Normal file
165
website/src/guide/project/standard.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# 规范
|
||||
|
||||
::: tip 贡献代码
|
||||
|
||||
- 如果你想向项目贡献代码,请确保你的代码符合项目的代码规范。
|
||||
- 如果你使用的是 `vscode`,需要安装以下插件:
|
||||
|
||||
- [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - 脚本代码检查
|
||||
- [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - 代码格式化
|
||||
- [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - 单词语法检查
|
||||
- [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint) - css 格式化
|
||||
|
||||
:::
|
||||
|
||||
## 作用
|
||||
|
||||
具备基本工程素养的同学都会注重编码规范,而代码风格检查(Code Linting,简称 Lint)是保障代码规范一致性的重要手段。
|
||||
|
||||
遵循相应的代码规范有以下好处:
|
||||
|
||||
- 较少 bug 错误率
|
||||
- 高效的开发效率
|
||||
- 更高的可读性
|
||||
|
||||
## 工具
|
||||
|
||||
项目的配置文件位于 `internal/lint-configs` 下,你可以在这里修改各种lint的配置。
|
||||
|
||||
项目内集成了以下几种代码校验工具:
|
||||
|
||||
- [ESLint](https://eslint.org/) 用于 JavaScript 代码检查
|
||||
- [Stylelint](https://stylelint.io/) 用于 CSS 样式检查
|
||||
- [Prettier](https://prettier.io/) 用于代码格式化
|
||||
- [Commitlint](https://commitlint.js.org/) 用于检查 git 提交信息的规范
|
||||
- [Publint](https://publint.dev/) 用于检查 npm 包的规范
|
||||
- [Lint Staged](https://github.com/lint-staged/lint-staged) 用于在 git 提交前运行代码校验
|
||||
- [Cspell](https://cspell.org/) 用于检查拼写错误
|
||||
|
||||
## ESLint
|
||||
|
||||
ESLint 是一个代码规范和错误检查工具,用于识别和报告 TypeScript 代码中的语法错误。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm eslint .
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
eslint 配置文件为 `eslint.config.mjs`,其核心配置放在`internal/lint-configs/eslint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## Stylelint
|
||||
|
||||
Stylelint 用于校验项目内部 css 的风格,加上编辑器的自动修复,可以很好的统一项目内部 css 风格
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm stylelint "**/*.{vue,css,less.scss}"
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
Stylelint 配置文件为 `stylelint.config.mjs`,其核心配置放在`internal/lint-configs/stylelint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## Prettier
|
||||
|
||||
Prettier 可以用于统一项目代码风格,统一的缩进,单双引号,尾逗号等等风格
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm prettier .
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
Prettier 配置文件为 `.prettier.mjs`,其核心配置放在`internal/lint-configs/prettier-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
## CommitLint
|
||||
|
||||
在一个团队中,每个人的 git 的 commit 信息都不一样,五花八门,没有一个机制很难保证规范化,如何才能规范化呢?可能你想到的是 git 的 hook 机制,去写 shell 脚本去实现。这当然可以,其实 JavaScript 有一个很好的工具可以实现这个模板,它就是 commitlint(用于校验 git 提交信息规范)。
|
||||
|
||||
### 配置
|
||||
|
||||
CommitLint 配置文件为 `.commitlintrc.mjs`,其核心配置放在`internal/lint-configs/commitlint-config`目录下,可以根据项目需求进行修改。
|
||||
|
||||
### Git 提交规范
|
||||
|
||||
参考 [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` 类型修改
|
||||
|
||||
### 关闭Git提交规范检查
|
||||
|
||||
如果你想关闭 Git 提交规范检查,有两种方式:
|
||||
|
||||
::: code-group
|
||||
|
||||
```bash [临时关闭]
|
||||
git commit -m 'feat: add home page' --no-verify
|
||||
```
|
||||
|
||||
```bash [永久关闭]
|
||||
# 在 .husky/commit-msg 内注释以下代码即可
|
||||
pnpm exec commitlint --edit "$1" # [!code --]
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Publint
|
||||
|
||||
Publint 是一个用于检查 npm 包的规范的工具,可以检查包的版本号是否符合规范,是否符合标准的 ESM 规范包等等。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm vsh publint
|
||||
```
|
||||
|
||||
## Cspell
|
||||
|
||||
Cspell 是一个用于检查拼写错误的工具,可以检查代码中的拼写错误,避免因为拼写错误导致的 bug。
|
||||
|
||||
### 命令
|
||||
|
||||
```bash
|
||||
pnpm cspell lint \"**/*.ts\" \"**/README.md\" \".changeset/*.md\" --no-progress
|
||||
```
|
||||
|
||||
### 配置
|
||||
|
||||
cspell 配置文件为 `cspell.json`,可以根据项目需求进行修改。
|
||||
|
||||
## Git Hook
|
||||
|
||||
git hook 一般结合各种 lint,在 git 提交代码的时候进行代码风格校验,如果校验没通过,则不会进行提交。需要开发者自行修改后再次进行提交
|
||||
|
||||
### husky
|
||||
|
||||
有一个问题就是校验会校验全部代码,但是我们只想校验我们自己提交的代码,这个时候就可以使用 husky。
|
||||
|
||||
最有效的解决方案就是将 Lint 校验放到本地,常见做法是使用 husky 或者 pre-commit 在本地提交之前先做一次 Lint 校验。
|
||||
|
||||
项目在 `.husky` 内部定义了相应的 hooks
|
||||
|
||||
#### 如何关闭 Husky
|
||||
|
||||
如果你想关闭 Husky,直接删除 `.husky` 目录即可。
|
||||
|
||||
### lint-staged
|
||||
|
||||
用于自动修复提交文件风格问题,其配置文件为 `.lintstagedrc.mjs`,其核心配置放在`internal/lint-configs/lint-staged-config`目录下,可以根据项目需求进行修改。
|
13
website/src/guide/project/tailwindcss.md
Normal file
13
website/src/guide/project/tailwindcss.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Tailwind CSS
|
||||
|
||||
[Tailwind CSS](https://tailwindcss.com/) 是一个实用性优先的CSS框架,用于快速构建自定义设计。
|
||||
|
||||
## 配置
|
||||
|
||||
项目的配置文件位于 `internal/tailwind-config` 下,你可以在这里修改 Tailwind CSS 的配置。
|
||||
|
||||
::: tip 包使用 tailwindcss 的限制
|
||||
|
||||
当前只有对应的包下面存在 `tailwind.config.mjs` 文件才会启用 tailwindcss 的编译,否则不会启用 tailwindcss。如果你是纯粹的 SDK 包,不需要使用 tailwindcss,可以不用创建 `tailwind.config.mjs` 文件。
|
||||
|
||||
:::
|
33
website/src/guide/project/test.md
Normal file
33
website/src/guide/project/test.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# 单元测试
|
||||
|
||||
项目内置了 [Vitest](https://vitest.dev/) 作为单元测试工具。Vitest 是一个基于 Vite 的测试运行器,它提供了一套简单的 API 来编写测试用例。
|
||||
|
||||
## 编写测试用例
|
||||
|
||||
在项目中,我们约定将测试文件名以 `.test.ts` 结尾,或者存放到`__tests__`目录内。例如,创建一个 `utils.ts` 文件,然后同级目录`utils.spec.ts` 文件,
|
||||
|
||||
```ts
|
||||
// utils.test.ts
|
||||
import { expect, test } from 'vitest';
|
||||
import { sum } from './sum';
|
||||
|
||||
test('adds 1 + 2 to equal 3', () => {
|
||||
expect(sum(1, 2)).toBe(3);
|
||||
});
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
|
||||
在大仓根目录下运行以下命令即可:
|
||||
|
||||
```bash
|
||||
pnpm test:unit
|
||||
```
|
||||
|
||||
## 现有单元测试
|
||||
|
||||
项目中已经有一些单元测试用例,可以搜索以`.test.ts`结尾的文件查看,在你更改到相关代码时,可以运行单元测试来保证代码的正确性,建议在开发过程中,保持单元测试的覆盖率,且同时在 CI/CD 流程中运行单元测试,保证测试通过在进行项目部署。
|
||||
|
||||
现有单元测试情况:
|
||||
|
||||

|
33
website/src/guide/project/vite.md
Normal file
33
website/src/guide/project/vite.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Vite Config
|
||||
|
||||
项目封装了一层vite配置,并集成了一些插件,方便在多个包以及应用内复用,使用方式如下:
|
||||
|
||||
## 应用
|
||||
|
||||
```ts
|
||||
// vite.config.mts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
application: {},
|
||||
// vite配置,参考vite官方文档进行覆盖
|
||||
vite: {},
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
## 包
|
||||
|
||||
```ts
|
||||
// vite.config.mts
|
||||
import { defineConfig } from '@vben/vite-config';
|
||||
|
||||
export default defineConfig(async () => {
|
||||
return {
|
||||
library: {},
|
||||
// vite配置,参考vite官方文档进行覆盖
|
||||
vite: {},
|
||||
};
|
||||
});
|
||||
```
|
Reference in New Issue
Block a user