mirror of
https://github.com/imdap/ruoyi-plus-vben5.git
synced 2026-04-23 00:38:34 +08:00
chore: 删除废弃文档和示例代码
This commit is contained in:
@@ -1,138 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { Button, Card, message, Space, Tag } from 'ant-design-vue';
|
||||
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
|
||||
const transformedValues = ref<Record<string, any>>({});
|
||||
const liveValues = ref<Record<string, any>>({});
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
commonConfig: {
|
||||
componentProps: {
|
||||
class: 'w-full',
|
||||
},
|
||||
},
|
||||
handleSubmit,
|
||||
schema: [
|
||||
{
|
||||
component: 'RangePicker',
|
||||
fieldName: 'reportRange',
|
||||
help: '通过 setValue 拆分为 startTime / endTime,并移除原字段',
|
||||
label: '统计时间范围',
|
||||
valueFormat(value, setValue) {
|
||||
setValue('startTime', value?.[0]?.valueOf());
|
||||
setValue('endTime', value?.[1]?.valueOf());
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'DatePicker',
|
||||
fieldName: 'deadline',
|
||||
help: '直接 return 时间戳,保留原字段名',
|
||||
label: '截止时间',
|
||||
valueFormat(value) {
|
||||
return value?.valueOf();
|
||||
},
|
||||
},
|
||||
{
|
||||
component: 'Input',
|
||||
componentProps: {
|
||||
placeholder: '请输入关键字',
|
||||
},
|
||||
fieldName: 'keyword',
|
||||
label: '关键字',
|
||||
},
|
||||
],
|
||||
wrapperClass: 'grid-cols-1 md:grid-cols-2',
|
||||
});
|
||||
|
||||
const liveValuesPreview = computed(() => formatJsonPreview(liveValues.value));
|
||||
|
||||
const transformedValuesPreview = computed(() => {
|
||||
return formatJsonPreview(transformedValues.value);
|
||||
});
|
||||
|
||||
function formatJsonPreview(value: Record<string, any>) {
|
||||
return JSON.stringify(
|
||||
value,
|
||||
(_key, currentValue) => {
|
||||
return isFormattableDateValue(currentValue)
|
||||
? currentValue.format('YYYY-MM-DD HH:mm:ss')
|
||||
: currentValue;
|
||||
},
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
function isFormattableDateValue(
|
||||
value: unknown,
|
||||
): value is { format: (template: string) => string } {
|
||||
return !!value && typeof value === 'object' && 'format' in value;
|
||||
}
|
||||
|
||||
async function handleInspectValues() {
|
||||
await syncPreviewValues();
|
||||
message.success('已刷新 getValues 输出');
|
||||
}
|
||||
|
||||
function handleSubmit(values: Record<string, any>) {
|
||||
transformedValues.value = values;
|
||||
message.success({
|
||||
content: `getValues output: ${JSON.stringify(values)}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function syncPreviewValues(values?: Record<string, any>) {
|
||||
liveValues.value = values ?? formApi.form?.values ?? {};
|
||||
transformedValues.value = await formApi.getValues();
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
watch(
|
||||
() => formApi.form?.values,
|
||||
async (values) => {
|
||||
await syncPreviewValues(values);
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<Tag color="processing">return 值:回写当前字段</Tag>
|
||||
<Tag color="success">setValue:拆分写入其他字段</Tag>
|
||||
<Tag color="warning">return undefined:保持原字段删除</Tag>
|
||||
</div>
|
||||
|
||||
<Card title="valueFormat 示例">
|
||||
<template #extra>
|
||||
<Space wrap>
|
||||
<Button type="primary" @click="handleInspectValues">
|
||||
查看 getValues 输出
|
||||
</Button>
|
||||
</Space>
|
||||
</template>
|
||||
<Form />
|
||||
</Card>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<Card title="原始 form.values(组件值)">
|
||||
<pre class="bg-muted overflow-auto rounded-md p-4 text-sm">{{
|
||||
liveValuesPreview
|
||||
}}</pre>
|
||||
</Card>
|
||||
<Card title="getValues / submit 输出(valueFormat 后)">
|
||||
<pre class="bg-muted overflow-auto rounded-md p-4 text-sm">{{
|
||||
transformedValuesPreview
|
||||
}}</pre>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben Alert
|
||||
|
||||
`Alert` provides lightweight JavaScript-driven dialogs for simple `alert`, `confirm`, and `prompt` style interactions.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Use `alert` for a single confirm button dialog:
|
||||
|
||||
<DemoPreview dir="demos/vben-alert/alert" />
|
||||
|
||||
Use `confirm` for confirm/cancel interactions:
|
||||
|
||||
<DemoPreview dir="demos/vben-alert/confirm" />
|
||||
|
||||
Use `prompt` when you need simple user input:
|
||||
|
||||
<DemoPreview dir="demos/vben-alert/prompt" />
|
||||
|
||||
## useAlertContext
|
||||
|
||||
If `content`, `footer`, or `icon` is rendered through a custom component, you can call `useAlertContext()` inside that component to access the current dialog actions.
|
||||
|
||||
| Method | Description | Type |
|
||||
| ----------- | -------------------------- | ------------ |
|
||||
| `doConfirm` | trigger the confirm action | `() => void` |
|
||||
| `doCancel` | trigger the cancel action | `() => void` |
|
||||
|
||||
## Core Types
|
||||
|
||||
```ts
|
||||
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
|
||||
|
||||
export type BeforeCloseScope = {
|
||||
isConfirm: boolean;
|
||||
};
|
||||
|
||||
export type AlertProps = {
|
||||
beforeClose?: (
|
||||
scope: BeforeCloseScope,
|
||||
) => boolean | Promise<boolean | undefined> | undefined;
|
||||
bordered?: boolean;
|
||||
buttonAlign?: 'center' | 'end' | 'start';
|
||||
cancelText?: string;
|
||||
centered?: boolean;
|
||||
confirmText?: string;
|
||||
containerClass?: string;
|
||||
content: Component | string;
|
||||
contentClass?: string;
|
||||
contentMasking?: boolean;
|
||||
footer?: Component | string;
|
||||
icon?: Component | IconType;
|
||||
overlayBlur?: number;
|
||||
showCancel?: boolean;
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type PromptProps<T = any> = {
|
||||
beforeClose?: (scope: {
|
||||
isConfirm: boolean;
|
||||
value: T | undefined;
|
||||
}) => boolean | Promise<boolean | undefined> | undefined;
|
||||
component?: Component;
|
||||
componentProps?: Recordable<any>;
|
||||
componentSlots?:
|
||||
| (() => any)
|
||||
| Recordable<unknown>
|
||||
| VNode
|
||||
| VNodeArrayChildren;
|
||||
defaultValue?: T;
|
||||
modelPropName?: string;
|
||||
} & Omit<AlertProps, 'beforeClose'>;
|
||||
```
|
||||
@@ -1,69 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben ApiComponent
|
||||
|
||||
`ApiComponent` is a wrapper used to attach remote-option loading behavior to an existing component while preserving the original component usage pattern.
|
||||
|
||||
## Common Usage
|
||||
|
||||
The current wrapper flow is:
|
||||
|
||||
- pass the target component through `component`
|
||||
- fetch remote data through `api`
|
||||
- transform data through `beforeFetch` and `afterFetch`
|
||||
- map remote fields through `resultField`, `valueField`, `labelField`, and `childrenField`
|
||||
- pass normalized options to the target component through `optionsPropName`
|
||||
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import { ApiComponent } from '@vben/common-ui';
|
||||
|
||||
import { Cascader } from 'ant-design-vue';
|
||||
|
||||
function fetchApi() {
|
||||
return Promise.resolve([
|
||||
{
|
||||
label: 'Zhejiang',
|
||||
value: 'zhejiang',
|
||||
children: [{ label: 'Hangzhou', value: 'hangzhou' }],
|
||||
},
|
||||
]);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ApiComponent
|
||||
:api="fetchApi"
|
||||
:component="Cascader"
|
||||
:immediate="false"
|
||||
children-field="children"
|
||||
loading-slot="suffixIcon"
|
||||
visible-event="onDropdownVisibleChange"
|
||||
/>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Current Props
|
||||
|
||||
| Prop | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `component` | wrapped target component | `Component` |
|
||||
| `api` | remote request function | `(arg?: any) => Promise<any>` |
|
||||
| `params` | extra request params | `Record<string, any>` |
|
||||
| `beforeFetch` | hook before request | `AnyPromiseFunction` |
|
||||
| `afterFetch` | hook after request | `AnyPromiseFunction` |
|
||||
| `visibleEvent` | event name used to lazy-load data | `string` |
|
||||
| `loadingSlot` | slot name used to render the loading icon | `string` |
|
||||
| `modelPropName` | model prop name of the wrapped component | `string` |
|
||||
| `autoSelect` | auto-pick the first / last / only option, or use a custom function | `'first' \| 'last' \| 'one' \| ((items) => item) \| false` |
|
||||
|
||||
## Exposed Methods
|
||||
|
||||
| Method | Description |
|
||||
| ------------------------ | -------------------------------------- |
|
||||
| `getComponentRef()` | returns the wrapped component instance |
|
||||
| `updateParam(newParams)` | merges and updates request params |
|
||||
| `getOptions()` | returns loaded options |
|
||||
| `getValue()` | returns the current bound value |
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben CountToAnimator
|
||||
|
||||
`CountToAnimator` renders animated number transitions.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Use `start-val`, `end-val`, and `duration` to control the animation range and timing.
|
||||
|
||||
<DemoPreview dir="demos/vben-count-to-animator/basic" />
|
||||
|
||||
## Formatting
|
||||
|
||||
Use `prefix`, `suffix`, `separator`, and `decimal` to control how the number is displayed.
|
||||
|
||||
<DemoPreview dir="demos/vben-count-to-animator/custom" />
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `startVal` | starting value | `number` | `0` |
|
||||
| `endVal` | ending value | `number` | `2021` |
|
||||
| `duration` | animation duration in ms | `number` | `1500` |
|
||||
| `autoplay` | start automatically | `boolean` | `true` |
|
||||
| `prefix` | value prefix | `string` | `''` |
|
||||
| `suffix` | value suffix | `string` | `''` |
|
||||
| `separator` | thousands separator | `string` | `','` |
|
||||
| `decimal` | decimal separator | `string` | `'.'` |
|
||||
| `color` | text color | `string` | `''` |
|
||||
| `useEasing` | enable transition preset easing | `boolean` | `true` |
|
||||
| `transition` | transition preset name | `keyof typeof TransitionPresets` | `'linear'` |
|
||||
| `decimals` | decimal places to keep | `number` | `0` |
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| ------------ | ------------------------------- | ------------ |
|
||||
| `started` | fired when the animation starts | `() => void` |
|
||||
| `finished` | fired when the animation ends | `() => void` |
|
||||
| `onStarted` | deprecated alias of `started` | `() => void` |
|
||||
| `onFinished` | deprecated alias of `finished` | `() => void` |
|
||||
|
||||
## Exposed Methods
|
||||
|
||||
| Method | Description | Type |
|
||||
| ------- | --------------------------------- | ------------ |
|
||||
| `reset` | reset to `startVal` and run again | `() => void` |
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben Drawer
|
||||
|
||||
`Vben Drawer` is the shared drawer wrapper used by the framework. It supports auto-height layout, loading state, connected components, and an imperative API similar to the modal API.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```ts
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
// props
|
||||
// events
|
||||
});
|
||||
```
|
||||
|
||||
<DemoPreview dir="demos/vben-drawer/basic" />
|
||||
|
||||
## Current Usage Notes
|
||||
|
||||
- If you use `connectedComponent`, the inner and outer components share data through `drawerApi.setData()` and `drawerApi.getData()`.
|
||||
- Default drawer behavior can be adjusted in `apps/<app>/src/bootstrap.ts` through `setDefaultDrawerProps(...)`.
|
||||
- `setState(...)` works on `DrawerState`, not `ModalState`.
|
||||
|
||||
## Key Props
|
||||
|
||||
| Prop | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `appendToMain` | mount inside the main content area instead of `body` | `boolean` |
|
||||
| `connectedComponent` | connect an inner component to the drawer wrapper | `Component` |
|
||||
| `closeIconPlacement` | position of the close icon | `'left' \| 'right'` |
|
||||
| `placement` | drawer side | `'left' \| 'right' \| 'top' \| 'bottom'` |
|
||||
| `overlayBlur` | blur amount for the overlay | `number` |
|
||||
| `submitting` | lock drawer interactions while submitting | `boolean` |
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `onBeforeClose` | called before close; returning `false` or rejecting prevents close | `() => Promise<boolean \| undefined> \| boolean \| undefined` |
|
||||
| `onOpenChange` | called when open state changes | `(isOpen: boolean) => void` |
|
||||
| `onOpened` | called after open animation completes | `() => void` |
|
||||
| `onClosed` | called after close animation completes | `() => void` |
|
||||
|
||||
## drawerApi
|
||||
|
||||
| Method | Description |
|
||||
| ----------------------- | -------------------------------------- |
|
||||
| `setState(...)` | updates drawer state |
|
||||
| `open()` | opens the drawer |
|
||||
| `close()` | closes the drawer |
|
||||
| `setData(data)` | stores shared data |
|
||||
| `getData<T>()` | reads shared data |
|
||||
| `lock(isLocked = true)` | locks the drawer into submitting state |
|
||||
| `unlock()` | alias for `lock(false)` |
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben EllipsisText
|
||||
|
||||
`EllipsisText` displays long text with truncation, tooltip support, and optional expand/collapse behavior.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Pass the text through the default slot and limit the visual width with `maxWidth`.
|
||||
|
||||
<DemoPreview dir="demos/vben-ellipsis-text/line" />
|
||||
|
||||
## Current Props
|
||||
|
||||
| Prop | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `expand` | allow click-to-expand behavior | `boolean` | `false` |
|
||||
| `line` | max visible line count | `number` | `1` |
|
||||
| `maxWidth` | max width of the text area | `number \| string` | `'100%'` |
|
||||
| `placement` | tooltip placement | `'bottom' \| 'left' \| 'right' \| 'top'` | `'top'` |
|
||||
| `tooltip` | enable tooltip | `boolean` | `true` |
|
||||
| `tooltipWhenEllipsis` | only show tooltip when text is actually truncated | `boolean` | `false` |
|
||||
| `ellipsisThreshold` | pixel threshold used when checking truncation | `number` | `3` |
|
||||
| `tooltipBackgroundColor` | tooltip background color | `string` | `''` |
|
||||
| `tooltipColor` | tooltip text color | `string` | `''` |
|
||||
| `tooltipFontSize` | tooltip font size in px | `number` | `14` |
|
||||
| `tooltipMaxWidth` | tooltip max width in px | `number` | - |
|
||||
| `tooltipOverlayStyle` | tooltip content style | `CSSProperties` | `{ textAlign: 'justify' }` |
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `expandChange` | fired when expand state changes | `(isExpand: boolean) => void` |
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| --------- | ---------------------- |
|
||||
| `tooltip` | custom tooltip content |
|
||||
@@ -1,214 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben Form
|
||||
|
||||
`Vben Form` is the shared form abstraction used across different UI-library variants such as `Ant Design Vue`, `Element Plus`, `Naive UI`, and other adapters added inside this repository.
|
||||
|
||||
> If some details are not obvious from the docs, check the live demos as well.
|
||||
|
||||
## Adapter Setup
|
||||
|
||||
Each app keeps its own adapter layer under `src/adapter/form.ts` and `src/adapter/component/index.ts`.
|
||||
|
||||
The current adapter pattern is:
|
||||
|
||||
- initialize the shared component adapter first
|
||||
- call `setupVbenForm(...)`
|
||||
- map special `v-model:*` prop names through `modelPropNameMap`
|
||||
- keep the form empty state aligned with the actual UI library behavior
|
||||
|
||||
### Form Adapter Example
|
||||
|
||||
```ts
|
||||
import type {
|
||||
VbenFormSchema as FormSchema,
|
||||
VbenFormProps,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentType } from './component';
|
||||
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { initComponentAdapter } from './component';
|
||||
|
||||
initComponentAdapter();
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
baseModelPropName: 'value',
|
||||
emptyStateValue: null,
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
```
|
||||
|
||||
### Component Adapter Example
|
||||
|
||||
```ts
|
||||
import type { Component, SetupContext } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { globalShareState } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
import {
|
||||
AutoComplete,
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
Divider,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputPassword,
|
||||
Mentions,
|
||||
notification,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Textarea,
|
||||
TimePicker,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
const withDefaultPlaceholder = <T extends Component>(
|
||||
component: T,
|
||||
type: 'input' | 'select',
|
||||
) => {
|
||||
return (props: any, { attrs, slots }: Omit<SetupContext, 'expose'>) => {
|
||||
const placeholder = props?.placeholder || $t(`ui.placeholder.${type}`);
|
||||
return h(component, { ...props, ...attrs, placeholder }, slots);
|
||||
};
|
||||
};
|
||||
|
||||
export type ComponentType =
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'DefaultButton'
|
||||
| 'Divider'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'InputPassword'
|
||||
| 'Mentions'
|
||||
| 'PrimaryButton'
|
||||
| 'Radio'
|
||||
| 'RadioGroup'
|
||||
| 'RangePicker'
|
||||
| 'Rate'
|
||||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| BaseFormComponentType;
|
||||
|
||||
async function initComponentAdapter() {
|
||||
const components: Partial<Record<ComponentType, Component>> = {
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
DefaultButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
Input: withDefaultPlaceholder(Input, 'input'),
|
||||
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||
Mentions: withDefaultPlaceholder(Mentions, 'input'),
|
||||
PrimaryButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||
},
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RangePicker,
|
||||
Rate,
|
||||
Select: withDefaultPlaceholder(Select, 'select'),
|
||||
Space,
|
||||
Switch,
|
||||
Textarea: withDefaultPlaceholder(Textarea, 'input'),
|
||||
TimePicker,
|
||||
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
|
||||
Upload,
|
||||
};
|
||||
|
||||
globalShareState.setComponents(components);
|
||||
globalShareState.defineMessage({
|
||||
copyPreferencesSuccess: (title, content) => {
|
||||
notification.success({
|
||||
description: content,
|
||||
message: title,
|
||||
placement: 'bottomRight',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export { initComponentAdapter };
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Create the form through `useVbenForm`:
|
||||
|
||||
<DemoPreview dir="demos/vben-form/basic" />
|
||||
|
||||
## Value Formatting
|
||||
|
||||
Use `schema.valueFormat` when the component value is convenient for the UI but the final payload returned by `getValues()` should use a different shape.
|
||||
|
||||
- return a value to write back to the current field
|
||||
- call `setValue(key, nextValue)` to write derived fields
|
||||
- return `undefined` to keep the original field removed after decomposition
|
||||
|
||||
<DemoPreview dir="demos/vben-form/value-format" />
|
||||
|
||||
## Key API Notes
|
||||
|
||||
- `useVbenForm` returns `[Form, formApi]`
|
||||
- `formApi.getFieldComponentRef()` and `formApi.getFocusedField()` are available in current versions
|
||||
- `handleValuesChange(values, fieldsChanged)` includes the second parameter in newer versions
|
||||
- `fieldMappingTime` and `scrollToFirstError` are part of the current form props
|
||||
- `schema.valueFormat` lets `getValues()` transform UI values into backend-friendly payloads
|
||||
|
||||
## Reference
|
||||
|
||||
For the complete Chinese API tables and more examples, see the Chinese component page if you need the full parameter matrix.
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben Modal
|
||||
|
||||
`Vben Modal` is the shared modal wrapper used by the framework. It supports draggable behavior, fullscreen mode, auto-height handling, loading state, connected components, and an imperative API.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```ts
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
// props
|
||||
// events
|
||||
});
|
||||
```
|
||||
|
||||
<DemoPreview dir="demos/vben-modal/basic" />
|
||||
|
||||
## Current Usage Notes
|
||||
|
||||
- If you use `connectedComponent`, the inner and outer components share data through `modalApi.setData()` and `modalApi.getData()`.
|
||||
- When `connectedComponent` is present, avoid pushing extra modal props through the connected side. Prefer `useVbenModal(...)` or `modalApi.setState(...)`.
|
||||
- Default modal behavior can be adjusted in `apps/<app>/src/bootstrap.ts` through `setDefaultModalProps(...)`.
|
||||
|
||||
## Key Props
|
||||
|
||||
| Prop | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `appendToMain` | mount inside the main content area instead of `body` | `boolean` |
|
||||
| `connectedComponent` | connect an inner component to the modal wrapper | `Component` |
|
||||
| `animationType` | modal enter/leave animation | `'slide' \| 'scale'` |
|
||||
| `fullscreenButton` | show or hide the fullscreen toggle | `boolean` |
|
||||
| `overlayBlur` | blur amount for the overlay | `number` |
|
||||
| `submitting` | lock modal interactions while submitting | `boolean` |
|
||||
|
||||
## Events
|
||||
|
||||
| Event | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `onBeforeClose` | called before close; returning `false` or rejecting prevents close | `() => Promise<boolean \| undefined> \| boolean \| undefined` |
|
||||
| `onOpenChange` | called when open state changes | `(isOpen: boolean) => void` |
|
||||
| `onOpened` | called after open animation completes | `() => void` |
|
||||
| `onClosed` | called after close animation completes | `() => void` |
|
||||
|
||||
## modalApi
|
||||
|
||||
| Method | Description |
|
||||
| ----------------------- | ------------------------------------- |
|
||||
| `setState(...)` | updates modal state |
|
||||
| `open()` | opens the modal |
|
||||
| `close()` | closes the modal |
|
||||
| `setData(data)` | stores shared data |
|
||||
| `getData<T>()` | reads shared data |
|
||||
| `lock(isLocked = true)` | locks the modal into submitting state |
|
||||
| `unlock()` | alias for `lock(false)` |
|
||||
@@ -1,87 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Vben Vxe Table
|
||||
|
||||
`Vben Vxe Table` wraps `vxe-table` together with `Vben Form` so you can build searchable data grids with a shared API.
|
||||
|
||||
## Adapter Example
|
||||
|
||||
The current renderer adapter uses `renderTableDefault(...)` for table cell rendering:
|
||||
|
||||
```ts
|
||||
vxeUI.renderer.add('CellImage', {
|
||||
renderTableDefault(_renderOpts, params) {
|
||||
const { column, row } = params;
|
||||
return h(Image, { src: row[column.field] });
|
||||
},
|
||||
});
|
||||
|
||||
vxeUI.renderer.add('CellLink', {
|
||||
renderTableDefault(renderOpts) {
|
||||
const { props } = renderOpts;
|
||||
return h(
|
||||
Button,
|
||||
{ size: 'small', type: 'link' },
|
||||
{ default: () => props?.text },
|
||||
);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
gridOptions: {},
|
||||
formOptions: {},
|
||||
gridEvents: {},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Grid />
|
||||
</template>
|
||||
```
|
||||
|
||||
<DemoPreview dir="demos/vben-vxe-table/basic" />
|
||||
|
||||
## GridApi
|
||||
|
||||
| Method | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `setLoading` | update loading state | `(loading: boolean) => void` |
|
||||
| `setGridOptions` | merge new grid options | `(options: Partial<VxeGridProps['gridOptions']>) => void` |
|
||||
| `reload` | reload data and reset pagination | `(params?: Record<string, any>) => void` |
|
||||
| `query` | query data while keeping the current page | `(params?: Record<string, any>) => void` |
|
||||
| `grid` | `vxe-grid` instance | `VxeGridInstance` |
|
||||
| `formApi` | search form API | `FormApi` |
|
||||
| `toggleSearchForm` | toggle or force the search form visible state | `(show?: boolean) => boolean` |
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Description | Type |
|
||||
| --- | --- | --- |
|
||||
| `tableTitle` | table title | `string` |
|
||||
| `tableTitleHelp` | help text for the table title | `string` |
|
||||
| `class` | class for the outer container | `string` |
|
||||
| `gridClass` | class for the `vxe-grid` node | `string` |
|
||||
| `gridOptions` | `vxe-grid` options | `DeepPartial<VxeTableGridOptions>` |
|
||||
| `gridEvents` | `vxe-grid` event handlers | `DeepPartial<VxeGridListeners>` |
|
||||
| `formOptions` | search form options | `VbenFormProps` |
|
||||
| `showSearchForm` | whether the search form is visible | `boolean` |
|
||||
| `separator` | separator between the search form and table body | `boolean \| SeparatorOptions` |
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ----------------- | ------------------------------------------------------- |
|
||||
| `toolbar-actions` | left side of the toolbar, near the title |
|
||||
| `toolbar-tools` | right side of the toolbar, before built-in tool buttons |
|
||||
| `table-title` | custom table title |
|
||||
|
||||
All named slots starting with `form-` are forwarded to the search form.
|
||||
@@ -1,15 +0,0 @@
|
||||
# Introduction
|
||||
|
||||
::: info README
|
||||
|
||||
This section documents the framework components, including their usage patterns, configuration points, and major APIs. If the built-in wrappers do not fit your needs, you can always use native components directly or build your own abstractions.
|
||||
|
||||
:::
|
||||
|
||||
## Layout Components
|
||||
|
||||
Layout components are usually used as top-level containers inside the page content area. They provide shared layout styles and some baseline behavior.
|
||||
|
||||
## Common Components
|
||||
|
||||
Common components include frequently used UI building blocks such as modals, drawers, forms, and API-backed selectors. Most of them are implemented on top of shared Tailwind CSS and shadcn-vue based primitives, while still allowing each app to adapt them to its own UI library.
|
||||
@@ -1,29 +0,0 @@
|
||||
---
|
||||
outline: deep
|
||||
---
|
||||
|
||||
# Page
|
||||
|
||||
`Page` is the standard top-level layout container for business pages. It provides a header area, a content area, and an optional footer area.
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| `title` | page title | `string \| slot` | - |
|
||||
| `description` | page description | `string \| slot` | - |
|
||||
| `contentClass` | class for the content area | `string` | - |
|
||||
| `headerClass` | class for the header area | `string` | - |
|
||||
| `footerClass` | class for the footer area | `string` | - |
|
||||
| `autoContentHeight` | auto-calculate the content area height from the visible layout height | `boolean` | `false` |
|
||||
| `heightOffset` | extra height offset subtracted from the content area when auto height is enabled | `number` | `0` |
|
||||
|
||||
## Slots
|
||||
|
||||
| Slot | Description |
|
||||
| ------------- | ------------------------- |
|
||||
| `default` | page content |
|
||||
| `title` | custom title |
|
||||
| `description` | custom description |
|
||||
| `extra` | right-side header content |
|
||||
| `footer` | footer content |
|
||||
@@ -1,231 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { PlaygroundPreferencesExtension } from '#/preferences';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import {
|
||||
getCustomPreferences,
|
||||
updateCustomPreferences,
|
||||
} from '@vben/preferences';
|
||||
|
||||
import { Alert, Button, Card, Space, Tag } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
interface DemoTaskItem {
|
||||
id: number;
|
||||
owner: string;
|
||||
priority: 'P0' | 'P1' | 'P2';
|
||||
title: string;
|
||||
}
|
||||
|
||||
type HighlightTone = PlaygroundPreferencesExtension['highlightTone'];
|
||||
|
||||
const playgroundPreferences =
|
||||
getCustomPreferences<PlaygroundPreferencesExtension>();
|
||||
|
||||
const demoTasks: DemoTaskItem[] = [
|
||||
{ id: 1, owner: 'Luna', priority: 'P0', title: '同步租户配置到缓存' },
|
||||
{ id: 2, owner: 'Aiden', priority: 'P1', title: '补充角色权限回归用例' },
|
||||
{ id: 3, owner: 'Mia', priority: 'P0', title: '修复看板接口超时重试' },
|
||||
{ id: 4, owner: 'Noah', priority: 'P2', title: '整理本周运营周报模板' },
|
||||
{ id: 5, owner: 'Ethan', priority: 'P1', title: '验证暗黑主题下图表对比度' },
|
||||
{ id: 6, owner: 'Sophia', priority: 'P1', title: '更新埋点字段映射文档' },
|
||||
{ id: 7, owner: 'Lucas', priority: 'P2', title: '检查消息中心未读状态同步' },
|
||||
{ id: 8, owner: 'Emma', priority: 'P0', title: '补齐导出任务失败告警' },
|
||||
];
|
||||
|
||||
const toneMap = {
|
||||
default: {
|
||||
alertType: 'info',
|
||||
cardClass: 'border-primary/20 bg-primary/5',
|
||||
label: $t('demos.preferencesExtensionDemo.tones.default'),
|
||||
tagColor: 'processing',
|
||||
},
|
||||
success: {
|
||||
alertType: 'success',
|
||||
cardClass: 'border-emerald-500/20 bg-emerald-500/5',
|
||||
label: $t('demos.preferencesExtensionDemo.tones.success'),
|
||||
tagColor: 'success',
|
||||
},
|
||||
warning: {
|
||||
alertType: 'warning',
|
||||
cardClass: 'border-amber-500/20 bg-amber-500/5',
|
||||
label: $t('demos.preferencesExtensionDemo.tones.warning'),
|
||||
tagColor: 'warning',
|
||||
},
|
||||
} as const satisfies Record<
|
||||
HighlightTone,
|
||||
{
|
||||
alertType: 'info' | 'success' | 'warning';
|
||||
cardClass: string;
|
||||
label: string;
|
||||
tagColor: string;
|
||||
}
|
||||
>;
|
||||
|
||||
const visibleTasks = computed(() => {
|
||||
return demoTasks.slice(0, playgroundPreferences.defaultVisibleRows);
|
||||
});
|
||||
|
||||
const toneConfig = computed(() => {
|
||||
return toneMap[playgroundPreferences.highlightTone];
|
||||
});
|
||||
|
||||
const formattedPlaygroundPreferences = computed(() => {
|
||||
return JSON.stringify(playgroundPreferences, null, 2);
|
||||
});
|
||||
|
||||
const preClasses =
|
||||
'mt-4 overflow-auto rounded-lg border border-border bg-muted p-4 text-sm';
|
||||
|
||||
function applyPreset(type: 'compact' | 'focus' | 'review') {
|
||||
const presetMap: Record<
|
||||
typeof type,
|
||||
Partial<PlaygroundPreferencesExtension>
|
||||
> = {
|
||||
compact: {
|
||||
defaultVisibleRows: 3,
|
||||
enableQuickActions: false,
|
||||
highlightTone: 'warning',
|
||||
reportTitle: $t('demos.preferencesExtensionDemo.presetTitles.compact'),
|
||||
},
|
||||
focus: {
|
||||
defaultVisibleRows: 4,
|
||||
enableQuickActions: true,
|
||||
highlightTone: 'default',
|
||||
reportTitle: $t('demos.preferencesExtensionDemo.presetTitles.default'),
|
||||
},
|
||||
review: {
|
||||
defaultVisibleRows: 6,
|
||||
enableQuickActions: true,
|
||||
highlightTone: 'success',
|
||||
reportTitle: $t('demos.preferencesExtensionDemo.presetTitles.review'),
|
||||
},
|
||||
};
|
||||
|
||||
updateCustomPreferences<PlaygroundPreferencesExtension>(presetMap[type]);
|
||||
}
|
||||
|
||||
function getPriorityColor(priority: DemoTaskItem['priority']) {
|
||||
switch (priority) {
|
||||
case 'P0': {
|
||||
return 'error';
|
||||
}
|
||||
case 'P1': {
|
||||
return 'warning';
|
||||
}
|
||||
default: {
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page :title="$t('demos.features.preferencesExtension')">
|
||||
<template #description>
|
||||
<div class="mt-2 text-foreground/80">
|
||||
{{ $t('demos.preferencesExtensionDemo.description') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Card
|
||||
class="mb-5"
|
||||
:title="$t('demos.preferencesExtensionDemo.currentConfig')"
|
||||
>
|
||||
<Alert :type="toneConfig.alertType" show-icon>
|
||||
<template #message>
|
||||
{{
|
||||
$t('demos.preferencesExtensionDemo.currentTitle', {
|
||||
title: playgroundPreferences.reportTitle,
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
<template #description>
|
||||
{{
|
||||
$t('demos.preferencesExtensionDemo.currentDescription', {
|
||||
count: playgroundPreferences.defaultVisibleRows,
|
||||
quickActionText: playgroundPreferences.enableQuickActions
|
||||
? $t('demos.preferencesExtensionDemo.showQuickActions')
|
||||
: $t('demos.preferencesExtensionDemo.hideQuickActions'),
|
||||
tone: toneConfig.label,
|
||||
})
|
||||
}}
|
||||
</template>
|
||||
</Alert>
|
||||
|
||||
<div class="mt-4 rounded-xl border p-4" :class="toneConfig.cardClass">
|
||||
<div class="mb-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<div class="text-lg font-semibold">
|
||||
{{ playgroundPreferences.reportTitle }}
|
||||
</div>
|
||||
<div class="text-sm text-foreground/60">
|
||||
{{ $t('demos.preferencesExtensionDemo.boardDescription') }}
|
||||
</div>
|
||||
</div>
|
||||
<Tag :color="toneConfig.tagColor">
|
||||
{{ toneConfig.label }}
|
||||
</Tag>
|
||||
</div>
|
||||
|
||||
<Space
|
||||
v-if="playgroundPreferences.enableQuickActions"
|
||||
wrap
|
||||
class="mb-4"
|
||||
>
|
||||
<Button type="primary">
|
||||
{{ $t('demos.preferencesExtensionDemo.quickActions.create') }}
|
||||
</Button>
|
||||
<Button>
|
||||
{{ $t('demos.preferencesExtensionDemo.quickActions.export') }}
|
||||
</Button>
|
||||
<Button>
|
||||
{{ $t('demos.preferencesExtensionDemo.quickActions.refresh') }}
|
||||
</Button>
|
||||
</Space>
|
||||
<div v-else class="mb-4 text-sm text-foreground/60">
|
||||
{{ $t('demos.preferencesExtensionDemo.quickActionsEnabled') }}
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="task in visibleTasks"
|
||||
:key="task.id"
|
||||
class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-border bg-background px-4 py-3"
|
||||
>
|
||||
<div>
|
||||
<div class="font-medium">{{ task.title }}</div>
|
||||
<div class="text-sm text-foreground/60">
|
||||
{{ $t('demos.preferencesExtensionDemo.owner') }}:{{
|
||||
task.owner
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<Tag :color="getPriorityColor(task.priority)">
|
||||
{{ task.priority }}
|
||||
</Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card :title="$t('demos.preferencesExtensionDemo.presetTitle')">
|
||||
<Space wrap>
|
||||
<Button @click="applyPreset('focus')">
|
||||
{{ $t('demos.preferencesExtensionDemo.presetButtons.default') }}
|
||||
</Button>
|
||||
<Button @click="applyPreset('compact')">
|
||||
{{ $t('demos.preferencesExtensionDemo.presetButtons.compact') }}
|
||||
</Button>
|
||||
<Button type="primary" @click="applyPreset('review')">
|
||||
{{ $t('demos.preferencesExtensionDemo.presetButtons.review') }}
|
||||
</Button>
|
||||
</Space>
|
||||
|
||||
<pre :class="preClasses">{{ formattedPlaygroundPreferences }}</pre>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
@@ -1,39 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
import { Page } from '@vben/common-ui';
|
||||
import { VbenTiptap, VbenTiptapPreview } from '@vben/plugins/tiptap';
|
||||
|
||||
import { Card } from 'ant-design-vue';
|
||||
const content = ref(`
|
||||
<h1>Vben Tiptap</h1>
|
||||
<p>这个编辑器已经被封装在 <code>packages/effects/plugins/src/tiptap</code> 中。</p>
|
||||
<p>你可以直接在各个 app 里通过 <code>@vben/plugins/tiptap</code> 引入。</p>
|
||||
<blockquote>默认内置 StarterKit、Underline、TextAlign、Placeholder。</blockquote>
|
||||
`);
|
||||
const previewContent = computed(() => content.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page title="Tiptap 富文本">
|
||||
<template #description>
|
||||
<div class="mt-2 text-foreground/80">
|
||||
统一封装后的富文本编辑器,适合在各个 app 中直接复用。
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<Card class="mb-5" title="编辑器">
|
||||
<VbenTiptap v-model="content" />
|
||||
</Card>
|
||||
|
||||
<Card class="mb-5" title="富文本预览">
|
||||
<VbenTiptapPreview :content="previewContent" />
|
||||
</Card>
|
||||
|
||||
<Card title="HTML 输出">
|
||||
<pre class="overflow-auto rounded-xl border border-border bg-muted p-4">
|
||||
{{ previewContent }}
|
||||
</pre>
|
||||
</Card>
|
||||
</Page>
|
||||
</template>
|
||||
Reference in New Issue
Block a user