1. 前文
プロジェクトアドレス: git clone form-demo: 一般的な el-form をカプセル化する
バックグラウンド管理システムの最も一般的な形式は、フォームです。最も一般的な形式は、入力ボックス、ドロップダウン選択、日付選択、ラジオ選択、チェック ボックスなどです。システムに複数のモジュールを追加する場合は、いくつかのモジュールをコピーして貼り付けます。el-form、el-form -item、一つ言えることは、要件を完了するのは速いですが、コードの冗長な部分が多すぎます。設定を通じてel-form と el-form-item を自動的に生成できますか
コードをラップする前に:
コードをラップした後:
2 つのカプセル化メソッドの変数名とメソッド名は基本的に同じです。
2. カスタムコンポーネントの方法で el-form をカプセル化する
2.1. カプセル化
(1) commentFormフォルダを新規作成し、index.vueファイルを作成します。
(2)index.vue内
<template>
<div>
<el-form ref="form" :model="form" :rules="rules" :inline="inline" :label-width="labelWidth">
<template v-for="(item, index) in formItemList">
<!-- 输入框类型 -->
<template v-if="item.type === 'input'">
<el-form-item :label="item.label" :prop="item.model">
<el-input v-model.trim="form[item.model]" :type="`${item.category || 'text'}`"
:style="`width: ${item.width || '250px'}`" :clearable="item.clearable === undefined || item.clearable"
filterable :placeholder="item.placeholder" />
</el-form-item>
</template>
<!-- 下拉选择框 -->
<template v-if="item.type === 'select'">
<el-form-item :label="item.label" :prop="item.model">
<el-select v-model.trim="form[item.model]" :style="`width:${item.width || '250px'}`" clearable
:placeholder="item.placeholder || ''">
<el-option v-for="(i, key) in item.options" :key="i[item.value] || key" :label="i[item.label] || i.label"
:value="i[item.value] || i.value">
</el-option>
</el-select>
</el-form-item>
</template>
<!-- 日期选择器 -->
<template v-if="item.type === 'date-picker'">
<el-form-item :prop="item.model" :label="item.label">
<el-date-picker v-model.trim="form[item.model]" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</el-form-item>
</template>
<!-- 开关 -->
<template v-if="item.type === 'switch'">
<el-form-item :prop="item.model" :label="item.label">
<el-switch v-model="form[item.model]"></el-switch>
</el-form-item>
</template>
<!-- 复选框 -->
<template v-if="item.type === 'checkbox'">
<el-form-item :prop="item.model" :label="item.label">
<el-checkbox-group v-model="form[item.model]">
<el-checkbox :label="item.label" :key="index" v-for="(item, index) in item.options"></el-checkbox>
</el-checkbox-group>
</el-form-item>
</template>
<!-- 单选框 -->
<template v-if="item.type === 'radio'">
<el-form-item :prop="item.model" :label="item.label">
<el-radio-group v-model="form[item.model]">
<el-radio :label="item.label" :key="index" v-for="(item, index) in item.options"></el-radio>
</el-radio-group>
</el-form-item>
</template>
<!-- 自己拓展... -->
</template>
</el-form>
</div>
</template>
<script>
export default {
props: {
form: {
type: Object,
default: () => { }
},
// 生成el-form-item的数组
formItemList: {
type: Array,
default: () => [],
},
// 是否行内表单模式
inline: {
type: Boolean,
default: false,
},
// el-form-item的label宽度
labelWidth: {
type: String,
default: '80px',
},
// 表单校验规则
rules: {
type: Object,
default: () => { },
},
},
data() {
return {
}
},
methods: {
// 返回经过校验的表单
returnForm() {
// 表单校验
this.$refs['form'].validate((valid) => {
if (valid) {
this.$emit('getForm', this.form)
}
})
},
// 重置表单
resetForm() {
// 重置复选框
this.$refs.form.resetFields();
}
}
}
</script>
<style scoped></style>
(3) 注意すべき事項:
- el-checkbox の v-model にバインドされた値は事前に存在している必要があります。そうでない場合は、長さ属性が見つからないというエラーが報告されます。
そこで事前にdataで配列を宣言しておき、v-modelはこの配列をバインドし、フォームを親コンポーネントに返すときはフォームオブジェクトに追加し、フォームをリセットするときはこの配列を空にすることでフォームリセット機能を実現します!
- リセットが失敗する理由は次のとおりである可能性があります: (1) プロップが追加されていない (2) プロップによってバインドされた値が、モデルによってバインドされたオブジェクトに対応するプロパティと一致しない
2.2. 使用方法
重点分野:
(1) formItemList のタイプによって、入力ボックス、ドロップダウン オプションなど、生成されるフォーム アイテムのタイプが決まります。
(2) formItemList のモデルは、フォーム項目の双方向バインディングの名前を示し、また、子コンポーネントによって親コンポーネントのオブジェクトに返されるオブジェクトの属性名でもあります。
(3) formItemListのオプションは、ドロップダウンオプション、チェックボックス、ラジオボタンのオプション値を表します。
(4) @getFormでバインドされたイベントは、子コンポーネントから親コンポーネントに返される検証済みのフォームを取得できます 通常、子コンポーネントはネットワークリクエストを行わず、親コンポーネントがネットワークリクエストを行います。
(5) 親コンポーネントは$refsを通じてコンポーネント インスタンスを取得し、子コンポーネントを呼び出してフォームをリセットし、フォームを送信します。
<template>
<div id="app">
<CommonForm ref="form" :form="form" :rules="rules" :formItemList="formItemList" @getForm="getForm">
</CommonForm>
<el-row>
<el-button type="primary" @click="submitForm">提交</el-button>
<el-button @click="resetForm">重置</el-button>
</el-row>
</div>
</template>
<script>
import CommonForm from '@/components/commonForm'
export default {
name: 'App',
components: {
CommonForm,
},
data() {
return {
form: {},
editForm: {
name: '张三',
region: 'shanghai',
delivery: true,
resource: '线上品牌商赞助',
startTime: [
'2023-05-10',
'2023-05-11'
],
types: '线下主题活动',
},
formItemList: [
{ label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
{ label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
{ label: '活动时间', type: 'date-picker', model: 'startTime', },
{ label: '即时配送', type: 'switch', model: 'delivery' },
{ label: '活动性质', type: 'checkbox', model: 'type', modelString: 'types', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
{ label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
],
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
}
}
},
created() {
// 表单初始化
this.formInit()
},
methods: {
// 表单初始化
formInit() {
this.form = JSON.stringify(this.editForm) ? this.editForm : {}
// checkbox类型的组件需要初始化数组
this.formItemList.forEach(item => {
if (['checkbox'].includes(item.type)) {
if (!this.form[item.modelString]) {
this.$set(this.form, item.model, [])
} else {
this.$set(this.form, item.model, this.form[item.modelString]?.split(','))
}
}
})
},
// 接受子组件传回来的form表单内容
getForm(val) {
this.formItemList.forEach(item => {
//
if (['checkbox'].includes(item.type)) {
if (this.form[item.model] && this.form[item.model]?.length != 0) {
val[item.modelString] = val[item.model]?.join(',')
}
}
})
console.log('val:', val);
// 此处可以请求后台了
},
// 点击提交,触发表单校验
submitForm() {
this.$refs.form.returnForm();
},
// 重置表单
resetForm() {
this.$refs.form.resetForm();
}
}
}
</script>
<style>
#app {
display: flex;
flex-direction: column;
align-items: center;
color: #2c3e50;
}
</style>
2.3. エコーデータ
オブジェクトを用意する: editForm はフォームに既にデータがあることをシミュレートし、 editFormが空でないかを判断し、値があればformに代入し、値がなければ{}を代入します。
this.form = JSON.stringify(this.editForm) ? this.editForm : {}
elementUIのチェックボックスのv-modelによってバインドされる値は実際の配列である必要があると 述べましたが、チェックボックスを処理するためにフォーム コンポーネントで追加の変数がインスタンス化される場合、それはそれほどインテリジェントではありません。
上記のエラーを報告せずに {} オブジェクトをフォーム コンポーネントに渡す方法。また、データをエコーすることもできます。これには、フォームコンポーネントを参照するコンポーネント内のオブジェクトを初期化する必要があります。
(1) elementuiのチェックボックス、Cascader カスケード セレクター、日付コンポーネントなどのV モデルは配列にバインドされており、場合によってはバックエンド インターフェイス ドキュメントが配列ではないため、手動で処理する必要があります。例:
フロントエンドは、選択された複数の値 (これらの値は配列内に存在します) を文字列形式で使用し、内部の値はカンマで区切られます。次に例を示します。
[1, 2, 3, 4] => "1,2,3,4"なので、これをバックエンドに渡すと、バックエンドは"1,2,3,4"を返し、配列に変換するように求めます。 [1,2 ,3,4] はelementui コンポーネントにレンダリングされます。
split(',') : 文字列内のカンマ区切りの要素を配列[1, 2, 3, 4] => "1,2,3,4" に変換します。
join(',') : 配列要素をカンマ区切りの文字列に変換します。 "1,2,3,4" => [1, 2, 3, 4]
$set(array/object, property/index, specific value) : Object.defineProperty は、 vue2で応答性の高い変更を監視するために使用されます。配列とオブジェクトの監視はそれほどフレンドリーではありません。多くの場合、データは変更されますが、ビューは変更されません。$ set が問題を解決するように見えました
// 表单初始化
formInit() {
this.form = JSON.stringify(this.editForm) ? this.editForm : {}
// checkbox类型的组件需要初始化数组
this.formItemList.forEach(item => {
if (['checkbox'].includes(item.type)) {
if (!this.form[item.modelString]) {
this.$set(this.form, item.model, [])
} else {
this.$set(this.form, item.model, this.form[item.modelString]?.split(','))
}
}
})
}
// 接受子组件传回来的form表单内容
getForm(val) {
this.formItemList.forEach(item => {
//
if (['checkbox'].includes(item.type)) {
if (this.form[item.model] && this.form[item.model]?.length != 0) {
val[item.modelString] = val[item.model]?.join(',')
}
}
})
console.log('val:', val);
// 此处可以请求后台了
},
3. JSX で el-form をカプセル化する方法
jsx カプセル化メソッドは、次の記事から借用しています: element-ui 一般形式のカプセル化と VUE JSX アプリケーション - Nuggets
3.1. カプセル化
(1) 前回のブログでは jsx を簡単に紹介しました: el-table_code を再パッケージする 2 つの方法 プログラミング ブログ - CSDN ブログ
(2) 新しいjsxFormフォルダーを作成し、index.jsファイルを作成します
(3) パッケージコード:
コードのロジック図は次のとおりです。
export default {
name: 'jsxForm',
props: {
// 生成el-form-item的数组
formItemList: {
type: Array,
default: () => [],
},
// 是否行内表单模式
inline: {
type: Boolean,
default: false,
},
// el-form-item的label宽度
labelWidth: {
type: String,
default: '100px',
},
// 表单校验规则
rules: {
type: Object,
default: () => { },
},
// 按钮列表
buttonList: {
type: Array,
default: () => [],
}
},
data() {
return {
form: {},
checkboxList: []
}
},
methods: {
// 生成选项
generateOption(itemObj) {
let options = []
for (let index = 0; index < itemObj.options.length; index++) {
const item = itemObj.options[index]
switch (itemObj.type) {
// 下拉菜单
case 'select':
options.push(<el-option label={item.label} value={item.value}></el-option>)
break
// 多选框
case 'checkbox':
options.push(<el-checkbox label={item.label}></el-checkbox>)
break
// 单选框
case 'radio':
options.push(<el-radio label={item.label}>{item.label}</el-radio>)
break
}
}
return options
},
// 生成下拉菜单
generateSelect(item) {
return <el-select v-model={this.form[item.model]}>{this.generateOption(item)}</el-select>
},
// 生成多选框
generateCheckbox(item) {
this.form[item.model] = this.checkboxList
return <el-checkbox-group v-model={this.checkboxList}>{this.generateOption(item)}</el-checkbox-group>
},
// 生成单选
generateRadio(item) {
return <div>
<el-radio-group v-model={this.form[item.model]}>{this.generateOption(item)}</el-radio-group>
</div>
},
// 生成输入框
generateInput(item) {
return (<div>
<el-input v-model={this.form[item.model]} style={
{ width: `${this.formItemContentWidth}` }}></el-input>
</div>)
},
// 生成开关
generateSwitch(item) {
return <div>
<el-switch v-model={this.form[item.model]}></el-switch>
</div>
},
// 生成日期组件
generateDate(item) {
return <div>
<el-date-picker v-model={this.form[item.model]} type={"daterange"} range-separator={"至"}
start-placeholder={"开始日期"} end-placeholder={"结束日期"}>
</el-date-picker>
</div >
},
// 生成表单项
generateFormItems(list = []) {
let formItems = []
list.forEach(item => {
let formItemContent = ''
switch (item.type) {
// 下拉菜单
case 'select':
formItemContent = this.generateSelect(item)
break
// 单选框
case 'radio':
formItemContent = this.generateRadio(item)
break
// 输入框
case 'input':
formItemContent = this.generateInput(item)
break;
// 开关
case 'switch':
formItemContent = this.generateSwitch(item)
break;
// 日期
case 'date-picker':
formItemContent = this.generateDate(item)
break;
// 复选框
case 'checkbox':
formItemContent = this.generateCheckbox(item)
break;
default:
break
}
formItems.push(<el-form-item label={item.label} prop={item.model}>{formItemContent}</el-form-item>)
})
return formItems
},
// 按钮列表
generateBtnList() {
let buttons = []
this.buttonList?.forEach(item => {
buttons.push(<el-button type={item.type} onClick={() => item.event()}>{item.text}</el-button>)
})
return buttons
},
// 重置表单
resetForm() {
// 重置复选框
this.checkboxList = []
this.$refs.form.resetFields();
},
// 返回经过校验的表单
returnForm() {
this.$refs['form'].validate((valid) => {
if (valid) {
this.$emit('submitForm', this.form)
}
})
}
},
render() {
return (
<div>
<el-form ref="form" props={
{ model: this.form }} rules={this.rules} inline={this.inline} label-width={this.labelWidth || '150px'}>
{this.generateFormItems(this.formItemList)}
<el-form-item>
{this.generateBtnList()}
</el-form-item>
</el-form>
</div>
)
}
}
3.2. 使用方法
<template>
<div id="app">
<jsxForm ref="form" :buttonList="buttonList" :rules="rules" :formItemList="formItemList" @submitForm="getForm">
</jsxForm>
</div>
</template>
<script>
import jsxForm from '@/components/jsxForm';
export default {
name: 'App',
components: {
JsxForm,
},
data() {
return {
formItemList: [
{ label: '活动名称', type: 'input', model: 'name', placeholder: '请输入活动名称' },
{ label: '活动区域', type: 'select', model: 'region', placeholder: '请选择状态', options: [{ label: '上海', value: 'shanghai' }, { label: '北京', value: 'beijing' }] },
{ label: '活动时间', type: 'date-picker', model: 'startTime', },
{ label: '即时配送', type: 'switch', model: 'delivery' },
{ label: '活动性质', type: 'checkbox', model: 'type', options: [{ label: '美食/餐厅线上活动' }, { label: '地推活动' }, { label: '线下主题活动' }, { label: '单纯品牌曝光' },] },
{ label: '特殊资源', type: 'radio', model: 'resource', options: [{ label: '线上品牌商赞助' }, { label: '线下场地免费' }] },
],
rules: {
name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
},
buttonList: [
{ text: '提交', type: 'primary', event: this.submitForm },
{ text: '重置', event: this.resetForm },
]
}
},
methods: {
// 接受子组件传回来的form表单内容
getForm(val) {
console.log('val:', val);
},
// 点击提交,触发表单校验
submitForm() {
this.$refs.form.returnForm();
},
// 重置表单
resetForm() {
this.$refs.form.resetForm();
}
}
}
</script>