vant2+vue2组件封装

175 阅读1分钟

编辑性表单

  • 封装EditForm
<!-- eslint-disable vue/no-mutating-props -->
<!-- eslint-disable vue/valid-v-for -->
<template>
  <van-form
    @submit="onSubmit"
    validate-first
    scroll-to-error
    colon
    v-bind="$attrs"
  >
    <template v-for="item in formItemList">
      <slot :name="item.slotName" v-if="item.slotName"></slot>
      <van-field
        v-else
        v-model="formData[item.prop]"
        :error="false"
        :disabled="$attrs.disabled || item.disabled"
        :name="item.prop"
        :label="item.label"
        :type="item.type"
        :readonly="item.readonly"
        :clickable="item.clickable"
        @click="
          () => {
            !$attrs.disabled &&
              !item.disabled &&
              item.clickable &&
              ((item.onClick && item.onClick()) ||
                (!item.onClick &&
                  showPopupPicker(
                    {
                      textKey: item.prop,
                      valueKey: item.valueKey,
                      dateType: item.dateType,
                    },
                    item.columns
                  )));
          }
        "
        :placeholder="
          item.placeholder
            ? item.placeholder
            : `${item.clickable ? '请选择' : '请输入'}${item.label}`
        "
        :rules="item.rules"
      >
        <template
          #extra
          v-if="item.extraText || item.extraIcon || item.clickable"
        >
          <span v-if="item.extraText">{{ item.extraText }}</span>
          <van-icon :name="item.extraIcon || 'arrow'" v-else />
        </template>
      </van-field>
    </template>

    <slot name="submitBtn">
      <div style="margin: 16px">
        <!-- TODO:   native-type="button" 快速-->
        <!-- <van-button round block type="info" native-type="submit">提交</van-button> -->
        <van-button
          round
          block
          type="info"
          native-type="button"
          @click="onGetForm"
          >提交</van-button
        >
      </div>
    </slot>

    <van-popup v-model="showPicker" position="bottom">
      <van-datetime-picker
        v-if="['date'].includes(activePickerInfo.dateType)"
        v-model="currentDate"
        :type="activePickerInfo.dateType"
        title="选择年月日"
        @confirm="onDatePickerConfirm"
        @cancel="showPicker = false"
      />

      <van-picker
        v-else
        show-toolbar
        :columns="columns"
        @confirm="onColumnPickerConfirm"
        :default-index="selectedIndex"
        @cancel="showPicker = false"
      />
    </van-popup>
  </van-form>
</template>
<script>
import moment from "moment";

export default {
  name: "EditForm",
  components: {},
  props: {
    // 表单数组
    formItemList: {
      type: Array,
      default: () => [],
    },
    // 业务传入的表单对象
    formData: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      selectedIndex: 0,
      showPicker: false,
      // picker的列选项 {text:"文案", value:"码值"}
      columns: [],
      // 日期对象
      currentDate: null,
      // 当前激活的picker textKey:显示key  valueKey:码值key  dateType:日期类型
      activePickerInfo: {
        textKey: "",
        valueKey: "",
        dateType: "",
      },
    };
  },
  computed: {},
  watch: {
    // formData: {
    //   //   deep: true,
    //   immediate: true,
    //   handler(val) {
    //     this.form = JSON.parse(JSON.stringify(val));
    //   },
    // },
  },
  created() {},
  methods: {
    // form校验通过后的回调
    onSubmit() {
      console.log("submit---passed");
      console.log("formData:", this.formData);
    },
    /**
     * 日期picker 点击确认的回调
     * @param  { Date } value:选中日期对象
     */
    onDatePickerConfirm(value) {
      console.log("value", value);
      // eslint-disable-next-line vue/no-mutating-props
      this.formData[this.activePickerInfo.textKey] =
        moment(value).format("YYYY-MM-DD");
      this.showPicker = false;
    },

    /**
     * 列类 点击确认的回调
     * @param  { Object } selectedItem: {text:"选中的文案", value:"选中的码值"}
     * @param  { Number } index: 选中的索引
     */
    onColumnPickerConfirm(selectedItem, index) {
      console.log(
        "onColumnPickerConfirm",
        selectedItem,
        index,
        this.activePickerInfo
      );
      // 展示用
      // eslint-disable-next-line vue/no-mutating-props
      this.formData[this.activePickerInfo.textKey] = selectedItem?.text;
      // 接口用
      // eslint-disable-next-line vue/no-mutating-props
      this.formData[this.activePickerInfo.valueKey] = selectedItem?.value;
      this.showPicker = false;
    },
    /**
     * picker 的展示
     * @param  { Object } activePickerInfo:{ textKey: "显示的文案key", valueKey: "码值key", dateType: "日期类type" }
     * @param  { Object } columns: [{text:"文案", value:"码值"}]
     */
    showPopupPicker(
      activePickerInfo = { textKey: "", valueKey: "", dateType: "" },
      columns
    ) {
      console.log("showPopupPicker", activePickerInfo);

      this.activePickerInfo.dateType = activePickerInfo?.dateType;
      this.activePickerInfo.textKey = activePickerInfo?.textKey;

      if (activePickerInfo?.dateType) {
        this.showDatePicker(activePickerInfo);
      } else {
        this.showColumnsPicker(activePickerInfo, columns);
      }
    },
    /**
     * 日期类picker 的展示
     */
    showDatePicker() {
      if (this.formData[this.activePickerInfo.textKey]) {
        // iOS 不支持以中划线分隔的日期格式,正确写法是new Date('2020/01/01')。
        this.currentDate = new Date(
          this.formData[this.activePickerInfo.textKey]
        );
      }
      this.showPicker = true;
    },
    /**
     * 列类picker 的展示
     * @param  { Object } activePickerInfo:{ textKey: "显示的文案key", valueKey: "码值key",  }
     */
    showColumnsPicker(
      activePickerInfo = { textKey: "", valueKey: "" },
      columns
    ) {
      console.log("showColumnsPicker", columns);
      this.activePickerInfo.dateType = "";
      this.activePickerInfo.valueKey = activePickerInfo?.valueKey;
      this.columns = columns;
      //  默认选择项
      const findIndex = this.columns.findIndex(
        (item) => item.value === this.formData[this.activePickerInfo.valueKey]
      );
      this.selectedIndex = findIndex < 0 ? 0 : findIndex;
      this.showPicker = true;
    },
    onGetForm() {
      console.log("onGetForm---form:", this.formData);
      localStorage.setItem("formData", JSON.stringify(this.formData));
    },
  },
};
</script>
  • 业务中使用
<template>
  <div>
    <EditForm
      :formData="formData"
      :formItemList="formItemList"
      ref="editFormRef"
    >
      <template #address>
        <van-field
          v-model="formData.address"
          :error="false"
          :disabled="false"
          name="address"
          label="地址"
          placeholder="请输入地址"
          :rules="[{ required: true, message: '请输入地址' }]"
        />
      </template>
      <template #addressInfo>
        <van-field
          v-model="formData.addressInfo"
          :error="false"
          :disabled="false"
          name="addressInfo"
          label="详细地址"
          placeholder="请输入详细地址"
          :rules="[{ required: true, message: '请输入详细地址' }]"
        />
      </template>
    </EditForm>
  </div>
</template>
<script>
import EditForm from "@/components/EditForm.vue";
export default {
  name: "App",
  components: {
    EditForm,
  },
  data() {
    return {
      formData: {
        address: "",
        addressInfo: "",
        username: "",
        phone: "",
        staffsNum: "",
        city: "",
        income: "",
        code: "",
        status: "",
      },
      formItemList: [
        {
          slotName: "address",
        },
        {
          prop: "username",
          label: "联系人姓名",
          rules: [{ required: true, message: "请输入联系人姓名" }],
        },
        {
          slotName: "addressInfo",
        },
        {
          prop: "phone",
          label: "电话号码",
          rules: [
            { required: true, message: "请输入电话号码" },
            { pattern: /^1[34578]\d{9}$/g, message: "请输入正确的电话号码" },
          ],
        },
        {
          prop: "staffsNum",
          label: "员工人数",
          type: "digit",
          extraText: "人",
          rules: [{ required: true, message: "请输入员工人数" }],
        },
        {
          prop: "income",
          label: "营业收入",
          type: "number",
          extraText: "元",
          rules: [{ required: true, message: "请输入营业收入" }],
        },
        {
          prop: "city",
          valueKey: "cityCode",
          label: "城市",
          readonly: true,
          clickable: true,
          showExtra: true,
          columns: [
            {
              text: "杭州",
              value: "1",
            },
            {
              text: "宁波",
              value: "2",
            },
            {
              text: "温州",
              value: "3",
            },
          ],
        },
        {
          prop: "code",
          label: "中征码",
          readonly: true,
        },
        {
          prop: "startDate",
          label: "起始日期",
          dateType: "date",
          readonly: true,
          clickable: true,
        },
        {
          prop: "endDate",
          label: "截止日期",
          dateType: "date",
          readonly: true,
          clickable: true,
        },
        {
          prop: "status",
          label: "存续状态",
          readonly: true,
          clickable: true,
          showExtra: true,
          onClick: () => {
            this.onshowStatusPicker();
          },
        },
      ],
    };
  },
  created() {
    this.formData = JSON.parse(localStorage.getItem("formData")) ?? {};
    setTimeout(() => {
      this.formData.code = "zh223";
    }, 2000);
  },
  methods: {
    async onshowStatusPicker() {
      console.log("onshowStatusPicker---start");
      setTimeout(() => {
        const mockDictColumns = [
          {
            text: "完成",
            value: "1",
          },
          {
            text: "继续",
            value: "2",
          },
          {
            text: "开始",
            value: "3",
          },
        ];
        this.$refs.editFormRef.showPopupPicker(
          { textKey: "status", valueKey: "statusCode" },
          mockDictColumns
        );
      }, 2000);
    },
  },
};
</script>

image.png

image.png