2024-09-10 21:48:51 +08:00
|
|
|
|
<script lang="ts" setup>
|
2024-12-26 19:23:59 +08:00
|
|
|
|
import { h, ref } from 'vue';
|
2024-12-04 21:40:41 +08:00
|
|
|
|
|
2024-09-10 21:48:51 +08:00
|
|
|
|
import { Page } from '@vben/common-ui';
|
|
|
|
|
|
2024-12-26 19:23:59 +08:00
|
|
|
|
import { useDebounceFn } from '@vueuse/core';
|
2025-02-21 12:07:32 +08:00
|
|
|
|
import { Button, Card, message, Spin, Tag } from 'ant-design-vue';
|
2024-09-10 21:48:51 +08:00
|
|
|
|
import dayjs from 'dayjs';
|
|
|
|
|
|
2025-02-20 23:05:08 +08:00
|
|
|
|
import { useVbenForm, z } from '#/adapter/form';
|
2024-12-04 22:56:29 +08:00
|
|
|
|
import { getAllMenusApi } from '#/api';
|
2024-09-10 21:48:51 +08:00
|
|
|
|
|
2024-10-04 23:05:28 +08:00
|
|
|
|
import DocButton from '../doc-button.vue';
|
|
|
|
|
|
2024-12-26 19:23:59 +08:00
|
|
|
|
const keyword = ref('');
|
|
|
|
|
const fetching = ref(false);
|
|
|
|
|
// 模拟远程获取数据
|
|
|
|
|
function fetchRemoteOptions({ keyword = '选项' }: Record<string, any>) {
|
|
|
|
|
fetching.value = true;
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const options = Array.from({ length: 10 }).map((_, index) => ({
|
|
|
|
|
label: `${keyword}-${index}`,
|
|
|
|
|
value: `${keyword}-${index}`,
|
|
|
|
|
}));
|
|
|
|
|
resolve(options);
|
|
|
|
|
fetching.value = false;
|
|
|
|
|
}, 1000);
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-12-04 21:40:41 +08:00
|
|
|
|
|
2024-09-10 21:48:51 +08:00
|
|
|
|
const [BaseForm, baseFormApi] = useVbenForm({
|
|
|
|
|
// 所有表单项共用,可单独在表单内覆盖
|
|
|
|
|
commonConfig: {
|
2024-12-16 22:37:29 +08:00
|
|
|
|
// 在label后显示一个冒号
|
|
|
|
|
colon: true,
|
2024-09-10 21:48:51 +08:00
|
|
|
|
// 所有表单项
|
|
|
|
|
componentProps: {
|
|
|
|
|
class: 'w-full',
|
|
|
|
|
},
|
|
|
|
|
},
|
2024-11-09 15:00:59 +08:00
|
|
|
|
fieldMappingTime: [['rangePicker', ['startTime', 'endTime'], 'YYYY-MM-DD']],
|
2024-09-10 21:48:51 +08:00
|
|
|
|
// 提交函数
|
|
|
|
|
handleSubmit: onSubmit,
|
2024-11-09 15:00:59 +08:00
|
|
|
|
|
2024-09-10 21:48:51 +08:00
|
|
|
|
// 垂直布局,label和input在不同行,值为vertical
|
|
|
|
|
// 水平布局,label和input在同一行
|
2024-09-19 21:56:49 +08:00
|
|
|
|
layout: 'horizontal',
|
2024-09-10 21:48:51 +08:00
|
|
|
|
schema: [
|
|
|
|
|
{
|
|
|
|
|
// 组件需要在 #/adapter.ts内注册,并加上类型
|
|
|
|
|
component: 'Input',
|
|
|
|
|
// 对应组件的参数
|
|
|
|
|
componentProps: {
|
|
|
|
|
placeholder: '请输入用户名',
|
|
|
|
|
},
|
|
|
|
|
// 字段名
|
|
|
|
|
fieldName: 'username',
|
|
|
|
|
// 界面显示的label
|
|
|
|
|
label: '字符串',
|
2024-12-16 22:37:29 +08:00
|
|
|
|
rules: 'required',
|
2024-09-10 21:48:51 +08:00
|
|
|
|
},
|
2024-12-04 22:56:29 +08:00
|
|
|
|
{
|
|
|
|
|
// 组件需要在 #/adapter.ts内注册,并加上类型
|
|
|
|
|
component: 'ApiSelect',
|
|
|
|
|
// 对应组件的参数
|
|
|
|
|
componentProps: {
|
|
|
|
|
// 菜单接口转options格式
|
|
|
|
|
afterFetch: (data: { name: string; path: string }[]) => {
|
|
|
|
|
return data.map((item: any) => ({
|
|
|
|
|
label: item.name,
|
|
|
|
|
value: item.path,
|
|
|
|
|
}));
|
|
|
|
|
},
|
|
|
|
|
// 菜单接口
|
|
|
|
|
api: getAllMenusApi,
|
2025-04-12 14:02:35 +08:00
|
|
|
|
autoSelect: 'first',
|
2024-12-04 22:56:29 +08:00
|
|
|
|
},
|
|
|
|
|
// 字段名
|
|
|
|
|
fieldName: 'api',
|
|
|
|
|
// 界面显示的label
|
|
|
|
|
label: 'ApiSelect',
|
|
|
|
|
},
|
2024-12-26 19:23:59 +08:00
|
|
|
|
{
|
|
|
|
|
component: 'ApiSelect',
|
|
|
|
|
// 对应组件的参数
|
|
|
|
|
componentProps: () => {
|
|
|
|
|
return {
|
|
|
|
|
api: fetchRemoteOptions,
|
|
|
|
|
// 禁止本地过滤
|
|
|
|
|
filterOption: false,
|
|
|
|
|
// 如果正在获取数据,使用插槽显示一个loading
|
|
|
|
|
notFoundContent: fetching.value ? undefined : null,
|
|
|
|
|
// 搜索词变化时记录下来, 使用useDebounceFn防抖。
|
|
|
|
|
onSearch: useDebounceFn((value: string) => {
|
|
|
|
|
keyword.value = value;
|
|
|
|
|
}, 300),
|
|
|
|
|
// 远程搜索参数。当搜索词变化时,params也会更新
|
|
|
|
|
params: {
|
|
|
|
|
keyword: keyword.value || undefined,
|
|
|
|
|
},
|
|
|
|
|
showSearch: true,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
// 字段名
|
|
|
|
|
fieldName: 'remoteSearch',
|
|
|
|
|
// 界面显示的label
|
|
|
|
|
label: '远程搜索',
|
|
|
|
|
renderComponentContent: () => {
|
|
|
|
|
return {
|
|
|
|
|
notFoundContent: fetching.value ? h(Spin) : undefined,
|
|
|
|
|
};
|
|
|
|
|
},
|
2025-02-20 23:05:08 +08:00
|
|
|
|
rules: 'selectRequired',
|
2024-12-26 19:23:59 +08:00
|
|
|
|
},
|
2024-12-09 12:47:33 +08:00
|
|
|
|
{
|
|
|
|
|
component: 'ApiTreeSelect',
|
|
|
|
|
// 对应组件的参数
|
|
|
|
|
componentProps: {
|
|
|
|
|
// 菜单接口
|
|
|
|
|
api: getAllMenusApi,
|
|
|
|
|
// 菜单接口转options格式
|
|
|
|
|
labelField: 'name',
|
|
|
|
|
valueField: 'path',
|
2025-01-01 11:39:49 +08:00
|
|
|
|
childrenField: 'children',
|
2024-12-09 12:47:33 +08:00
|
|
|
|
},
|
|
|
|
|
// 字段名
|
|
|
|
|
fieldName: 'apiTree',
|
|
|
|
|
// 界面显示的label
|
|
|
|
|
label: 'ApiTreeSelect',
|
|
|
|
|
},
|
2024-09-10 21:48:51 +08:00
|
|
|
|
{
|
|
|
|
|
component: 'InputPassword',
|
|
|
|
|
componentProps: {
|
|
|
|
|
placeholder: '请输入密码',
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'password',
|
|
|
|
|
label: '密码',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'InputNumber',
|
|
|
|
|
componentProps: {
|
|
|
|
|
placeholder: '请输入',
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'number',
|
|
|
|
|
label: '数字(带后缀)',
|
|
|
|
|
suffix: () => '¥',
|
|
|
|
|
},
|
2024-12-04 21:42:21 +08:00
|
|
|
|
{
|
|
|
|
|
component: 'IconPicker',
|
|
|
|
|
fieldName: 'icon',
|
|
|
|
|
label: '图标',
|
|
|
|
|
},
|
2024-09-10 21:48:51 +08:00
|
|
|
|
{
|
2025-02-20 23:05:08 +08:00
|
|
|
|
colon: false,
|
2024-09-10 21:48:51 +08:00
|
|
|
|
component: 'Select',
|
|
|
|
|
componentProps: {
|
|
|
|
|
allowClear: true,
|
|
|
|
|
filterOption: true,
|
|
|
|
|
options: [
|
|
|
|
|
{
|
|
|
|
|
label: '选项1',
|
|
|
|
|
value: '1',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '选项2',
|
|
|
|
|
value: '2',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
placeholder: '请选择',
|
|
|
|
|
showSearch: true,
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'options',
|
2025-02-20 23:05:08 +08:00
|
|
|
|
label: () => h(Tag, { color: 'warning' }, () => '😎自定义:'),
|
2024-09-10 21:48:51 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'RadioGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{
|
|
|
|
|
label: '选项1',
|
|
|
|
|
value: '1',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '选项2',
|
|
|
|
|
value: '2',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'radioGroup',
|
|
|
|
|
label: '单选组',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'Radio',
|
|
|
|
|
fieldName: 'radio',
|
|
|
|
|
label: '',
|
|
|
|
|
renderComponentContent: () => {
|
|
|
|
|
return {
|
|
|
|
|
default: () => ['Radio'],
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'CheckboxGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
name: 'cname',
|
|
|
|
|
options: [
|
|
|
|
|
{
|
|
|
|
|
label: '选项1',
|
|
|
|
|
value: '1',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: '选项2',
|
|
|
|
|
value: '2',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'checkboxGroup',
|
|
|
|
|
label: '多选组',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'Checkbox',
|
|
|
|
|
fieldName: 'checkbox',
|
|
|
|
|
label: '',
|
|
|
|
|
renderComponentContent: () => {
|
|
|
|
|
return {
|
|
|
|
|
default: () => ['我已阅读并同意'],
|
|
|
|
|
};
|
|
|
|
|
},
|
2025-02-21 11:14:59 +08:00
|
|
|
|
rules: z
|
|
|
|
|
.boolean()
|
|
|
|
|
.refine((v) => v, { message: '为什么不同意?勾上它!' }),
|
2024-09-10 21:48:51 +08:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'Mentions',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{
|
|
|
|
|
label: 'afc163',
|
|
|
|
|
value: 'afc163',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'zombieJ',
|
|
|
|
|
value: 'zombieJ',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
placeholder: '请输入',
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'mentions',
|
|
|
|
|
label: '提及',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'Rate',
|
|
|
|
|
fieldName: 'rate',
|
|
|
|
|
label: '评分',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'Switch',
|
|
|
|
|
componentProps: {
|
|
|
|
|
class: 'w-auto',
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'switch',
|
2025-02-21 11:14:59 +08:00
|
|
|
|
help: () =>
|
|
|
|
|
['这是一个多行帮助信息', '第二行', '第三行'].map((v) => h('p', v)),
|
2024-09-10 21:48:51 +08:00
|
|
|
|
label: '开关',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'DatePicker',
|
|
|
|
|
fieldName: 'datePicker',
|
|
|
|
|
label: '日期选择框',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'RangePicker',
|
|
|
|
|
fieldName: 'rangePicker',
|
|
|
|
|
label: '范围选择器',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'TimePicker',
|
|
|
|
|
fieldName: 'timePicker',
|
|
|
|
|
label: '时间选择框',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
component: 'TreeSelect',
|
|
|
|
|
componentProps: {
|
|
|
|
|
allowClear: true,
|
|
|
|
|
placeholder: '请选择',
|
|
|
|
|
showSearch: true,
|
|
|
|
|
treeData: [
|
|
|
|
|
{
|
|
|
|
|
label: 'root 1',
|
|
|
|
|
value: 'root 1',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
label: 'parent 1',
|
|
|
|
|
value: 'parent 1',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
label: 'parent 1-0',
|
|
|
|
|
value: 'parent 1-0',
|
|
|
|
|
children: [
|
|
|
|
|
{
|
|
|
|
|
label: 'my leaf',
|
|
|
|
|
value: 'leaf1',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'your leaf',
|
|
|
|
|
value: 'leaf2',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'parent 1-1',
|
|
|
|
|
value: 'parent 1-1',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
label: 'parent 2',
|
|
|
|
|
value: 'parent 2',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
treeNodeFilterProp: 'label',
|
|
|
|
|
},
|
|
|
|
|
fieldName: 'treeSelect',
|
|
|
|
|
label: '树选择',
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
// 大屏一行显示3个,中屏一行显示2个,小屏一行显示1个
|
|
|
|
|
wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function onSubmit(values: Record<string, any>) {
|
|
|
|
|
message.success({
|
|
|
|
|
content: `form values: ${JSON.stringify(values)}`,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleSetFormValue() {
|
|
|
|
|
/**
|
|
|
|
|
* 设置表单值(多个)
|
|
|
|
|
*/
|
|
|
|
|
baseFormApi.setValues({
|
|
|
|
|
checkboxGroup: ['1'],
|
|
|
|
|
datePicker: dayjs('2022-01-01'),
|
|
|
|
|
mentions: '@afc163',
|
|
|
|
|
number: 3,
|
|
|
|
|
options: '1',
|
|
|
|
|
password: '2',
|
|
|
|
|
radioGroup: '1',
|
|
|
|
|
rangePicker: [dayjs('2022-01-01'), dayjs('2022-01-02')],
|
|
|
|
|
rate: 3,
|
|
|
|
|
switch: true,
|
|
|
|
|
timePicker: dayjs('2022-01-01 12:00:00'),
|
|
|
|
|
treeSelect: 'leaf1',
|
|
|
|
|
username: '1',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 设置单个表单值
|
|
|
|
|
baseFormApi.setFieldValue('checkbox', true);
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<Page
|
2024-09-30 09:47:16 +08:00
|
|
|
|
content-class="flex flex-col gap-4"
|
2024-09-10 21:48:51 +08:00
|
|
|
|
description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。"
|
|
|
|
|
title="表单组件"
|
|
|
|
|
>
|
2024-12-04 21:40:41 +08:00
|
|
|
|
<template #description>
|
|
|
|
|
<div class="text-muted-foreground">
|
|
|
|
|
<p>
|
|
|
|
|
表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
2024-10-04 23:05:28 +08:00
|
|
|
|
<template #extra>
|
2024-12-17 14:41:03 +08:00
|
|
|
|
<DocButton class="mb-2" path="/components/common-ui/vben-form" />
|
2024-10-04 23:05:28 +08:00
|
|
|
|
</template>
|
2025-02-21 12:07:32 +08:00
|
|
|
|
<Card title="基础示例">
|
2024-09-10 21:48:51 +08:00
|
|
|
|
<template #extra>
|
|
|
|
|
<Button type="primary" @click="handleSetFormValue">设置表单值</Button>
|
|
|
|
|
</template>
|
|
|
|
|
<BaseForm />
|
|
|
|
|
</Card>
|
|
|
|
|
</Page>
|
|
|
|
|
</template>
|