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:
Vben
2024-07-28 14:29:05 +08:00
committed by GitHub
parent 14538f7ed5
commit 376fd17a61
225 changed files with 7731 additions and 1784 deletions

View 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
```
运行之后,在自动打开的页面可以看到具体的体积分布,以分析哪些依赖有问题。
![](/guide/report.png)
## 部署
简单的部署只需要将最终生成的静态文件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;
}
}
```

View File

@@ -0,0 +1,21 @@
# 基础概念
新版本中,整体工程进行了重构,现在我们将会介绍一些基础名词概念,以便于你更好的理解整个文档。请务必仔先阅读这一部分。
## 大仓
大仓指的是整个项目的仓库,包含了所有的代码、包、应用、规范、文档、配置等,也就是一整个 `Monorepo` 目录的所有内容。
## 应用
应用指的是一个完整的项目,一个项目可以包含多个应用,这些项目可以复用大仓内的代码、包、规范等。应用都被放置在 `apps` 目录下。每个应用都是独立的,可以单独运行、构建、测试、部署,可以引入不同的组件库等等。
::: tip
应用不限于前端应用,也可以是后端应用、移动端应用等,例如 `apps/backend-mock`就是一个后端服务。
:::
## 包
包指的是一个独立的模块,可以是一个组件、一个工具、一个库等。包可以被多个应用引用,也可以被其他包引用。包都被放置在 `packages` 目录下。

View 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。
![Vue DevTools](/guide/devtools.png)
## 本地运行文档
如需本地运行文档,并进行调整,可以执行以下命令:
```bash
pnpm dev:docs
```
## 问题解决
如果你在使用过程中遇到依赖相关的问题,可以尝试以下重新安装依赖:
```bash
# 请在项目根目录下执行
# 该命令会删除整个仓库所有的 node_modules、yarn.lock、package.lock.json后
# 再进行依赖重新安装(安装速度会明显变慢)。
pnpm reinstall
```

View 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 注意
- 如果组件有依赖样式,则需要再引入样式文件
:::

View 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>
```

View 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>
```

View 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
```

View 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
# 是否打开 devtoolstrue 为打开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`是为了避免这种情况,一个纯粹的包,应避免使用特定构建工具的变量。
:::
## 偏好设置
项目提供了非常丰富的偏好设置,用于动态配置项目的各种功能:
![](/guide/preferences.png)
如果你找不到文档说明,可以尝试自己配置好以后,点击`复制偏好设置`,覆盖项目默认即可。配置文件位于应用目录下的`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`方法内覆盖即可,不要修改默认配置文件。
:::

View 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)。