chore: 删除废弃文档和示例代码

This commit is contained in:
dap
2026-04-16 19:56:48 +08:00
parent 2dc05a1992
commit 89f53869ec
13 changed files with 0 additions and 1103 deletions

View File

@@ -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>

View File

@@ -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'>;
```

View File

@@ -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 |

View File

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

View File

@@ -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)` |

View File

@@ -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 |

View File

@@ -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.

View File

@@ -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)` |

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 |

View File

@@ -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>

View File

@@ -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>