分享一下最近使用vben-admin中basicForm的一些心得吧(鱼都摸秃了)
首先确认一下您使用vben是2.8.0版本的而不是vben3(我也在学习中),其中有些不足之处也请各位大佬多多包涵,欢迎各位大佬在评论区留言指正,我也会不断学习。同时本文也会在各位大佬的指正之下不断修改。
首先是BasicForm的使用:
先看看文档的基本使用: 其中一些参数的介绍和一些表单的配置在文档中都有介绍,这里就不过多赘述了,不懂小伙伴仔细阅读一些文档,实在找不到配置的可以评论区向各位大佬请教,我能回答的也一定会去回复的。
先说一下我遇到的最多的吧,就是表单中select的数据问题,因为后端没有把返回数据统一起来label、key、value啥的都会给你,我这边最多的就是对返回数据的处理
js
// 这是基础的一个select
{
field: 'state',
label: '状态',
component: 'Select',
colProps: {
span: 12,
},
componentProps: {
options: [
{
label: '排队中',
value: 'IN_LINE',
},
{
label: '已退出',
value: 'HAS_EXITED',
},
{
label: '已办证',
value: 'HAS_CERTIFIED',
},
{
label: '待审核',
value: 'WAIT_REVIEWED',
},
{
label: '已驳回',
value: 'HAS_REJECTED',
},
],
},
required: true,
},
这是一个简单的Select所有选项都是由前端定死的
但是如果数据是后端给的并且数据并不是这种 [{ label:<String>, value:<String> }]
形式的,我们也可以通过ApiSelect的形式去获取动态数据
js
{
label: '角色',
colProps: {
span: 8,
},
field: 'roleId',
component: 'ApiSelect',
componentProps: {
api: (params) => Api.getRulesList({ params }),
getPopupContainer: () => document.body,
labelField: 'name',
valueField: 'id',
immediate: false,
placeholder: '请选择角色',
},
},
api: (params) => Api.getRulesList({ params })
是我用了axiosClient封装了一下,感兴趣的可以去npm上看一下这个,最终给我的就是接口返回的数组了
但是后端大佬又给我出难题了,给了[<string>,<string>,<string>......]
这种格式的,怎么办呢?
喽一眼文档:
我悟了
js
{
field: 'city',
component: 'ApiSelect',
label: '城市',
required: true,
componentProps: ({ formModel }) => {
return {
api: async (params) => {
let list = await Api.get_grid_cityList({ params });
return Promise.resolve(
list.map((i) => {
return {
label: i,
value: i,
};
}),
);
},
placeholder: '请选择城市',
immediate: true,
};
},
colProps: {
span: 12,
},
},
ok,那么这个问题解决之后,又来难题了(联动)那怎么搞?再去看看文档
嗯~不错,上手
js
{
field: 'city',
component: 'ApiSelect',
label: '城市',
required: true,
componentProps: ({ formModel }) => {
return {
api: async (params) => {
let list = await Api.get_grid_cityList({ params });
return Promise.resolve(
list.map((i) => {
return {
label: i,
value: i,
};
}),
);
},
placeholder: '请选择城市',
immediate: true,
onChange: (e) => {
countyParams.value.city = e;
formModel.county = null;
formModel.street = null;
formModel.gridName = null;
},
};
},
colProps: {
span: 12,
},
},
{
field: 'county',
component: 'ApiSelect',
label: '区县',
required: true,
componentProps: ({ formModel }) => {
return {
api: async (params) => {
let list = await Api.get_grid_countyList({ params });
return Promise.resolve(
list.map((i) => {
return {
label: i,
value: i,
};
}),
);
},
placeholder: '请选择区县',
params: countyParams.value,
onChange: (e) => {
countyParams.value.county = e;
formModel.street = null;
formModel.gridName = null;
},
immediate: true,
};
},
colProps: {
span: 12,
},
},
{
field: 'street',
component: 'ApiSelect',
label: '街道',
required: true,
componentProps: ({ formModel }) => {
return {
api: async (params) => {
let list = await Api.get_grid_streetList({ params });
return Promise.resolve(
list.map((i) => {
return {
label: i,
value: i,
};
}),
);
},
placeholder: '请选择街道',
params: countyParams.value,
onChange: (e) => {
countyParams.value.street = e;
formModel.gridName = null;
},
immediate: true,
};
},
colProps: {
span: 12,
},
},
这里注意:params变化是基于watch来完成的,所以你要确保它能侦听到你数据的变化
其实到这里我就有点不明白了,这一个又一个选择框的干嘛不用级联呢?问了下产品,产品:"客户想要这样的"(我内心os:你*** 的 *** ** )
然后我们看下插槽的使用
js
{
label: '组织',
colProps: {
span: 12,
},
field: 'organIds',
component: 'Select',
slot: 'organTree',
},
html
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
:title="getTitle"
width="600px"
@ok="handleSubmit"
>
<BasicForm @register="registerForm">
<template #organTree="{ model, field }">
<TreeSelect
v-model:value="model[field]"
show-search
treeCheckable
:show-checked-strategy="SHOW_PARENT"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="请选择组织"
allow-clear
tree-default-expand-all
:tree-data="treeData"
:field-names="{
children: 'children',
label: 'abbrName',
value: 'id',
}"
/>
</template>
</BasicForm>
</BasicModal>
</template>
然后就是校验
js
{
label: '手机',
field: 'mobile',
component: 'Input',
// required: true,
colProps: {
span: 12,
},
rules: [
{
required: false,
message: '请输入用户手机号',
},
{
validator: async (_, value) => {
if (!value) {
return Promise.resolve();
} else {
const role = /^((1[3,4,5,6,8,7,9][0-9])|(14[5,7])|(17[0,6,7,8])|(19[7]))\d{8}$/;
const regular = new RegExp(role);
if (regular.test(value)) {
return Promise.resolve();
} else {
return Promise.reject('手机号格式不正确');
}
}
},
},
],
},
然后碰到了一个问题,就是这些规则是写死的还好,但是我的校验如果是动态可以配置的,那么怎么办呢?
js
{
label: '密码',
field: 'password',
component: 'StrengthMeter',
componentProps: ({ formModel }) => {
return {
placeholder: '请输入密码',
onChange: (e) => {
formModel.password = e;
},
};
},
colProps: {
span: 12,
},
rules: [
{
validator: async (value) => {
const userStore = useUserStore();
// arr是我从登录的时候获取的一个数组里面是各种校验规则,比如必须包含特殊字符,密码长度,必须包含英文大小等等
const arr = userStore.regularStr;
if (!value) {
return Promise.resolve();
} else {
let a: any = [];
// 获取校验不成功的项
a = arr.filter((i) => {
return !i.str.test(value);
});
// 如果没有就抛出成功
if (a.length == 0) {
return Promise.resolve();
}
// 如果有就抛出首项
for (let i = 0; i < a.length; i++) {
return Promise.reject(a[i].warning);
}
}
},
// ❗❗❗ change 校验慢一步 blur 客户体验感不太好
trigger: 'blur',
},
],
},
这样就解决了校验规则可配的问题,但是如果是chage的话就会发现value始终获取的是上一次输入的值,确定了不是async的问题,请各位大佬指教。
然后就是在BasicTable中开启searchForm后form的使用
首先,基本的一些配置是和form差不多的,只是要使用form的方法需要额外做一下引用
然后就是插槽的使用,与BasicForm中的插槽不同的是,在BasicTable中开启searchForm之后要使用表单的插槽的话就需要额外加上"form-"
(话不多说上图)
当然这只是这种使用插槽的方式,你也可以使用别的方式来用。
重点来了,上文介绍的那种省市县联动的形式,我们已经解决了,但是呢产品那边又来活了,产品:你能加上默认值吗? 我:可以,加时间!产品:要多久?我:一周吧!🤫
首先明确一下需求,这个默认值是用于table的表头搜索的,并且之间有联动;那么 ------ 开工......
js
<template>
<TitleCard> </TitleCard>
<Card>
<BasicForm @register="register" />
</Card>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { Card } from 'ant-design-vue';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import TitleCard from '../components/TitleCard/index.vue';
import Api from '../api';
const formDate: any = ref({});
const schemas: FormSchema[] = [
{
field: 'year',
component: 'Select',
label: '年份',
colProps: {
span: 6,
},
},
{
field: 'month',
component: 'Select',
label: '月份',
colProps: {
span: 6,
},
},
];
const getHeardList = async () => {
const yearsList = await Api.market_monitor_years();
updateSchema({
label: '年份',
colProps: { xl: 6 },
field: 'year',
component: 'Select',
componentProps: ({ formModel }) => {
return {
getPopupContainer: () => document.body,
onChange: async (e) => {
if (formModel.month) {
formModel.month = null;
}
if (e) {
const monthOptions = await Api.common_getAllMonth({ params: { year: e } });
// getMonthParams.value.year = e;
updateSchema({
label: '月份',
colProps: {
span: 6,
},
field: 'month',
component: 'Select',
componentProps: ({ formModel }) => {
return {
getPopupContainer: () => document.body,
onChange: async (e) => {
if (e) {
formModel.month = e;
formDate.value = getFieldsValue();
}
},
options: monthOptions.map((i) => {
return {
label: i.key + '月',
value: i.value,
};
}),
};
},
});
if (monthOptions.length != 0) {
setFieldsValue({ month: monthOptions[0].value });
}
}
},
options: yearsList.map((i) => {
return {
label: i.key,
value: i.value,
};
}),
};
},
});
setFieldsValue({
year: yearsList[0].value,
});
};
getHeardList();
const [register, { updateSchema, setFieldsValue, getFieldsValue }] = useForm({
labelWidth: 100,
schemas,
actionColOptions: {
span: 24,
},
showActionButtonGroup: false,
});
</script>
因为要设置默认值,又要联动,所以写出来的东西一层又一层的看起来很繁琐,有没有更好的方式来写,请各位大佬指正。
还有就是表格单元格开启编辑 这个编辑就不能在schmes里面配置了,需要在BasicTable的columns里面进行配置
js
// 正常的columns
{
title: '代码',
dataIndex: 'regularCode',
key: 'regularCode',
width: 100,
},
js
{
title: '方式',
dataIndex: 'methodRevised',
key: 'methodRevised',
// width: 160,
edit: true,
editComponent: 'ApiSelect',
editComponentProps: ({}) => {
return {
// 正常这样配置就会拿着当前行的id去请求下拉框数据
// api: () => Api.getDeliveryOptionType({ pathVars: { id: record.id } }),
// 因为后面业务变更,所以我这边需要拿着表头筛选框的数据来请求数据
api: async () => {
const formModel = getForm().getFieldsValue();
if (!formModel.year || !formModel.month || !formModel.freq) {
return;
}
let list = await Api.getDeliveryOptionType({ params: { ...formModel } });
return Promise.resolve(list);
},
getPopupContainer: () => document.body,
labelField: 'value',
valueField: 'key',
};
},
},
表单提交在他们示例里面已经有很详细的代码演示了,这里就不再多说了
欢迎JYM评论指正