perf: improve modal and drawer component documentation and fix known problems (#4264)
* feat: improve modal and drawer component documentation and fix known problems * chore: update ci
This commit is contained in:
parent
84816ef769
commit
36e7ca19a1
16
.github/labeler.yml
vendored
Normal file
16
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Add 'feature' label to any PR where the head branch name starts with `feature` or has a `feature` section in the name
|
||||||
|
feature:
|
||||||
|
- head-branch: ["^feat", "feat"]
|
||||||
|
|
||||||
|
bug:
|
||||||
|
- head-branch: ["^fix", "fix"]
|
||||||
|
|
||||||
|
chore:
|
||||||
|
- head-branch: ["^chore", "chore"]
|
||||||
|
|
||||||
|
perf:
|
||||||
|
- head-branch: ["^perf", "perf"]
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: ["**/*.md", "docs/**"]
|
2
.github/release-drafter.yml
vendored
2
.github/release-drafter.yml
vendored
@ -13,13 +13,13 @@ categories:
|
|||||||
- title: "🚀 Features"
|
- title: "🚀 Features"
|
||||||
labels:
|
labels:
|
||||||
- "feature"
|
- "feature"
|
||||||
- "enhancement"
|
|
||||||
- title: "🐞 Bug Fixes"
|
- title: "🐞 Bug Fixes"
|
||||||
labels:
|
labels:
|
||||||
- "bug"
|
- "bug"
|
||||||
- title: "📈 Performance"
|
- title: "📈 Performance"
|
||||||
labels:
|
labels:
|
||||||
- "perf"
|
- "perf"
|
||||||
|
- "enhancement"
|
||||||
- title: 📝 Documentation
|
- title: 📝 Documentation
|
||||||
labels:
|
labels:
|
||||||
- "documentation"
|
- "documentation"
|
||||||
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
# - macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
@ -62,7 +62,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
# - macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -85,7 +85,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-latest
|
- ubuntu-latest
|
||||||
- macos-latest
|
# - macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
26
.github/workflows/deploy.yml
vendored
26
.github/workflows/deploy.yml
vendored
@ -6,7 +6,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy-push-playground-ftp:
|
deploy-playground-ftp:
|
||||||
name: Deploy Push Playground Ftp
|
name: Deploy Push Playground Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -27,7 +27,7 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-node
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build:play && pnpm build:docs
|
run: pnpm build:play
|
||||||
|
|
||||||
- name: Sync Playground files
|
- name: Sync Playground files
|
||||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
@ -37,6 +37,22 @@ jobs:
|
|||||||
password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }}
|
password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }}
|
||||||
local-dir: ./playground/dist/
|
local-dir: ./playground/dist/
|
||||||
|
|
||||||
|
deploy-docs-ftp:
|
||||||
|
name: Deploy Push Docs Ftp
|
||||||
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: pnpm build:docs
|
||||||
|
|
||||||
- name: Sync Docs files
|
- name: Sync Docs files
|
||||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
with:
|
with:
|
||||||
@ -45,7 +61,7 @@ jobs:
|
|||||||
password: ${{ secrets.WEBSITE_FTP_PASSWORD }}
|
password: ${{ secrets.WEBSITE_FTP_PASSWORD }}
|
||||||
local-dir: ./docs/.vitepress/dist/
|
local-dir: ./docs/.vitepress/dist/
|
||||||
|
|
||||||
deploy-push-antd-ftp:
|
deploy-antd-ftp:
|
||||||
name: Deploy Push Antd Ftp
|
name: Deploy Push Antd Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -76,7 +92,7 @@ jobs:
|
|||||||
password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }}
|
password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }}
|
||||||
local-dir: ./apps/web-antd/dist/
|
local-dir: ./apps/web-antd/dist/
|
||||||
|
|
||||||
deploy-push-ele-ftp:
|
deploy-ele-ftp:
|
||||||
name: Deploy Push Element Ftp
|
name: Deploy Push Element Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -107,7 +123,7 @@ jobs:
|
|||||||
password: ${{ secrets.WEB_ELE_FTP_PASSWORD }}
|
password: ${{ secrets.WEB_ELE_FTP_PASSWORD }}
|
||||||
local-dir: ./apps/web-ele/dist/
|
local-dir: ./apps/web-ele/dist/
|
||||||
|
|
||||||
deploy-push-naive-ftp:
|
deploy-naive-ftp:
|
||||||
name: Deploy Push Naive Ftp
|
name: Deploy Push Naive Ftp
|
||||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
21
.github/workflows/labeler.yml
vendored
Normal file
21
.github/workflows/labeler.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: PR Labeler
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, edited, synchronize]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
label:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Label PR based on title or file changes
|
||||||
|
uses: actions/labeler@v5
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
configuration-path: .github/labeler.yml
|
@ -154,7 +154,11 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
|
|||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
link: 'common-ui/vben-modal',
|
link: 'common-ui/vben-modal',
|
||||||
text: 'Modal 弹窗',
|
text: 'Vben Modal 模态框',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: 'common-ui/vben-drawer',
|
||||||
|
text: 'Vben Drawer 抽屉',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
108
docs/src/components/common-ui/vben-drawer.md
Normal file
108
docs/src/components/common-ui/vben-drawer.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
outline: deep
|
||||||
|
---
|
||||||
|
|
||||||
|
# Vben Drawer 抽屉
|
||||||
|
|
||||||
|
框架提供的抽屉组件,支持`自动高度`、`loading`等功能。
|
||||||
|
|
||||||
|
## 基础用法
|
||||||
|
|
||||||
|
使用 `useVbenDrawer` 创建最基础的模态框。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-drawer/basic" />
|
||||||
|
|
||||||
|
## 组件抽离
|
||||||
|
|
||||||
|
Drawer 内的内容一般业务中,会比较复杂,所以我们可以将 drawer 内的内容抽离出来,也方便复用。通过 `connectedComponent` 参数,可以将内外组件进行连接,而不用其他任何操作。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-drawer/extra" />
|
||||||
|
|
||||||
|
## 自动计算高度
|
||||||
|
|
||||||
|
弹窗会自动计算内容高度,超过一定高度会出现滚动条,同时结合 `loading` 效果以及使用 `prepend-footer` 插槽。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-drawer/auto-height" />
|
||||||
|
|
||||||
|
## 使用 Api
|
||||||
|
|
||||||
|
通过 `drawerApi` 可以调用 drawer 的方法以及使用 `setState` 更新 drawer 的状态。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-drawer/dynamic" />
|
||||||
|
|
||||||
|
## 数据共享
|
||||||
|
|
||||||
|
如果你使用了 `connectedComponent` 参数,那么内外组件会共享数据,比如一些表单回填等操作。可以用 `drawerApi` 来获取数据和设置数据,配合 `onOpenChange`,可以满足大部分的需求。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-drawer/shared-data" />
|
||||||
|
|
||||||
|
::: info 注意
|
||||||
|
|
||||||
|
- `VbenDrawer` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenDrawer参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
||||||
|
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenDrawer`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Drawer 为弹窗组件
|
||||||
|
// drawerApi 为弹窗的方法
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
// 属性
|
||||||
|
// 事件
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Props
|
||||||
|
|
||||||
|
所有属性都可以传入 `useVbenDrawer` 的第一个参数中。
|
||||||
|
|
||||||
|
| 属性名 | 描述 | 类型 | 默认值 |
|
||||||
|
| ------------------ | ------------------- | --------------- | ------- |
|
||||||
|
| title | 标题 | `string\|slot` | - |
|
||||||
|
| titleTooltip | 标题提示信息 | `string\|slot` | - |
|
||||||
|
| description | 描述信息 | `string\|slot` | - |
|
||||||
|
| isOpen | 弹窗打开状态 | `boolean` | `false` |
|
||||||
|
| loading | 弹窗加载状态 | `boolean` | `false` |
|
||||||
|
| closable | 显示关闭按钮 | `boolean` | `true` |
|
||||||
|
| modal | 显示遮罩 | `boolean` | `true` |
|
||||||
|
| header | 显示header | `boolean` | `true` |
|
||||||
|
| footer | 显示footer | `boolean\|slot` | `true` |
|
||||||
|
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
|
||||||
|
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
|
||||||
|
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
|
||||||
|
| confirmText | 确认按钮文本 | `boolean\|slot` | `确认` |
|
||||||
|
| cancelText | 取消按钮文本 | `boolean\|slot` | `取消` |
|
||||||
|
|
||||||
|
### Event
|
||||||
|
|
||||||
|
以下事件,只有在 `useVbenDrawer({onCancel:()=>{}})` 中传入才会生效。
|
||||||
|
|
||||||
|
| 事件名 | 描述 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |
|
||||||
|
| onCancel | 点击取消按钮触发 | `()=>void` |
|
||||||
|
| onConfirm | 点击确认按钮触发 | `()=>void` |
|
||||||
|
| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |
|
||||||
|
|
||||||
|
### Slots
|
||||||
|
|
||||||
|
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
||||||
|
|
||||||
|
| 插槽名 | 描述 |
|
||||||
|
| -------------- | ------------------- |
|
||||||
|
| default | 默认插槽 - 弹窗内容 |
|
||||||
|
| prepend-footer | 取消按钮左侧 |
|
||||||
|
| append-footer | 取消按钮右侧 |
|
||||||
|
|
||||||
|
### modalApi
|
||||||
|
|
||||||
|
| 事件名 | 描述 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| setState | 动态设置弹窗状态属性 | `setState(props) \| setState((prev)=>(props))` |
|
||||||
|
| open | 打开弹窗 | `()=>void` |
|
||||||
|
| close | 关闭弹窗 | `()=>void` |
|
||||||
|
| setData | 设置共享数据 | `<T>(data:T)=>void` |
|
||||||
|
| getData | 获取共享数据 | `<T>()=>T` |
|
||||||
|
| useStore | 获取可响应式状态 | - |
|
@ -2,44 +2,117 @@
|
|||||||
outline: deep
|
outline: deep
|
||||||
---
|
---
|
||||||
|
|
||||||
# vben-modal
|
# Vben Modal 模态框
|
||||||
|
|
||||||
::: tip
|
框架提供的模态框组件,支持`拖拽`、`全屏`、`自动高度`、`loading`等功能。
|
||||||
|
|
||||||
文档还在完善中,敬请期待。
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
框架提供的模态框组件,支持`拖拽`、`全屏`、`自定义`等功能。
|
|
||||||
|
|
||||||
## 基础用法
|
## 基础用法
|
||||||
|
|
||||||
使用 `useVbenModal` 创建最基于的模态框。
|
使用 `useVbenModal` 创建最基础的模态框。
|
||||||
|
|
||||||
<DemoPreview dir="demos/vben-modal/basic" />
|
<DemoPreview dir="demos/vben-modal/basic" />
|
||||||
|
|
||||||
## 组件抽离
|
## 组件抽离
|
||||||
|
|
||||||
modal 内的内容一般业务中,会比较复杂,所以我们可以将 modal 内的内容抽离出来。
|
Modal 内的内容一般业务中,会比较复杂,所以我们可以将 modal 内的内容抽离出来,也方便复用。通过 `connectedComponent` 参数,可以将内外组件进行连接,而不用其他任何操作。
|
||||||
|
|
||||||
<DemoPreview dir="demos/vben-modal/extra" />
|
<DemoPreview dir="demos/vben-modal/extra" />
|
||||||
|
|
||||||
|
## 开启拖拽
|
||||||
|
|
||||||
|
通过 `draggable` 参数,可开启拖拽功能。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-modal/draggable" />
|
||||||
|
|
||||||
|
## 自动计算高度
|
||||||
|
|
||||||
|
弹窗会自动计算内容高度,超过一定高度会出现滚动条,同时结合 `loading` 效果以及使用 `prepend-footer` 插槽。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-modal/auto-height" />
|
||||||
|
|
||||||
|
## 使用 Api
|
||||||
|
|
||||||
|
通过 `modalApi` 可以调用 modal 的方法以及使用 `setState` 更新 modal 的状态。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-modal/dynamic" />
|
||||||
|
|
||||||
|
## 数据共享
|
||||||
|
|
||||||
|
如果你使用了 `connectedComponent` 参数,那么内外组件会共享数据,比如一些表单回填等操作。可以用 `modalApi` 来获取数据和设置数据,配合 `onOpenChange`,可以满足大部分的需求。
|
||||||
|
|
||||||
|
<DemoPreview dir="demos/vben-modal/shared-data" />
|
||||||
|
|
||||||
|
::: info 注意
|
||||||
|
|
||||||
|
- `VbenModal` 组件对与参数的处理优先级是 `slot` > `props` > `state`(通过api更新的状态以及useVbenModal参数)。如果你已经传入了 `slot` 或者 `props`,那么 `setState` 将不会生效,这种情况下你可以通过 `slot` 或者 `props` 来更新状态。
|
||||||
|
- 如果你使用到了 `connectedComponent` 参数,那么会存在 2 个`useVbenModal`, 此时,如果同时设置了相同的参数,那么以内部为准(也就是没有设置 connectedComponent 的代码)。比如 同时设置了 `onComfirm`,那么以内部的 `onComfirm` 为准。`onOpenChange`事件除外,内外都会触发。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
### 属性
|
```ts
|
||||||
|
// Modal 为弹窗组件
|
||||||
|
// modalApi 为弹窗的方法
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
// 属性
|
||||||
|
// 事件
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
| 属性名 | 描述 | 类型 | 默认值 |
|
### Props
|
||||||
| ------ | ----- | -------- | ------ |
|
|
||||||
| title | 标题. | `string` | — |
|
|
||||||
|
|
||||||
### 事件
|
所有属性都可以传入 `useVbenModal` 的第一个参数中。
|
||||||
|
|
||||||
|
| 属性名 | 描述 | 类型 | 默认值 |
|
||||||
|
| ------------------ | ------------------- | --------------- | ------- |
|
||||||
|
| title | 标题 | `string\|slot` | - |
|
||||||
|
| titleTooltip | 标题提示信息 | `string\|slot` | - |
|
||||||
|
| description | 描述信息 | `string\|slot` | - |
|
||||||
|
| isOpen | 弹窗打开状态 | `boolean` | `false` |
|
||||||
|
| loading | 弹窗加载状态 | `boolean` | `false` |
|
||||||
|
| fullscreen | 全屏显示 | `boolean` | `false` |
|
||||||
|
| fullscreenButton | 显示全屏按钮 | `boolean` | `true` |
|
||||||
|
| draggable | 可拖拽 | `boolean` | `false` |
|
||||||
|
| closable | 显示关闭按钮 | `boolean` | `true` |
|
||||||
|
| centered | 居中显示 | `boolean` | `false` |
|
||||||
|
| modal | 显示遮罩 | `boolean` | `true` |
|
||||||
|
| header | 显示header | `boolean` | `true` |
|
||||||
|
| footer | 显示footer | `boolean\|slot` | `true` |
|
||||||
|
| confirmLoading | 确认按钮loading状态 | `boolean` | `false` |
|
||||||
|
| closeOnClickModal | 点击遮罩关闭弹窗 | `boolean` | `true` |
|
||||||
|
| closeOnPressEscape | esc 关闭弹窗 | `boolean` | `true` |
|
||||||
|
| confirmText | 确认按钮文本 | `boolean\|slot` | `确认` |
|
||||||
|
| cancelText | 取消按钮文本 | `boolean\|slot` | `取消` |
|
||||||
|
|
||||||
|
### Event
|
||||||
|
|
||||||
|
以下事件,只有在 `useVbenModal({onCancel:()=>{}})` 中传入才会生效。
|
||||||
|
|
||||||
| 事件名 | 描述 | 类型 |
|
| 事件名 | 描述 | 类型 |
|
||||||
| ------ | ---- | ---- |
|
| --- | --- | --- |
|
||||||
| TODO | TODO | TODO |
|
| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |
|
||||||
|
| onCancel | 点击取消按钮触发 | `()=>void` |
|
||||||
|
| onConfirm | 点击确认按钮触发 | `()=>void` |
|
||||||
|
| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |
|
||||||
|
|
||||||
### 插槽
|
### Slots
|
||||||
|
|
||||||
| 插槽名 | 描述 |
|
除了上面的属性类型包含`slot`,还可以通过插槽来自定义弹窗的内容。
|
||||||
| ------- | ---- |
|
|
||||||
| default | xx. |
|
| 插槽名 | 描述 |
|
||||||
|
| -------------- | ------------------- |
|
||||||
|
| default | 默认插槽 - 弹窗内容 |
|
||||||
|
| prepend-footer | 取消按钮左侧 |
|
||||||
|
| append-footer | 取消按钮右侧 |
|
||||||
|
|
||||||
|
### modalApi
|
||||||
|
|
||||||
|
| 事件名 | 描述 | 类型 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| setState | 动态设置弹窗状态属性 | `setState(props) \| setState((prev)=>(props))` |
|
||||||
|
| open | 打开弹窗 | `()=>void` |
|
||||||
|
| close | 关闭弹窗 | `()=>void` |
|
||||||
|
| setData | 设置共享数据 | `<T>(data:T)=>void` |
|
||||||
|
| getData | 获取共享数据 | `<T>()=>T` |
|
||||||
|
| useStore | 获取可响应式状态 | - |
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# 介绍
|
# 介绍
|
||||||
|
|
||||||
::: tip README
|
::: info README
|
||||||
|
|
||||||
该文档介绍的是框架组件的使用方法、属性、事件等。如果你觉得组件封装的不好,或者不符合你的需求,你可以直接使用原生的组件,或者自己封装一个组件,不需要拘泥于框架提供的组件。我们只是提供了一些常用的组件,方便你快速开发。是否使用,取决于你的需求。
|
该文档介绍的是框架组件的使用方法、属性、事件等。如果你觉得组件封装的不好,或者不符合你的需求,你可以直接使用原生的组件,或者自己封装一个组件,不需要拘泥于框架提供的组件。我们只是提供了一些常用的组件,方便你快速开发。是否使用,取决于你的需求。
|
||||||
|
|
||||||
|
45
docs/src/demos/vben-drawer/auto-height/drawer.vue
Normal file
45
docs/src/demos/vben-drawer/auto-height/drawer.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const list = ref<number[]>([]);
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
onCancel() {
|
||||||
|
drawerApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.log('onConfirm');
|
||||||
|
},
|
||||||
|
onOpenChange(isOpen) {
|
||||||
|
if (isOpen) {
|
||||||
|
handleUpdate(10);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleUpdate(len: number) {
|
||||||
|
drawerApi.setState({ loading: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
list.value = Array.from({ length: len }, (_v, k) => k + 1);
|
||||||
|
drawerApi.setState({ loading: false });
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Drawer title="自动计算高度">
|
||||||
|
<div
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item"
|
||||||
|
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
<template #prepend-footer>
|
||||||
|
<VbenButton type="link" @click="handleUpdate(6)">
|
||||||
|
点击更新数据
|
||||||
|
</VbenButton>
|
||||||
|
</template>
|
||||||
|
</Drawer>
|
||||||
|
</template>
|
21
docs/src/demos/vben-drawer/auto-height/index.vue
Normal file
21
docs/src/demos/vben-drawer/auto-height/index.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraDrawer from './drawer.vue';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraDrawer,
|
||||||
|
});
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
drawerApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Drawer />
|
||||||
|
<VbenButton @click="open">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
11
docs/src/demos/vben-drawer/basic/index.vue
Normal file
11
docs/src/demos/vben-drawer/basic/index.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<VbenButton @click="() => drawerApi.open()">Open</VbenButton>
|
||||||
|
<Drawer class="w-[600px]" title="基础示例"> drawer content </Drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
26
docs/src/demos/vben-drawer/dynamic/drawer.vue
Normal file
26
docs/src/demos/vben-drawer/dynamic/drawer.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
onCancel() {
|
||||||
|
drawerApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.info('onConfirm');
|
||||||
|
},
|
||||||
|
title: '动态修改配置示例',
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleUpdateTitle() {
|
||||||
|
drawerApi.setState({ title: '内部动态标题' });
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Drawer>
|
||||||
|
<div class="flex-col-center">
|
||||||
|
<VbenButton class="mb-3" type="primary" @click="handleUpdateTitle()">
|
||||||
|
内部动态修改标题
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</Drawer>
|
||||||
|
</template>
|
30
docs/src/demos/vben-drawer/dynamic/index.vue
Normal file
30
docs/src/demos/vben-drawer/dynamic/index.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraDrawer from './drawer.vue';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraDrawer,
|
||||||
|
});
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
drawerApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateTitle() {
|
||||||
|
drawerApi.setState({ title: '外部动态标题' });
|
||||||
|
drawerApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Drawer />
|
||||||
|
|
||||||
|
<VbenButton @click="open">Open</VbenButton>
|
||||||
|
<VbenButton class="ml-2" type="primary" @click="handleUpdateTitle">
|
||||||
|
从外部修改标题并打开
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
8
docs/src/demos/vben-drawer/extra/drawer.vue
Normal file
8
docs/src/demos/vben-drawer/extra/drawer.vue
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const [Drawer] = useVbenDrawer();
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Drawer title="组件抽离示例"> extra drawer content </Drawer>
|
||||||
|
</template>
|
21
docs/src/demos/vben-drawer/extra/index.vue
Normal file
21
docs/src/demos/vben-drawer/extra/index.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraDrawer from './drawer.vue';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraDrawer,
|
||||||
|
});
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
drawerApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Drawer />
|
||||||
|
<VbenButton @click="open">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
26
docs/src/demos/vben-drawer/shared-data/drawer.vue
Normal file
26
docs/src/demos/vben-drawer/shared-data/drawer.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenDrawer } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
onCancel() {
|
||||||
|
drawerApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.info('onConfirm');
|
||||||
|
},
|
||||||
|
onOpenChange(isOpen: boolean) {
|
||||||
|
if (isOpen) {
|
||||||
|
data.value = drawerApi.getData<Record<string, any>>();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Drawer title="数据共享示例">
|
||||||
|
<div class="flex-col-center">外部传递数据: {{ data }}</div>
|
||||||
|
</Drawer>
|
||||||
|
</template>
|
26
docs/src/demos/vben-drawer/shared-data/index.vue
Normal file
26
docs/src/demos/vben-drawer/shared-data/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenDrawer, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraDrawer from './drawer.vue';
|
||||||
|
|
||||||
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraDrawer,
|
||||||
|
});
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
drawerApi.setData({
|
||||||
|
content: '外部传递的数据 content',
|
||||||
|
payload: '外部传递的数据 payload',
|
||||||
|
});
|
||||||
|
drawerApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Drawer />
|
||||||
|
|
||||||
|
<VbenButton @click="open">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
21
docs/src/demos/vben-modal/auto-height/index.vue
Normal file
21
docs/src/demos/vben-modal/auto-height/index.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraModal from './modal.vue';
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraModal,
|
||||||
|
});
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Modal />
|
||||||
|
<VbenButton @click="openModal">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
45
docs/src/demos/vben-modal/auto-height/modal.vue
Normal file
45
docs/src/demos/vben-modal/auto-height/modal.vue
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const list = ref<number[]>([]);
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
onCancel() {
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.log('onConfirm');
|
||||||
|
},
|
||||||
|
onOpenChange(isOpen) {
|
||||||
|
if (isOpen) {
|
||||||
|
handleUpdate(10);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleUpdate(len: number) {
|
||||||
|
modalApi.setState({ loading: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
list.value = Array.from({ length: len }, (_v, k) => k + 1);
|
||||||
|
modalApi.setState({ loading: false });
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal title="自动计算高度">
|
||||||
|
<div
|
||||||
|
v-for="item in list"
|
||||||
|
:key="item"
|
||||||
|
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
<template #prepend-footer>
|
||||||
|
<VbenButton type="link" @click="handleUpdate(6)">
|
||||||
|
点击更新数据
|
||||||
|
</VbenButton>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
@ -5,7 +5,7 @@ const [Modal, modalApi] = useVbenModal();
|
|||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VbenButton @click="() => modalApi.open()">打开弹窗</VbenButton>
|
<VbenButton @click="() => modalApi.open()">Open</VbenButton>
|
||||||
<Modal title="基础示例"> modal content </Modal>
|
<Modal class="w-[600px]" title="基础示例"> modal content </Modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
21
docs/src/demos/vben-modal/draggable/index.vue
Normal file
21
docs/src/demos/vben-modal/draggable/index.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraModal from './modal.vue';
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraModal,
|
||||||
|
});
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Modal />
|
||||||
|
<VbenButton @click="openModal">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
10
docs/src/demos/vben-modal/draggable/modal.vue
Normal file
10
docs/src/demos/vben-modal/draggable/modal.vue
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const [Modal] = useVbenModal({
|
||||||
|
draggable: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal title="拖拽示例"> modal content </Modal>
|
||||||
|
</template>
|
30
docs/src/demos/vben-modal/dynamic/index.vue
Normal file
30
docs/src/demos/vben-modal/dynamic/index.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraModal from './modal.vue';
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraModal,
|
||||||
|
});
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateTitle() {
|
||||||
|
modalApi.setState({ title: '外部动态标题' });
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Modal />
|
||||||
|
|
||||||
|
<VbenButton @click="openModal">Open</VbenButton>
|
||||||
|
<VbenButton class="ml-2" type="primary" @click="handleUpdateTitle">
|
||||||
|
从外部修改标题并打开
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
38
docs/src/demos/vben-modal/dynamic/modal.vue
Normal file
38
docs/src/demos/vben-modal/dynamic/modal.vue
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
draggable: true,
|
||||||
|
onCancel() {
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.info('onConfirm');
|
||||||
|
},
|
||||||
|
title: '动态修改配置示例',
|
||||||
|
});
|
||||||
|
|
||||||
|
const state = modalApi.useStore();
|
||||||
|
|
||||||
|
function handleUpdateTitle() {
|
||||||
|
modalApi.setState({ title: '内部动态标题' });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleToggleFullscreen() {
|
||||||
|
modalApi.setState((prev) => {
|
||||||
|
return { ...prev, fullscreen: !prev.fullscreen };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal>
|
||||||
|
<div class="flex-col-center">
|
||||||
|
<VbenButton class="mb-3" type="primary" @click="handleUpdateTitle()">
|
||||||
|
内部动态修改标题
|
||||||
|
</VbenButton>
|
||||||
|
<VbenButton class="mb-3" @click="handleToggleFullscreen()">
|
||||||
|
{{ state.fullscreen ? '退出全屏' : '打开全屏' }}
|
||||||
|
</VbenButton>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
@ -4,7 +4,7 @@ import { useVbenModal, VbenButton } from '@vben/common-ui';
|
|||||||
import ExtraModal from './modal.vue';
|
import ExtraModal from './modal.vue';
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
// 链接抽离的组件
|
// 连接抽离的组件
|
||||||
connectedComponent: ExtraModal,
|
connectedComponent: ExtraModal,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -16,7 +16,6 @@ function openModal() {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Modal />
|
<Modal />
|
||||||
|
<VbenButton @click="openModal">Open</VbenButton>
|
||||||
<VbenButton @click="openModal">打开弹窗</VbenButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -4,5 +4,5 @@ import { useVbenModal } from '@vben/common-ui';
|
|||||||
const [Modal] = useVbenModal();
|
const [Modal] = useVbenModal();
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Modal title="基础示例"> extra modal content </Modal>
|
<Modal title="组件抽离示例"> extra modal content </Modal>
|
||||||
</template>
|
</template>
|
||||||
|
26
docs/src/demos/vben-modal/shared-data/index.vue
Normal file
26
docs/src/demos/vben-modal/shared-data/index.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useVbenModal, VbenButton } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import ExtraModal from './modal.vue';
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
// 连接抽离的组件
|
||||||
|
connectedComponent: ExtraModal,
|
||||||
|
});
|
||||||
|
|
||||||
|
function openModal() {
|
||||||
|
modalApi.setData({
|
||||||
|
content: '外部传递的数据 content',
|
||||||
|
payload: '外部传递的数据 payload',
|
||||||
|
});
|
||||||
|
modalApi.open();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<Modal />
|
||||||
|
|
||||||
|
<VbenButton @click="openModal">Open</VbenButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
26
docs/src/demos/vben-modal/shared-data/modal.vue
Normal file
26
docs/src/demos/vben-modal/shared-data/modal.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
|
||||||
|
const [Modal, modalApi] = useVbenModal({
|
||||||
|
onCancel() {
|
||||||
|
modalApi.close();
|
||||||
|
},
|
||||||
|
onConfirm() {
|
||||||
|
console.info('onConfirm');
|
||||||
|
},
|
||||||
|
onOpenChange(isOpen: boolean) {
|
||||||
|
if (isOpen) {
|
||||||
|
data.value = modalApi.getData<Record<string, any>>();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Modal title="数据共享示例">
|
||||||
|
<div class="flex-col-center">外部传递数据: {{ data }}</div>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
@ -72,6 +72,8 @@ pnpm install
|
|||||||
|
|
||||||
### 运行项目
|
### 运行项目
|
||||||
|
|
||||||
|
#### 选择项目
|
||||||
|
|
||||||
执行以下命运行项目:
|
执行以下命运行项目:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -84,12 +86,24 @@ pnpm dev
|
|||||||
```bash
|
```bash
|
||||||
│
|
│
|
||||||
◆ Select the app you need to run [dev]:
|
◆ Select the app you need to run [dev]:
|
||||||
│ ● @vben/web-antd
|
│ ○ @vben/web-antd
|
||||||
│ ○ @vben/web-ele
|
│ ○ @vben/web-ele
|
||||||
│ ○ @vben/web-naive
|
│ ○ @vben/web-naive
|
||||||
│ ○ @vben/docs
|
│ ○ @vben/docs
|
||||||
│ ○ @vben/playground
|
│ ● @vben/playground
|
||||||
└
|
└
|
||||||
```
|
```
|
||||||
|
|
||||||
现在,你可以在浏览器访问 `http://localhost:5555` 查看项目。
|
现在,你可以在浏览器访问 `http://localhost:5555` 查看项目。
|
||||||
|
|
||||||
|
#### 运行指定项目
|
||||||
|
|
||||||
|
如果你不想选择项目,可以直接运行以下命令运行你需要的应用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm run dev:antd
|
||||||
|
pnpm run dev:ele
|
||||||
|
pnpm run dev:naive
|
||||||
|
pnpm run dev:docs
|
||||||
|
pnpm run dev:play
|
||||||
|
```
|
||||||
|
@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
::: info 你正在阅读的是 [Vben Admin](https://github.com/vbenjs/vue-vben-admin) `5.0`版本的文档!
|
::: info 你正在阅读的是 [Vben Admin](https://github.com/vbenjs/vue-vben-admin) `5.0`版本的文档!
|
||||||
|
|
||||||
- Vben Admin 2.x 目前已经存档,只修复一些严重的问题。
|
- Vben Admin 2.x 目前已存档,仅进行重大问题修复。
|
||||||
- 新版本与旧版本不兼容,如果你使用的是旧版本(v2、v3),请查看 [Vue Vben Admin 2.x 文档](https://doc.vvbin.cn)
|
- 新版本与旧版本不兼容,如果你使用的是旧版本(v2、v3),请查看 [Vue Vben Admin 2.x 文档](https://doc.vvbin.cn)
|
||||||
- 如发现文档有误,欢迎提提交 Issue 帮助我们改进。
|
- 如发现文档有误,欢迎提交 [issue](https://github.com/vbenjs/vue-vben-admin/issues) 帮助我们改进。
|
||||||
- 如果你只是想体验一下,你可以查看 [快速开始](./quick-start.md)。
|
- 如果你只是想体验一下,你可以查看[快速开始](./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` 等主流技术。该项目会持续跟进最新技术,并将其应用在项目中。
|
[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` 等前端前沿技术开发。
|
- **最新技术栈**:使用 `Vue3`、`Vite`、`TypeScript` 等前端前沿技术开发。
|
||||||
- **国际化**:内置完善的国际化方案,支持多语言切换。
|
- **国际化**:内置完善的国际化方案,支持多语言切换。
|
||||||
- **权限验证**:完善的权限验证方案,按钮级别权限控制。
|
- **权限验证**:完善的权限验证方案,按钮级别权限控制。
|
||||||
- **多主题**:内置多种主题配置&黑暗模式,满足个性化需求。
|
- **多主题**:内置多种主题配置和黑暗模式,满足个性化需求。
|
||||||
- **动态菜单**:支持动态菜单,可以根据权限配置显示菜单。
|
- **动态菜单**:支持动态菜单,可以根据权限配置显示菜单。
|
||||||
- **Mock 数据**:基于 Nitro 的本地高性能 Mock 数据方案。
|
- **Mock 数据**:基于 Nitro 的本地高性能 Mock 数据方案。
|
||||||
- **组件丰富**:提供了丰富的组件,可以满足大部分的业务需求。
|
- **组件丰富**:提供了丰富的组件,可以满足大部分的业务需求。
|
||||||
- **规范**:代码规范,使用 `ESLint`、`Prettier`、`Stylelint`、`Publint`、`CSpell` 等工具保证代码质量。
|
- **规范**:代码规范,使用 `ESLint`、`Prettier`、`Stylelint`、`Publint`、`CSpell` 等工具保证代码质量。
|
||||||
- **工程化**:使用 `Pnpm Monorepo`、`TurboRepo`、`Changeset` 等工具,提高开发效率。
|
- **工程化**:使用 `Pnpm Monorepo`、`TurboRepo`、`Changeset` 等工具,提高开发效率。
|
||||||
- **多UI库支持**:支持 `Ant Design Vue`、`Element Plus`、`Vuetify` 等主流 UI 库,不再限制于特定框架。
|
- **多UI库支持**:支持 `Ant Design Vue`、`Element Plus`、`Naive` 等主流 UI 库,不再限制于特定框架。
|
||||||
|
|
||||||
## 浏览器支持
|
## 浏览器支持
|
||||||
|
|
||||||
@ -32,17 +32,15 @@
|
|||||||
|
|
||||||
| [<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 |
|
| [<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 |
|
| 不支持 | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
## 贡献
|
## 贡献
|
||||||
|
|
||||||
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) 还在持续更新中,本项目欢迎您的参与,共同维护,逐步完善,打造更好的中后台解决方案。
|
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) 还在持续更新中,本项目欢迎您的参与,共同维护,逐步完善,打造更好的中后台解决方案。
|
||||||
- 如果你想加入我们,可以多提供一些好的建议或者提交 pr,我们会根据你的活跃度邀请你加入。
|
- 如果你想加入我们,可以提供有价值的建议或者参与讨论,协助解决 issue,- 如果你想加入我们,可以提供有价值的建议或者参与讨论,协助解决 issue,我们会根据你的活跃度邀请你加入。。
|
||||||
|
|
||||||
::: info 加入我们
|
::: info 加入我们
|
||||||
|
|
||||||
如果你想加入我们,你可以从以下几个方面开始,我们会根据你的活跃度邀请你加入:
|
|
||||||
|
|
||||||
- 长期提交 `PR`。
|
- 长期提交 `PR`。
|
||||||
- 提供一些好的建议。
|
- 提供一些好的建议。
|
||||||
- 参与讨论,帮助解决一些 `issue`。
|
- 参与讨论,帮助解决一些 `issue`。
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
# 为什么选择我们?
|
# 为什么选择我们?
|
||||||
|
|
||||||
首先,我们不会去和其他框架做比较。我们认为每个框架都有自己的特点,适合不同的场景。我们的目标是提供一个简单、易用的框架,让开发者可以快速上手,专注于业务逻辑的开发。所以我们只会不断完善和优化我们的框架,提供更好的体验。
|
::: info 写在前面
|
||||||
|
|
||||||
|
我们不会去和其他框架做比较。我们认为每个框架都有自己的特点,适合不同的场景。我们的目标是提供一个简单、易用的框架,让开发者可以快速上手,专注于业务逻辑的开发。所以我们只会不断完善和优化我们的框架,提供更好的体验。
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
我们致力于为开发者提供一个高效、现代、易用的前端框架。我们的解决方案基于最新的技术栈,如 Vue3、Vite 和 TypeScript,确保您在构建项目时始终走在技术的前沿。同时,我们注重代码的质量与规范,通过严格的工具链保证代码的一致性和可维护性。无论是初创项目还是企业级应用,我们的框架都能帮助您快速构建、迭代和部署。
|
||||||
|
|
||||||
## 框架历程
|
## 框架历程
|
||||||
|
|
||||||
从 Vue Vben Admin 1.x 版本开始,框架经历了许多迭代和优化。从一开始使用 `Vite 0.x` 版本,没有现成的插件,开发了很多自定义插件来弥合 Webpack 和 Vite 之间的差异。虽然很多现在已经被代替,但是我们的初衷一直没有变,就是提供一个简单、易用的框架。
|
从 Vue Vben Admin 1.x 版本开始,框架经历了许多迭代和优化。从一开始使用 `Vite 0.x` 版本,没有现成的插件,开发了很多自定义插件来弥合 Webpack 和 Vite 之间的差异。虽然很多现在已经被代替,但是我们的初衷一直没有变,就是提供一个简单、易用的框架。
|
||||||
|
|
||||||
虽然中间有段时间由社区维护,但我们一直密切关注 Vue Vben Admin 的发展。见证了许多开发者使用 Vben Admin,并提供了许多宝贵的建议和反馈。非常感谢大家的支持和贡献,这些都是我们持续改进 Vben Admin 的动力。新版本中,我们持续收集用户反馈,重新开始,不断优化框架,以提供更好的用户体验。我们的目标是让开发者能够快速上手,专注于业务逻辑的开发。
|
虽然中间有段时间由社区维护,但我们一直密切关注 Vue Vben Admin 的发展。见证了许多开发者使用 Vben Admin,并提供了许多宝贵的建议和反馈。非常感谢大家的支持和贡献,这些都是我们持续改进 Vben Admin 的动力。新版本中,我们持续收集用户反馈,重新开始,不断优化框架,以提供更好的用户体验。我们的目标是让开发者能够快速上手,专注于业务逻辑的开发。
|
||||||
|
|
||||||
|
## 单元测试
|
||||||
|
|
||||||
|
单元测试是确保代码质量的基石。在开发过程中编写和执行单元测试,以捕捉潜在的错误并提升代码的可靠性。框架核心逻辑使用 `vitest` 做了单元测试,并在逐步增加覆盖率。通过单元测试,可以放心地进行代码重构,减少回归问题,从而提高整体开发效率。
|
||||||
|
|
||||||
|
## 质量与规范
|
||||||
|
|
||||||
|
我们始终高度重视代码的质量与规范。通过使用 ESLint、Prettier、Stylelint、Publint、CSpell 等工具来确保代码质量。我们的代码规范基于 Vue3、Vite、TypeScript 等现代前端技术制定,旨在提供一个简洁、易用的框架,使开发者能够快速上手并专注于业务逻辑的开发。
|
||||||
|
@ -15,10 +15,6 @@ export async function vue(): Promise<Linter.Config[]> {
|
|||||||
{
|
{
|
||||||
files: ['**/*.vue'],
|
files: ['**/*.vue'],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
|
||||||
// TODO: 等待插件正式支持后删除
|
|
||||||
defineModel: true,
|
|
||||||
},
|
|
||||||
parser: parserVue,
|
parser: parserVue,
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaFeatures: {
|
ecmaFeatures: {
|
||||||
|
@ -77,7 +77,7 @@
|
|||||||
/* ============= custom ============= */
|
/* ============= custom ============= */
|
||||||
|
|
||||||
/* 遮罩颜色 */
|
/* 遮罩颜色 */
|
||||||
--overlay: 0deg 0% 0% / 30%;
|
--overlay: 0 0% 0% / 30%;
|
||||||
|
|
||||||
/* 基本文字大小 */
|
/* 基本文字大小 */
|
||||||
--font-size-base: 16px;
|
--font-size-base: 16px;
|
||||||
|
@ -38,7 +38,6 @@ export class DrawerApi {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
modal: true,
|
modal: true,
|
||||||
sharedData: {},
|
|
||||||
title: '',
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -93,7 +92,11 @@ export class DrawerApi {
|
|||||||
* 取消操作
|
* 取消操作
|
||||||
*/
|
*/
|
||||||
onCancel() {
|
onCancel() {
|
||||||
this.api.onCancel?.();
|
if (this.api.onCancel) {
|
||||||
|
this.api.onCancel?.();
|
||||||
|
} else {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { DrawerProps, ExtendedDrawerApi } from './drawer';
|
import type { DrawerProps, ExtendedDrawerApi } from './drawer';
|
||||||
|
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
import { useIsMobile, usePriorityValue } from '@vben-core/composables';
|
import { useIsMobile, usePriorityValue } from '@vben-core/composables';
|
||||||
import { Info, X } from '@vben-core/icons';
|
import { Info, X } from '@vben-core/icons';
|
||||||
import {
|
import {
|
||||||
@ -31,6 +33,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
drawerApi: undefined,
|
drawerApi: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const wrapperRef = ref<HTMLElement>();
|
||||||
|
|
||||||
const { isMobile } = useIsMobile();
|
const { isMobile } = useIsMobile();
|
||||||
const state = props.drawerApi?.useStore?.();
|
const state = props.drawerApi?.useStore?.();
|
||||||
|
|
||||||
@ -47,6 +51,18 @@ const confirmText = usePriorityValue('confirmText', props, state);
|
|||||||
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
|
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
|
||||||
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
|
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => showLoading.value,
|
||||||
|
(v) => {
|
||||||
|
if (v && wrapperRef.value) {
|
||||||
|
wrapperRef.value.scrollTo({
|
||||||
|
// behavior: 'smooth',
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
function interactOutside(e: Event) {
|
function interactOutside(e: Event) {
|
||||||
if (!closeOnClickModal.value) {
|
if (!closeOnClickModal.value) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -129,9 +145,10 @@ function pointerDownOutside(e: Event) {
|
|||||||
</SheetHeader>
|
</SheetHeader>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
ref="wrapperRef"
|
||||||
:class="
|
:class="
|
||||||
cn('relative flex-1 p-3', contentClass, {
|
cn('relative flex-1 overflow-y-auto p-3', contentClass, {
|
||||||
'overflow-y-auto': !showLoading,
|
'overflow-hidden': showLoading,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
|
@ -38,10 +38,10 @@ export class ModalApi {
|
|||||||
footer: true,
|
footer: true,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
fullscreenButton: true,
|
fullscreenButton: true,
|
||||||
|
header: true,
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
modal: true,
|
modal: true,
|
||||||
sharedData: {},
|
|
||||||
title: '',
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,12 +60,16 @@ export interface ModalProps {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
fullscreenButton?: boolean;
|
fullscreenButton?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否显示顶栏
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
header?: boolean;
|
||||||
/**
|
/**
|
||||||
* 弹窗是否显示
|
* 弹窗是否显示
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示遮罩
|
* 是否显示遮罩
|
||||||
* @default true
|
* @default true
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
|
||||||
VbenButton,
|
VbenButton,
|
||||||
VbenIconButton,
|
VbenIconButton,
|
||||||
VbenLoading,
|
VbenLoading,
|
||||||
@ -21,8 +20,6 @@ import {
|
|||||||
} from '@vben-core/shadcn-ui';
|
} from '@vben-core/shadcn-ui';
|
||||||
import { cn } from '@vben-core/shared';
|
import { cn } from '@vben-core/shared';
|
||||||
|
|
||||||
// import { useElementSize } from '@vueuse/core';
|
|
||||||
|
|
||||||
import { useModalDraggable } from './use-modal-draggable';
|
import { useModalDraggable } from './use-modal-draggable';
|
||||||
|
|
||||||
interface Props extends ModalProps {
|
interface Props extends ModalProps {
|
||||||
@ -42,15 +39,15 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const contentRef = ref();
|
const contentRef = ref();
|
||||||
|
const wrapperRef = ref<HTMLElement>();
|
||||||
const dialogRef = ref();
|
const dialogRef = ref();
|
||||||
const headerRef = ref();
|
const headerRef = ref();
|
||||||
const footerRef = ref();
|
const footerRef = ref();
|
||||||
|
|
||||||
const { isMobile } = useIsMobile();
|
const { isMobile } = useIsMobile();
|
||||||
// const { height: headerHeight } = useElementSize(headerRef);
|
|
||||||
// const { height: footerHeight } = useElementSize(footerRef);
|
|
||||||
const state = props.modalApi?.useStore?.();
|
const state = props.modalApi?.useStore?.();
|
||||||
|
|
||||||
|
const header = usePriorityValue('header', props, state);
|
||||||
const title = usePriorityValue('title', props, state);
|
const title = usePriorityValue('title', props, state);
|
||||||
const fullscreen = usePriorityValue('fullscreen', props, state);
|
const fullscreen = usePriorityValue('fullscreen', props, state);
|
||||||
const description = usePriorityValue('description', props, state);
|
const description = usePriorityValue('description', props, state);
|
||||||
@ -68,9 +65,12 @@ const fullscreenButton = usePriorityValue('fullscreenButton', props, state);
|
|||||||
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
|
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
|
||||||
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
|
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
|
||||||
|
|
||||||
const shouldFullscreen = computed(() => fullscreen.value || isMobile.value);
|
const shouldFullscreen = computed(
|
||||||
|
() => (fullscreen.value && header.value) || isMobile.value,
|
||||||
|
);
|
||||||
|
|
||||||
const shouldDraggable = computed(
|
const shouldDraggable = computed(
|
||||||
() => draggable.value && !shouldFullscreen.value,
|
() => draggable.value && !shouldFullscreen.value && header.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { dragging, transform } = useModalDraggable(
|
const { dragging, transform } = useModalDraggable(
|
||||||
@ -79,32 +79,29 @@ const { dragging, transform } = useModalDraggable(
|
|||||||
shouldDraggable,
|
shouldDraggable,
|
||||||
);
|
);
|
||||||
|
|
||||||
// const loadingStyle = computed(() => {
|
|
||||||
// // py-5 4px*5*2
|
|
||||||
// const headerPadding = 40;
|
|
||||||
// // p-2 4px*2*2
|
|
||||||
// const footerPadding = 16;
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// bottom: `${footerHeight.value + footerPadding}px`,
|
|
||||||
// height: `calc(100% - ${footerHeight.value + headerHeight.value + headerPadding + footerPadding}px)`,
|
|
||||||
// top: `${headerHeight.value + headerPadding}px`,
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => state?.value?.isOpen,
|
() => state?.value?.isOpen,
|
||||||
async (v) => {
|
async (v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
if (contentRef.value) {
|
if (!contentRef.value) return;
|
||||||
const innerContentRef = contentRef.value.getContentRef();
|
const innerContentRef = contentRef.value.getContentRef();
|
||||||
dialogRef.value = innerContentRef.$el;
|
dialogRef.value = innerContentRef.$el;
|
||||||
|
// reopen modal reassign value
|
||||||
|
const { offsetX, offsetY } = transform;
|
||||||
|
dialogRef.value.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// reopen modal reassign value
|
watch(
|
||||||
const { offsetX, offsetY } = transform;
|
() => showLoading.value,
|
||||||
dialogRef.value.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
|
(v) => {
|
||||||
}
|
if (v && wrapperRef.value) {
|
||||||
|
wrapperRef.value.scrollTo({
|
||||||
|
// behavior: 'smooth',
|
||||||
|
top: 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -142,10 +139,6 @@ function pointerDownOutside(e: Event) {
|
|||||||
:open="state?.isOpen"
|
:open="state?.isOpen"
|
||||||
@update:open="() => modalApi?.close()"
|
@update:open="() => modalApi?.close()"
|
||||||
>
|
>
|
||||||
<DialogTrigger v-if="$slots.trigger" as-child>
|
|
||||||
<slot name="trigger"> </slot>
|
|
||||||
</DialogTrigger>
|
|
||||||
|
|
||||||
<DialogContent
|
<DialogContent
|
||||||
ref="contentRef"
|
ref="contentRef"
|
||||||
:class="
|
:class="
|
||||||
@ -170,8 +163,9 @@ function pointerDownOutside(e: Event) {
|
|||||||
ref="headerRef"
|
ref="headerRef"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'border-b px-6 py-5',
|
'border-b px-5 py-4',
|
||||||
{
|
{
|
||||||
|
hidden: !header,
|
||||||
'cursor-move select-none': shouldDraggable,
|
'cursor-move select-none': shouldDraggable,
|
||||||
},
|
},
|
||||||
props.headerClass,
|
props.headerClass,
|
||||||
@ -182,12 +176,14 @@ function pointerDownOutside(e: Event) {
|
|||||||
<slot name="title">
|
<slot name="title">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
|
|
||||||
<VbenTooltip v-if="titleTooltip" side="right">
|
<slot v-if="titleTooltip" name="titleTooltip">
|
||||||
<template #trigger>
|
<VbenTooltip side="right">
|
||||||
<Info class="inline-flex size-5 cursor-pointer pb-1" />
|
<template #trigger>
|
||||||
</template>
|
<Info class="inline-flex size-5 cursor-pointer pb-1" />
|
||||||
{{ titleTooltip }}
|
</template>
|
||||||
</VbenTooltip>
|
{{ titleTooltip }}
|
||||||
|
</VbenTooltip>
|
||||||
|
</slot>
|
||||||
</slot>
|
</slot>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription v-if="description">
|
<DialogDescription v-if="description">
|
||||||
@ -201,13 +197,18 @@ function pointerDownOutside(e: Event) {
|
|||||||
</VisuallyHidden>
|
</VisuallyHidden>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div
|
<div
|
||||||
|
ref="wrapperRef"
|
||||||
:class="
|
:class="
|
||||||
cn('relative min-h-40 flex-1 p-3', contentClass, {
|
cn('relative min-h-40 flex-1 overflow-y-auto p-3', contentClass, {
|
||||||
'overflow-y-auto': !showLoading,
|
'overflow-hidden': showLoading,
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<VbenLoading v-if="showLoading" class="size-full" spinning />
|
<VbenLoading
|
||||||
|
v-if="showLoading"
|
||||||
|
class="size-full h-auto min-h-full"
|
||||||
|
spinning
|
||||||
|
/>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -20,9 +20,6 @@ export function useModalDraggable(
|
|||||||
|
|
||||||
const dragging = ref(false);
|
const dragging = ref(false);
|
||||||
|
|
||||||
// let isFirstDrag = true;
|
|
||||||
// let initialX = 0;
|
|
||||||
// let initialY = 0;
|
|
||||||
const onMousedown = (e: MouseEvent) => {
|
const onMousedown = (e: MouseEvent) => {
|
||||||
const downX = e.clientX;
|
const downX = e.clientX;
|
||||||
const downY = e.clientY;
|
const downY = e.clientY;
|
||||||
@ -31,12 +28,6 @@ export function useModalDraggable(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (isFirstDrag) {
|
|
||||||
// const { x, y } = getInitialTransform(targetRef.value);
|
|
||||||
// initialX = x;
|
|
||||||
// initialY = y;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const targetRect = targetRef.value.getBoundingClientRect();
|
const targetRect = targetRef.value.getBoundingClientRect();
|
||||||
|
|
||||||
const { offsetX, offsetY } = transform;
|
const { offsetX, offsetY } = transform;
|
||||||
@ -56,12 +47,9 @@ export function useModalDraggable(
|
|||||||
const onMousemove = (e: MouseEvent) => {
|
const onMousemove = (e: MouseEvent) => {
|
||||||
let moveX = offsetX + e.clientX - downX;
|
let moveX = offsetX + e.clientX - downX;
|
||||||
let moveY = offsetY + e.clientY - downY;
|
let moveY = offsetY + e.clientY - downY;
|
||||||
// const x = isFirstDrag ? initialX : 0;
|
|
||||||
// const y = isFirstDrag ? initialY : 0;
|
|
||||||
moveX = Math.min(Math.max(moveX, minLeft), maxLeft);
|
moveX = Math.min(Math.max(moveX, minLeft), maxLeft);
|
||||||
// + x;
|
|
||||||
moveY = Math.min(Math.max(moveY, minTop), maxTop);
|
moveY = Math.min(Math.max(moveY, minTop), maxTop);
|
||||||
// + y;
|
|
||||||
|
|
||||||
transform.offsetX = moveX;
|
transform.offsetX = moveX;
|
||||||
transform.offsetY = moveY;
|
transform.offsetY = moveY;
|
||||||
@ -73,7 +61,6 @@ export function useModalDraggable(
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onMouseup = () => {
|
const onMouseup = () => {
|
||||||
// isFirstDrag = false;
|
|
||||||
dragging.value = false;
|
dragging.value = false;
|
||||||
document.removeEventListener('mousemove', onMousemove);
|
document.removeEventListener('mousemove', onMousemove);
|
||||||
document.removeEventListener('mouseup', onMouseup);
|
document.removeEventListener('mouseup', onMouseup);
|
||||||
@ -127,20 +114,3 @@ export function useModalDraggable(
|
|||||||
transform,
|
transform,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// function getInitialTransform(target: HTMLElement) {
|
|
||||||
// let x = 0;
|
|
||||||
// let y = 0;
|
|
||||||
// const transformValue = window.getComputedStyle(target)?.transform;
|
|
||||||
// if (transformValue) {
|
|
||||||
// const match = transformValue.match(/matrix\(([^)]+)\)/);
|
|
||||||
// if (match) {
|
|
||||||
// const values = match[1]?.split(', ') ?? [];
|
|
||||||
// // 获取 translateX 值
|
|
||||||
// x = Number.parseFloat(`${values[4]}`);
|
|
||||||
// // 获取 translateY 值
|
|
||||||
// y = Number.parseFloat(`${values[5]}`);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return { x, y };
|
|
||||||
// }
|
|
||||||
|
@ -69,7 +69,7 @@ function onTransitionEnd() {
|
|||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'bg-overlay z-100 pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center backdrop-blur-sm transition-all duration-500',
|
'bg-overlay z-100 pointer-events-none absolute left-0 top-0 flex size-full flex-col items-center justify-center transition-all duration-500',
|
||||||
{
|
{
|
||||||
'invisible opacity-0': !showSpinner,
|
'invisible opacity-0': !showSpinner,
|
||||||
},
|
},
|
||||||
|
@ -44,7 +44,7 @@ defineExpose({
|
|||||||
<template>
|
<template>
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay
|
<DialogOverlay
|
||||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-overlay fixed inset-0 z-[1000] backdrop-blur-sm"
|
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-overlay fixed inset-0 z-[1000]"
|
||||||
data-dismissable-modal="true"
|
data-dismissable-modal="true"
|
||||||
@click="() => emits('close')"
|
@click="() => emits('close')"
|
||||||
/>
|
/>
|
||||||
|
@ -30,6 +30,8 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
class="mb-2 flex justify-between text-lg font-semibold"
|
class="mb-2 flex justify-between text-lg font-semibold"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
|
|
||||||
|
<slot name="extra"></slot>
|
||||||
</div>
|
</div>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
||||||
|
@ -47,8 +47,8 @@ watch(
|
|||||||
:close-on-press-escape="false"
|
:close-on-press-escape="false"
|
||||||
:footer="false"
|
:footer="false"
|
||||||
:fullscreen-button="false"
|
:fullscreen-button="false"
|
||||||
|
:header="false"
|
||||||
class="border-none px-10 py-6 text-center shadow-xl sm:w-[600px] sm:rounded-2xl md:h-[unset]"
|
class="border-none px-10 py-6 text-center shadow-xl sm:w-[600px] sm:rounded-2xl md:h-[unset]"
|
||||||
header-class="hidden"
|
|
||||||
>
|
>
|
||||||
<VbenAvatar :src="avatar" class="mx-auto mb-6 size-20" />
|
<VbenAvatar :src="avatar" class="mx-auto mb-6 size-20" />
|
||||||
<AuthenticationLogin
|
<AuthenticationLogin
|
||||||
|
2
packages/stores/shim-pinia.d.ts
vendored
2
packages/stores/shim-pinia.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
// TODO: https://github.com/vuejs/pinia/issues/2098
|
// https://github.com/vuejs/pinia/issues/2098
|
||||||
declare module 'pinia' {
|
declare module 'pinia' {
|
||||||
export function acceptHMRUpdate(
|
export function acceptHMRUpdate(
|
||||||
initialUseStore: any | StoreDefinition,
|
initialUseStore: any | StoreDefinition,
|
||||||
|
@ -66,7 +66,6 @@ export const useTabbarStore = defineStore('core-tabbar', {
|
|||||||
*/
|
*/
|
||||||
async _goToDefaultTab(router: Router) {
|
async _goToDefaultTab(router: Router) {
|
||||||
if (this.getTabs.length <= 0) {
|
if (this.getTabs.length <= 0) {
|
||||||
// TODO: 跳转首页
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const firstTab = this.getTabs[0];
|
const firstTab = this.getTabs[0];
|
||||||
|
@ -76,7 +76,7 @@ function normalizeViewPath(path: string): string {
|
|||||||
? normalizedPath
|
? normalizedPath
|
||||||
: `/${normalizedPath}`;
|
: `/${normalizedPath}`;
|
||||||
|
|
||||||
// TODO: 这里耦合了vben-admin的目录结构
|
// 这里耦合了vben-admin的目录结构
|
||||||
return viewPath.replace(/^\/views/, '');
|
return viewPath.replace(/^\/views/, '');
|
||||||
}
|
}
|
||||||
export { generateRoutesByBackend };
|
export { generateRoutesByBackend };
|
||||||
|
16
playground/src/views/examples/doc-button.vue
Normal file
16
playground/src/views/examples/doc-button.vue
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { VBEN_DOC_URL } from '@vben/constants';
|
||||||
|
import { openWindow } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Button } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const props = defineProps<{ path: string }>();
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
openWindow(VBEN_DOC_URL + props.path);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button type="link" @click="handleClick">查看组件文档</Button>
|
||||||
|
</template>
|
@ -5,6 +5,8 @@ import { useVbenDrawer } from '@vben/common-ui';
|
|||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const list = ref<number[]>([]);
|
||||||
|
|
||||||
const [Drawer, drawerApi] = useVbenDrawer({
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
onCancel() {
|
onCancel() {
|
||||||
drawerApi.close();
|
drawerApi.close();
|
||||||
@ -13,14 +15,19 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
message.info('onConfirm');
|
message.info('onConfirm');
|
||||||
// drawerApi.close();
|
// drawerApi.close();
|
||||||
},
|
},
|
||||||
|
onOpenChange(isOpen) {
|
||||||
|
if (isOpen) {
|
||||||
|
handleUpdate(10);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const list = ref<number[]>([]);
|
function handleUpdate(len: number) {
|
||||||
|
drawerApi.setState({ loading: true });
|
||||||
list.value = Array.from({ length: 10 }, (_v, k) => k + 1);
|
setTimeout(() => {
|
||||||
|
list.value = Array.from({ length: len }, (_v, k) => k + 1);
|
||||||
function handleUpdate() {
|
drawerApi.setState({ loading: false });
|
||||||
list.value = Array.from({ length: 6 }, (_v, k) => k + 1);
|
}, 2000);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
@ -34,7 +41,7 @@ function handleUpdate() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #prepend-footer>
|
<template #prepend-footer>
|
||||||
<Button type="link" @click="handleUpdate">点击更新数据</Button>
|
<Button type="link" @click="handleUpdate(6)">点击更新数据</Button>
|
||||||
</template>
|
</template>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -11,14 +11,6 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
message.info('onConfirm');
|
message.info('onConfirm');
|
||||||
// drawerApi.close();
|
// drawerApi.close();
|
||||||
},
|
},
|
||||||
onOpenChange(isOpen) {
|
|
||||||
if (isOpen) {
|
|
||||||
drawerApi.setState({ loading: true });
|
|
||||||
setTimeout(() => {
|
|
||||||
drawerApi.setState({ loading: false });
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -3,18 +3,18 @@ import { Page, useVbenDrawer } from '@vben/common-ui';
|
|||||||
|
|
||||||
import { Button, Card } from 'ant-design-vue';
|
import { Button, Card } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import DocButton from '../doc-button.vue';
|
||||||
import AutoHeightDemo from './auto-height-demo.vue';
|
import AutoHeightDemo from './auto-height-demo.vue';
|
||||||
import BaseDemo from './base-demo.vue';
|
import BaseDemo from './base-demo.vue';
|
||||||
import DynamicDemo from './dynamic-demo.vue';
|
import DynamicDemo from './dynamic-demo.vue';
|
||||||
import SharedDataDemo from './shared-data-demo.vue';
|
import SharedDataDemo from './shared-data-demo.vue';
|
||||||
|
|
||||||
const [BaseDrawer, baseDrawerApi] = useVbenDrawer({
|
const [BaseDrawer, baseDrawerApi] = useVbenDrawer({
|
||||||
// 链接抽离的组件
|
// 连接抽离的组件
|
||||||
connectedComponent: BaseDemo,
|
connectedComponent: BaseDemo,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({
|
const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({
|
||||||
// 链接抽离的组件
|
|
||||||
connectedComponent: AutoHeightDemo,
|
connectedComponent: AutoHeightDemo,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,6 +57,9 @@ function openSharedDrawer() {
|
|||||||
description="抽屉组件通常用于在当前页面上显示一个覆盖层,用以展示重要信息或提供用户交互界面。"
|
description="抽屉组件通常用于在当前页面上显示一个覆盖层,用以展示重要信息或提供用户交互界面。"
|
||||||
title="抽屉组件示例"
|
title="抽屉组件示例"
|
||||||
>
|
>
|
||||||
|
<template #extra>
|
||||||
|
<DocButton path="/components/common-ui/vben-drawer" />
|
||||||
|
</template>
|
||||||
<BaseDrawer />
|
<BaseDrawer />
|
||||||
<AutoHeightDrawer />
|
<AutoHeightDrawer />
|
||||||
<DynamicDrawer />
|
<DynamicDrawer />
|
||||||
|
@ -5,24 +5,31 @@ import { useVbenModal } from '@vben/common-ui';
|
|||||||
|
|
||||||
import { Button, message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
const list = ref<number[]>([]);
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
onCancel() {
|
onCancel() {
|
||||||
modalApi.close();
|
modalApi.close();
|
||||||
},
|
},
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
message.info('onConfirm');
|
message.info('onConfirm');
|
||||||
// modalApi.close();
|
},
|
||||||
|
onOpenChange(isOpen) {
|
||||||
|
if (isOpen) {
|
||||||
|
handleUpdate(10);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const list = ref<number[]>([]);
|
function handleUpdate(len: number) {
|
||||||
|
modalApi.setState({ loading: true });
|
||||||
list.value = Array.from({ length: 10 }, (_v, k) => k + 1);
|
setTimeout(() => {
|
||||||
|
list.value = Array.from({ length: len }, (_v, k) => k + 1);
|
||||||
function handleUpdate() {
|
modalApi.setState({ loading: false });
|
||||||
list.value = Array.from({ length: 6 }, (_v, k) => k + 1);
|
}, 2000);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal title="自动计算高度">
|
<Modal title="自动计算高度">
|
||||||
<div
|
<div
|
||||||
@ -32,9 +39,8 @@ function handleUpdate() {
|
|||||||
>
|
>
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #prepend-footer>
|
<template #prepend-footer>
|
||||||
<Button type="link" @click="handleUpdate">点击更新数据</Button>
|
<Button type="link" @click="handleUpdate(6)">点击更新数据</Button>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
@ -11,14 +11,6 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
message.info('onConfirm');
|
message.info('onConfirm');
|
||||||
// modalApi.close();
|
// modalApi.close();
|
||||||
},
|
},
|
||||||
onOpenChange(isOpen) {
|
|
||||||
if (isOpen) {
|
|
||||||
modalApi.setState({ loading: true });
|
|
||||||
setTimeout(() => {
|
|
||||||
modalApi.setState({ loading: false });
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -3,6 +3,7 @@ import { Page, useVbenModal } from '@vben/common-ui';
|
|||||||
|
|
||||||
import { Button, Card } from 'ant-design-vue';
|
import { Button, Card } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import DocButton from '../doc-button.vue';
|
||||||
import AutoHeightDemo from './auto-height-demo.vue';
|
import AutoHeightDemo from './auto-height-demo.vue';
|
||||||
import BaseDemo from './base-demo.vue';
|
import BaseDemo from './base-demo.vue';
|
||||||
import DragDemo from './drag-demo.vue';
|
import DragDemo from './drag-demo.vue';
|
||||||
@ -10,7 +11,7 @@ import DynamicDemo from './dynamic-demo.vue';
|
|||||||
import SharedDataDemo from './shared-data-demo.vue';
|
import SharedDataDemo from './shared-data-demo.vue';
|
||||||
|
|
||||||
const [BaseModal, baseModalApi] = useVbenModal({
|
const [BaseModal, baseModalApi] = useVbenModal({
|
||||||
// 链接抽离的组件
|
// 连接抽离的组件
|
||||||
connectedComponent: BaseDemo,
|
connectedComponent: BaseDemo,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,9 +63,12 @@ function handleUpdateTitle() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Page
|
<Page
|
||||||
description="弹窗组件常用于在不离开当前页面的情况下,显示额外的信息、表单或操作提示。"
|
description="弹窗组件常用于在不离开当前页面的情况下,显示额外的信息、表单或操作提示,更多api请查看组件文档。"
|
||||||
title="弹窗组件示例"
|
title="弹窗组件示例"
|
||||||
>
|
>
|
||||||
|
<template #extra>
|
||||||
|
<DocButton path="/components/common-ui/vben-modal" />
|
||||||
|
</template>
|
||||||
<BaseModal />
|
<BaseModal />
|
||||||
<AutoHeightModal />
|
<AutoHeightModal />
|
||||||
<DragModal />
|
<DragModal />
|
||||||
|
Loading…
Reference in New Issue
Block a user