element ui移动段 动态生成 form页面
最近做了很多这种类似的画面,一直在写重复代码,就在想怎么能复用逻辑。接下来就是实现一键配置生成移动端form画面的代码了。还可以使用el-form的rules校验
//basic.js 组件主要代码
export const ElementType = {
INPUT: 1, //输入框
SELECTOR: 2, //选择框
DATE: 3, //时间类型
DATETIME: 4 //时间日期类型
}
export const BasicInputs = {
PURCHASENO: () => ({
type: ElementType.INPUT,
name: 'purchaseno',
label: '采购单单号'
}),
MBLNR: () => ({
type: ElementType.INPUT,
name: 'mblnr',
label: '入库通知单'
}),
}
export const BasicSelecotrs = {
PARTSNO: () => ({
type: ElementType.SELECTOR,
name: 'partsno',
label: '产品编码',
searchCode: 't_material_info'
}),
PURCHASESTATUS: () => ({
type: ElementType.SELECTOR,
name: 'status',
label: '状态',
searchCode: 't_purchase_status_info'
}),
SUPPLIERCODE: () => ({
type: ElementType.SELECTOR,
name: 'suppliercode',
label: '供应商名称',
searchCode: 't_supplier'
}),
KIND: () => ({
type: ElementType.SELECTOR,
name: 'kind',
label: '采购单类型',
searchCode: 't_purchase_kind_info'
}),
SHIPPING: () => ({
type: ElementType.SELECTOR,
name: 'shippingcode',
label: '运输方式',
searchCode: 't_shipping_info'
}),
WAREHOUSE: () => ({
type: ElementType.SELECTOR,
name: 'warehousecode',
label: '到货仓库',
searchCode: 't_warehouse_info'
}),
CURRENCIE: () => ({
type: ElementType.SELECTOR,
name: 'currency',
label: '币种',
searchCode: 't_currency_info'
}),
COOPERATION: () => ({
type: ElementType.SELECTOR,
name: 'cooperationMode',
label: '合作方式',
searchCode: 't_basic_code_info',
query: 1252
}),
}
export const BasicDates = {
ARRIVEDDATE: () => ({
type: ElementType.DATE,
name: 'arriveddate',
label: '预计到货日期',
minDate: new Date(),
maxDate: new Date(new Date().getFullYear() + 1, 0, 1),
}),
ACTURALSTARTTIME: () => ({
type: ElementType.DATE,
name: 'acturalStartTime',
label: '预计入库时间',
minDate: new Date(),
maxDate: new Date(new Date().getFullYear() + 1, 0, 1),
}),
}
//用于给 BasicAttrs 添加某些属性
export class Attrs {
static addArrt(original, attr) {
return { ...original, ...attr }
}
static disabledArrt(original) {
if (original.type === ElementType.SELECTOR) {
original.searchCode = undefined;
}
original.disabled = true;
return original;
}
static createAttr(name, label, type) {
return {
name, label, type
}
}
static createInputAttr(name, label) {
return Attrs.createAttr(name, label, ElementType.INPUT);
}
/**
* 一般用于明明是相同的下拉框列表,但是后台接收的字段名称 不一致
* Attrs.changeAttrName(BasicSelecotrs.SUPPLIERCODE(), 'lifnr') 原本叫suppliercode但是有的地方后台接收lifnr。
* @param {*} original
* @param {*} newName
* @returns
*/
static changeAttrName(original, newName) {
original.name = newName;
return original;
}
}
//组件代码
<template>
<!-- 根据配置自动生成form页 -->
<div class="basic-form">
<el-form
ref="basicForm"
:model="info"
:rules="rules"
class="form-content"
label-position="left"
>
<el-form-item
v-for="(part, index) in config"
:prop="getProp(part)"
:label="part.label"
:key="index"
>
<el-input
v-if="ElementType.INPUT === part.type"
:disabled="part.disabled"
v-model="info[part.name]"
type="text"
:name="part.name"
/>
<div
v-if="ElementType.SELECTOR === part.type"
@click="openSelector(part.name)"
>
<el-input
:value="info[part.showName]"
class="eli-select-input"
type="text"
:name="part.showName"
:placeholder="getSelectorTip(part.label)"
/>
<el-button type="text" icon="el-icon-arrow-right" />
</div>
<div
v-if="ElementType.DATE === part.type"
@click="openDatePicker(part.name)"
>
<el-input
:value="info[part.name]"
class="eli-select-input"
type="text"
:name="part.name"
:placeholder="getSelectorTip(part.label)"
/>
<el-button type="text" icon="el-icon-arrow-right" />
</div>
</el-form-item>
<slot></slot>
</el-form>
<van-calendar
v-model="showDatePicker"
:min-date="curDateInfo.minDate"
:max-date="curDateInfo.maxDate"
@confirm="onDatePickerConfirm"
/>
<van-popup v-model="showPicker" position="bottom">
<van-picker
:title="curOptionsInfo.title"
show-toolbar
:default-index="curOptionsInfo.index"
:columns="curOptionsInfo.columns"
@confirm="onPickerConfirm"
@cancel="showPicker = false"
/>
</van-popup>
</div>
</template>
<script>
import { Popup, Picker, Calendar } from "vant";
import { ElementType } from "./utils/basic.js";
import { getDropDownOptions } from "@/api/purchase/purchaseOrder.js";
export default {
props: {
config: {
type: Array,
required: true
},
rules: {
type: Object,
required: false
}
},
components: {
[Popup.name]: Popup,
[Picker.name]: Picker,
[Calendar.name]: Calendar
},
data() {
return {
showPicker: false,
showDatePicker: false,
info: {},
curDateInfo: {
name: "",
minDate: new Date(),
maxDate: new Date()
},
curOptionsInfo: {
//当前选中的options 信息
name: "",
showName: "",
title: "",
index: 0,
columns: [],
entry: {}
},
ElementType: ElementType
};
},
created() {
this.init();
},
methods: {
init() {
try {
console.log('children node');
console.log(this.config);
for (let i = 0; i < this.config.length; i++) {
const v = this.config[i];
this.$set(this.info, v.name, v.value);
if (v.type === ElementType.SELECTOR && v.searchCode !== undefined) {
v.showName = v.name + "Name";
getDropDownOptions(v.searchCode, v.query).then(options => {
v.options = options;
v.columns = v.options.map(v => v.name);
this.$set(
this.info,
v.showName,
this.getSelectorName(v.options, v.value)
);
});
}
}
} catch (err) {
console.log(err);
}
},
getSelectorName(options, value) {
if (!value || options === undefined || options.length === 0) {
return undefined;
}
return options.find(v => v.code === value).name;
},
getSelectorTip(label) {
return "请选择" + label;
},
getProp(part) {
if (part.type === ElementType.SELECTOR) {
return part.showName;
}
return part.name;
},
onPickerConfirm(val) {
this.showPicker = false;
if (val === undefined) {
return;
}
const key = this.curOptionsInfo.name;
const name = this.curOptionsInfo.showName;
this.info[name] = val;
this.info[key] = this.curOptionsInfo.entry[val].code;
},
openSelector(attrName) {
this.showPicker = true;
this.setCurOptionAttrKey(attrName);
const curName = this.info[this.curOptionsInfo.showName];
if (curName) {
this.curOptionsInfo.index = this.curOptionsInfo.entry[curName].index;
} else {
this.curOptionsInfo.index = 0;
}
},
openDatePicker(attrName) {
this.showDatePicker = true;
const part = this.config.find(v => v.name === attrName);
this.curDateInfo.name = attrName;
if (part.minDate) {
this.curDateInfo.minDate = part.minDate;
}
if (part.maxDate) {
this.curDateInfo.maxDate = part.maxDate;
}
},
onDatePickerConfirm(date) {
this.showDatePicker = false;
this.info[this.curDateInfo.name] = this.DateUtils.formatter(
date,
"yyyy-MM-dd"
);
},
setCurOptionAttrKey(attrName) {
const part = this.config.find(v => v.name === attrName);
this.curOptionsInfo.title = part.label;
this.curOptionsInfo.name = part.name;
this.curOptionsInfo.showName = part.showName;
this.curOptionsInfo.columns = part.columns;
part.options.forEach(
(v, i) =>
(this.curOptionsInfo.entry[v.name] = { code: v.code, index: i })
);
},
reset() {
Object.keys(this.info).forEach(key => {
this.info[key] = undefined;
});
},
validate() {
return new Promise((resolve, reject) => {
this.$refs.basicForm.validate(valid => {
if (valid) {
resolve();
} else {
reject();
}
});
});
},
getFormData() {
const res = {};
this.config.forEach(item => {
res[item.name] = this.info[item.name];
});
return res;
}
}
};
</script>
.van-cell__label {
width: 200%;
}
.form-content .el-form-item {
color: #454545;
display: flex;
margin: 14px 12px;
border-bottom: 1px solid #edeff6;
white-space: nowrap;
}
.form-content .el-input__inner {
border: 0px;
background-color: #fff !important;
}
.form-content .el-form-item__label {
width: 32%;
}
.form-content .el-form-item__content {
flex: 1;
}
.edit-content {
height: 100%;
}
.form-content .el-select,
.form-content .el-select--mini {
width: 100%;
}
.eli-select-input {
width: 92%;
pointer-events: none;
}
.form-content .el-select-dropdown__item {
text-align: center;
}
.form-file .el-form-item__content {
display: flex;
justify-content: flex-end;
}
.operator-group-box {
border-bottom: none !important;
}
.operator-group {
display: flex;
margin-bottom: 8px;
}
.operator-group>button {
flex: 1;
padding: 12px 0;
}
在父组件中
<basic-form v-if="showForm" ref="form" :rules="rules" :config="config">
<el-form-item>
<el-button type="default" size="mini" icon="el-icon-plus" @click="addProduct">添加产品</el-button>
<purchase-product-info :productList="productList" @showProduct="showProduct" />
</el-form-item>
<el-form-item>
<el-button
style="width: 100%;margin-bottom: 12px;"
type="success"
icon="el-icon-check"
:loading="submitLoading"
@click="save"
>提交</el-button
>
</el-form-item>
</basic-form>
config: [
Attrs.disabledArrt(BasicInputs.PURCHASENO()),
BasicDates.ARRIVEDDATE(),
BasicSelecotrs.KIND(),
BasicSelecotrs.SUPPLIERCODE(),
Attrs.createInputAttr('total', '采购单价'),
BasicSelecotrs.SHIPPING(),
Attrs.createInputAttr('deliveryform', '交货方式'),
Attrs.createInputAttr('paymentcondition', '付款条件'),
BasicSelecotrs.WAREHOUSE(),
BasicSelecotrs.CURRENCIE(),
BasicSelecotrs.COOPERATION(),
Attrs.createInputAttr('memo', '备注'),
],