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)。
|
Reference in New Issue
Block a user