123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- <script lang="ts" setup>
- import { h, ref } from 'vue';
- import { Page } from '@vben/common-ui';
- import { useDebounceFn } from '@vueuse/core';
- import {
- Button,
- Card,
- message,
- Spin,
- TabPane,
- Tabs,
- Tag,
- } from 'ant-design-vue';
- import dayjs from 'dayjs';
- import { useVbenForm, z } from '#/adapter/form';
- import { getAllMenusApi } from '#/api';
- import DocButton from '../doc-button.vue';
- const activeTab = ref('basic');
- 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);
- });
- }
- const [BaseForm, baseFormApi] = useVbenForm({
- // 所有表单项共用,可单独在表单内覆盖
- commonConfig: {
- // 在label后显示一个冒号
- colon: true,
- // 所有表单项
- componentProps: {
- class: 'w-full',
- },
- },
- fieldMappingTime: [['rangePicker', ['startTime', 'endTime'], 'YYYY-MM-DD']],
- // 提交函数
- handleSubmit: onSubmit,
- // 垂直布局,label和input在不同行,值为vertical
- // 水平布局,label和input在同一行
- layout: 'horizontal',
- schema: [
- {
- // 组件需要在 #/adapter.ts内注册,并加上类型
- component: 'Input',
- // 对应组件的参数
- componentProps: {
- placeholder: '请输入用户名',
- },
- // 字段名
- fieldName: 'username',
- // 界面显示的label
- label: '字符串',
- rules: 'required',
- },
- {
- // 组件需要在 #/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,
- },
- // 字段名
- fieldName: 'api',
- // 界面显示的label
- label: 'ApiSelect',
- },
- {
- 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,
- };
- },
- rules: 'selectRequired',
- },
- {
- component: 'ApiTreeSelect',
- // 对应组件的参数
- componentProps: {
- // 菜单接口
- api: getAllMenusApi,
- // 菜单接口转options格式
- labelField: 'name',
- valueField: 'path',
- childrenField: 'children',
- },
- // 字段名
- fieldName: 'apiTree',
- // 界面显示的label
- label: 'ApiTreeSelect',
- },
- {
- component: 'InputPassword',
- componentProps: {
- placeholder: '请输入密码',
- },
- fieldName: 'password',
- label: '密码',
- },
- {
- component: 'InputNumber',
- componentProps: {
- placeholder: '请输入',
- },
- fieldName: 'number',
- label: '数字(带后缀)',
- suffix: () => '¥',
- },
- {
- component: 'IconPicker',
- fieldName: 'icon',
- label: '图标',
- },
- {
- colon: false,
- component: 'Select',
- componentProps: {
- allowClear: true,
- filterOption: true,
- options: [
- {
- label: '选项1',
- value: '1',
- },
- {
- label: '选项2',
- value: '2',
- },
- ],
- placeholder: '请选择',
- showSearch: true,
- },
- fieldName: 'options',
- label: () => h(Tag, { color: 'warning' }, () => '😎自定义:'),
- },
- {
- 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: () => ['我已阅读并同意'],
- };
- },
- rules: z
- .boolean()
- .refine((v) => v, { message: '为什么不同意?勾上它!' }),
- },
- {
- 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',
- help: () =>
- ['这是一个多行帮助信息', '第二行', '第三行'].map((v) => h('p', v)),
- 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',
- });
- const [CustomLayoutForm] = useVbenForm({
- // 所有表单项共用,可单独在表单内覆盖
- commonConfig: {
- // 所有表单项
- componentProps: {
- class: 'w-full',
- },
- },
- layout: 'horizontal',
- schema: [
- {
- component: 'Select',
- fieldName: 'field1',
- label: '字符串',
- },
- {
- component: 'TreeSelect',
- fieldName: 'field2',
- label: '字符串',
- },
- {
- component: 'Mentions',
- fieldName: 'field3',
- label: '字符串',
- },
- {
- component: 'Input',
- fieldName: 'field4',
- label: '字符串',
- },
- {
- component: 'InputNumber',
- fieldName: 'field5',
- // 从第三列开始 相当于中间空了一列
- formItemClass: 'col-start-3',
- label: '前面空了一列',
- },
- {
- component: 'Textarea',
- fieldName: 'field6',
- // 占满三列空间 基线对齐
- formItemClass: 'col-span-3 items-baseline',
- label: '占满三列',
- },
- {
- component: 'Input',
- fieldName: 'field7',
- // 占满2列空间 从第二列开始 相当于前面空了一列
- formItemClass: 'col-span-2 col-start-2',
- label: '占满2列',
- },
- {
- component: 'Input',
- fieldName: 'field8',
- // 左右留空
- formItemClass: 'col-start-2',
- label: '左右留空',
- },
- {
- component: 'InputPassword',
- fieldName: 'field9',
- formItemClass: 'col-start-1',
- label: '字符串',
- },
- ],
- // 一共三列
- wrapperClass: '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
- content-class="flex flex-col gap-4"
- description="表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。"
- header-class="pb-0"
- title="表单组件"
- >
- <template #description>
- <div class="text-muted-foreground">
- <p>
- 表单组件基础示例,请注意,该页面用到的参数代码会添加一些简单注释,方便理解,请仔细查看。
- </p>
- </div>
- <Tabs v-model:active-key="activeTab" :tab-bar-style="{ marginBottom: 0 }">
- <TabPane key="basic" tab="基础示例" />
- <TabPane key="layout" tab="自定义布局" />
- </Tabs>
- </template>
- <template #extra>
- <DocButton class="mb-2" path="/components/common-ui/vben-form" />
- </template>
- <Card v-show="activeTab === 'basic'" title="基础示例">
- <template #extra>
- <Button type="primary" @click="handleSetFormValue">设置表单值</Button>
- </template>
- <BaseForm />
- </Card>
- <Card v-show="activeTab === 'layout'" title="使用tailwind自定义布局">
- <CustomLayoutForm />
- </Card>
- </Page>
- </template>
|