element ui移动段 动态生成 form页面

58 阅读3分钟

element ui移动段 动态生成 form页面

1720519001687.jpg

最近做了很多这种类似的画面,一直在写重复代码,就在想怎么能复用逻辑。接下来就是实现一键配置生成移动端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', '备注'),
        ],