Arco-Design + Vue3 封装通过JSON数组类型配置表单组件
一、业务场景
在开发过程中频繁会使用到一些表单,封装一个简单的表单组件来处理这些业务逻辑
二、开发环境
Arco-Design + Vite + Vue3
三、挂载全局组件 TableForm
// components/index.ts注册组件
import {
App } from 'vue';
// ...其它组件
import TableForm from './table-form/index.vue';
export default {
install(Vue: App) {
// ...其它组件
Vue.component('TableForm', TableForm);
},
};
四、TableForm.vue组件介绍
<template>
<a-row>
<a-form ref="tableForm" :model="formModel" auto-label-width :rules="rules">
<a-col
v-for="field in (visibleFields as FieldProps)"
:key="field.name"
:span="22"
>
<a-form-item :field="field.name" :label="field.label">
<component
:is="field.type"
v-model="formModel[field.name]"
allow-clear
:multiple="field.multiple"
:style="field.style"
:allow-search="field.search"
:placeholder="field.placeholder"
:options="field.options"
:type="field.elType"
:field-names="field.fieldNames"
/>
</a-form-item>
</a-col>
</a-form>
</a-row>
</template>
<script lang="ts" setup>
import {
toRefs, ref, watch, computed } from 'vue';
import {
FormInstance } from '@arco-design/web-vue';
import type {
FieldProps } from '@/types/global';
const props = defineProps({
// 表单json配置项
formJson: {
type: Array,
},
// 表单校验规则
rules: {
type: Object,
},
// 表单数据
formState: {
type: Object,
},
});
// ref绑定组件
const tableForm = ref<FormInstance>();
const formModel = ref({
} as any);
const {
formJson } = toRefs(props);
defineExpose({
formModel,
tableForm,
});
/*
** 表单过滤,特殊业务场景需要,没有则可以删除
** 用于条件判断是否通过表单条件展示需要的内容
*/
const visibleFields = computed(() => {
if (formJson?.value === undefined) return false;
return formJson.value.filter((field: FieldProps) => {
if (field.name === 'pid') {
return formModel.value.level === '2';
}
return true;
});
});
// 监听表单数据变化赋值
watch(
() => props.formState,
(val) => {
if (!val) return;
formModel.value = val;
},
{
deep: true, immediate: true }
);
</script>
<script lang="ts">
export default {
name: 'TableForm',
};
</script>
<style lang="less" scoped>
:deep(.arco-form) {
align-items: center;
}
</style>
表单配置项
- allow-clear 是否支持清空
- multiple 选择框是否支持多选
- style a-form-item的样式
- allow-search 选择框是否支持搜索
- placeholder 占位符
- options选择框的选择项
- 表单组件内的type样式
- field-names options选项值配置
其它自定义可以依次添加
五、使用场景
<template>
<a-modal v-model:visible="visible" @ok="handleOk" @cancel="handleCancel">
<template #title>{
{
title }}</template>
<TableForm
ref="formRef"
:form-json="newFormJson"
:form-state="state.formModel"
:rules="rules"
></TableForm>
</a-modal>
</template>
<script setup lang="ts">
import {
reactive, toRefs, ref, watch, computed } from 'vue';
import {
omit } from 'lodash';
import {
cpDetail } from '@/api/settings';
import {
jsonToFormData } from '@/utils';
import {
FormModelProps } from '../types';
import {
adminFormJson } from '../formJson';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
type: {
type: String,
default: '',
},
record: {
type: Object,
default: () => ({
}),
},
serviceList: {
type: Array,
default: () => [],
},
logList: {
type: Array,
default: () => [],
},
});
const rules = {
real_name: [
{
required: true,
message: `所属人员不能为空!`,
},
],
account: [
{
required: true,
message: `账号不能为空!`,
},
],
pwd: [
{
required: true,
message: `请输入密码!`,
},
{
minLength: 6,
message: `密码不能少于六位!`,
},
],
comfirmpwd: [
{
required: true,
message: `请确认密码!`,
},
{
minLength: 6,
message: `密码不能少于六位!`,
},
],
pid: [
{
required: true,
message: `上级账号不能为空!`,
},
],
status: [
{
required: true,
message: `状态不能为空!`,
},
],
};
const {
visible, title, type, record, serviceList, logList } = toRefs(props);
const state = reactive({
formModel: {
level: '1',
} as FormModelProps,
});
const formRef = ref();
// 生成一个新的json,补全选择框的options
const newFormJson = computed(() => {
return adminFormJson.map((item: any) => {
if (item.name === 'service_ids') {
return {
...item,
options: serviceList.value,
};
}
return {
...item };
});
});
const emit = defineEmits(['onOk', 'onCancel']);
const handleCancel = async () => {
emit('onCancel', false);
};
const handleOk = async () => {
// 表单校验
const res = await formRef.value.tableForm?.validate();
if (!res) {
state.formModel.service_ids = state.formModel.service_ids.join();
if (state.formModel.level === '1')
state.formModel = omit(state.formModel, ['pid']);
emit('onOk', false, state.formModel);
}
};
const getCpDetail = async (id: string) => {
const {
result } = await cpDetail(jsonToFormData({
id }));
state.formModel = result;
state.formModel.service_ids = result.service_list.map((item) => item.service_id);
state.formModel.pid = data.pid === '0' ? '' : data.pid;
};
watch(
() => type,
(val) => {
if (val.value === 'edit') {
getCpDetail(record.value.id);
state.formModel = omit(state.formModel, [
'last_time',
'service_list',
'p_name',
]);
}
},
{
deep: true, immediate: true }
);
</script>
<style scoped lang="less">
.arco-btn {
width: 80px;
}
.arco-col {
text-align: center;
}
</style>
选择否也就是条件判断是否需要显示其它表单组件
下面贴一下formJson.ts
export const adminFormJson = [
{
name: 'real_name',
label: '所属人员',
type: 'a-input',
placeholder: '请输入所属人员名称',
options: [],
},
{
name: 'account',
label: '账号',
type: 'a-input',
placeholder: '请输入账号',
options: [],
},
{
name: 'service_ids',
label: '授权小程序',
type: 'a-select',
multiple: true,
style: `{textAlign: 'left'}`,
placeholder: '请选择授权小程序',
fieldNames: {
value: 'id', label: 'service_name' },
options: [],
},
{
name: 'pwd',
label: '密码',
type: 'a-input',
placeholder: '请输入密码',
options: [],
},
{
name: 'comfirmpwd',
label: '确认密码',
type: 'a-input',
placeholder: '请确认密码',
options: [],
},
{
name: 'level',
label: '是否一级账号',
type: 'a-radio-group',
placeholder: '',
elType: 'button',
options: [
{
value: '1',
label: '是',
},
{
value: '2',
label: '否',
},
],
},
{
name: 'pid',
label: '上级账号ID',
type: 'a-select',
placeholder: '请选择上级账号ID',
fieldNames: {
value: 'id', label: 'real_name' },
options: [],
},
{
name: 'status',
label: '状态',
type: 'a-select',
placeholder: '请选择账号状态',
options: [
{
value: '1',
label: '正常',
},
{
value: '2',
label: '禁用',
},
],
},
];