Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin
parent
a653e428f3
commit
adecddae67
|
|
@ -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,161 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed, nextTick, onMounted, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import { Page } from '@vben/common-ui';
|
|
||||||
|
|
||||||
import { Button, Card, message, Space, Tag } from 'ant-design-vue';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
|
|
||||||
import { useVbenForm } from '#/adapter/form';
|
|
||||||
|
|
||||||
import DocButton from '../doc-button.vue';
|
|
||||||
|
|
||||||
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 dayjs.isDayjs(currentValue)
|
|
||||||
? currentValue.format('YYYY-MM-DD HH:mm:ss')
|
|
||||||
: currentValue;
|
|
||||||
},
|
|
||||||
2,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleInspectValues() {
|
|
||||||
await syncPreviewValues();
|
|
||||||
message.success('已刷新 getValues 输出');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSetExampleValue() {
|
|
||||||
formApi.setValues({
|
|
||||||
deadline: dayjs('2026-04-12 18:30:00'),
|
|
||||||
keyword: 'invoice',
|
|
||||||
reportRange: [dayjs('2026-04-01 00:00:00'), dayjs('2026-04-12 23:59:59')],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
|
||||||
<Page
|
|
||||||
content-class="flex flex-col gap-4"
|
|
||||||
description="演示 schema.valueFormat 如何把组件值转换为提交/查询所需的 payload。"
|
|
||||||
title="表单 valueFormat"
|
|
||||||
>
|
|
||||||
<template #description>
|
|
||||||
<div class="text-muted-foreground space-y-2">
|
|
||||||
<p>
|
|
||||||
<code>form.values</code> 保持组件原始值,<code>getValues()</code> /
|
|
||||||
提交时会按 <code>schema.valueFormat</code> 输出转换后的 payload。
|
|
||||||
</p>
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<Tag color="processing">return 值:回写当前字段</Tag>
|
|
||||||
<Tag color="success">setValue:拆分写入其他字段</Tag>
|
|
||||||
<Tag color="warning">return undefined:保持原字段删除</Tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #extra>
|
|
||||||
<DocButton path="/components/common-ui/vben-form" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<Card title="valueFormat 示例">
|
|
||||||
<template #extra>
|
|
||||||
<Space wrap>
|
|
||||||
<Button @click="handleSetExampleValue">填充示例数据</Button>
|
|
||||||
<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>
|
|
||||||
</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>
|
|
||||||
Loading…
Reference in New Issue