element-ui 表单TS封装table-form -戴向天

大家好!我叫戴向天

QQ群:602504799

如若有不理解的,可加QQ群进行咨询了解

<template>
  <div :class="getClassName()" class="flex-sw-22">
    <el-form
      :label-width="labelWidth"
      :model="params"
      ref="form"
      :rules="rules"
    >
      <div class="flex" v-for="(row, key) in forms" :key="key">
        <el-form-item
          :class="`flex-sw-${24 / row.length}`"
          v-for="(single, s) in row"
          :key="s"
          :label="single.label"
          v-show="!single.hide"
          :prop="single.prop"
        >
          <template v-if="single.type === 'select'">
            <div :class="getClassName('select')">
              <el-select
                v-bind="single.bind"
                v-model="params[single.prop]"
                @change="changeHandler(single)"
              >
                <el-option
                  v-for="(option, o) in single.options || []"
                  :key="o"
                  :label="option.label"
                  :value="option.value"
                ></el-option>
              </el-select>
              <el-button
                @click="buttonHandler(single)"
                v-if="'button' in single"
                size="mini"
                v-bind="single.button.bind || {}"
                >{
   
   { single.button.name }}</el-button
              >
            </div>
          </template>
          <template v-else-if="single.type === 'switch'">
            <el-switch
              v-model="params[single.prop]"
              @change="changeHandler(single)"
              v-bind="single.bind"
            >
            </el-switch>
          </template>

          <template v-else-if="single.type === 'checkbox'">
            <el-checkbox-group
              v-model="params[single.prop]"
              @change="changeHandler(single)"
            >
              <el-checkbox
                v-for="(option, o) in single.options"
                :key="o"
                v-bind="option.bind"
              ></el-checkbox>
            </el-checkbox-group>
          </template>

          <template v-else-if="single.type === 'radio'">
            <el-radio-group
              v-model="params[single.prop]"
              @change="changeHandler(single)"
              v-bind="single.bind"
            >
              <el-radio
                v-for="(option, o) in single.options"
                :key="o"
                :label="option.value"
                >{
   
   { option.label }}</el-radio
              >
            </el-radio-group>
            <span
              class="tips"
              v-if="getOptionTips(params[single.prop], single.options)"
              v-html="getOptionTips(params[single.prop], single.options)"
            ></span>
          </template>

          <template v-else-if="single.type === 'date'">
            <el-date-picker
              @change="changeHandler(single)"
              v-model="params[single.prop]"
              v-bind="single.bind"
            >
            </el-date-picker>
          </template>
          <template v-else-if="single.type === 'download'">
            <a
              :class="getClassName('download', ['a'])"
              @click="downloadFile(single.bind.href)"
              :download="single.bind.text"
              >{
   
   { single.bind.text }}</a
            >
          </template>
          <template v-else-if="single.type === 'downloads'">
            <div v-for="(file, f) in params[single.prop] || []" :key="f">
              <a
                :class="getClassName('download', ['a'])"
                :href="getUrl(file.href)"
                :download="file.name"
                >{
   
   { file.name }}</a
              >
            </div>
          </template>
          <template v-else-if="single.type === 'upload'">
            <ul :class="getClassName('file', ['list'])">
              <li v-for="(file, f) in params[single.prop] || []" :key="f">
                {
   
   { file.name
                }}<i class="el-icon-close" @click="deleteFile(single, f)"></i>
              </li>
            </ul>
            <div class="flex-center-y">
              <template
                v-if="
                  (params[single.prop] || []).length > 0
                    ? (single.bind || {}).max
                      ? single.bind.max > (params[single.prop] || []).length
                      : single.bind.multiple
                    : true
                "
              >
                <UploadButton
                  v-bind="single.bind"
                  v-model="params[single.prop]"
                ></UploadButton>
                <div class="tips" v-if="single.tips" v-html="single.tips"></div>
              </template>
            </div>
          </template>

          <template v-else-if="single.type === 'tips'">
            <div v-bind="single.bind" v-html="single.html"></div>
          </template>

          <template v-else>
            <el-input v-bind="single.bind" v-model.trim="params[single.prop]">
              <el-button
                slot="append"
                @click="buttonHandler(single)"
                v-bind="single.button.bind || {}"
                v-if="single.button"
                >{
   
   { single.button.name }}</el-button
              >
            </el-input>
          </template>
        </el-form-item>
      </div>
    </el-form>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import UploadButton from "@/components/common/form/upload-button.vue";
import {
  ITableForm,
  ITableFormrSingle,
  ISelectRequestOption,
  IFillValue,
  IFillValueOptions,
  IEvent,
  IOption,
} from "@/lib/interface/component/tableForm";
import downloadByUrl from "@/lib/methods/downloadByUrl";

@Component({
  name: "TableForm",
  components: { UploadButton },
})
export default class TableForm extends Vue {
  @Prop({
    default: "3rem",
  })
  private labelWidth?: string;

  @Prop({
    default: () => [],
  })
  private config: ITableForm;

  private forms: ITableForm = [];

  private params: any = {};

  private rules: any = {};

  @Watch("config", {
    deep: true,
  })
  private configWatch() {
    this.params = this.createParams(this.config, this.params);
    this.forms = this.createForms(this.config);
    // this.rules = {};
    // this.$nextTick(() => {
    //   this.rules = this.createRule(this.config, this.params);
    // });
  }

  private get getOptionTips() {
    return (val: string, options: IOption[]) => {
      const option = options.find((item: IOption) => item.value === val);
      if (option && option.tips) {
        return option.tips;
      } else {
        return null;
      }
    };
  }

  private createForms(config: ITableForm) {
    return JSON.parse(JSON.stringify(config)).map(
      (row: ITableFormrSingle[]) => {
        return row.map((single: ITableFormrSingle) => {
          delete single.follow;
          single.value = "";
          return single;
        });
      }
    );
  }

  private get getUrl() {
    return (url: string = "") => {
      return url
        .split("/")
        .map((str) => (/[\u4e00-\u9fa5]+/g.test(str) ? encodeURI(str) : str))
        .join("/");
    };
  }

  /**
   * 输入框的按钮点击处理
   */
  private buttonHandler(item: ITableFormrSingle) {
    if (item.button) {
      this.$emit("eventHandler", item.button.emit, item);
      this.$emit("buttonHandler", item.button.emit, item);
    }
  }

  /**
   * 创建出一级数组
   */
  private toSingleArray(forms: ITableForm) {
    return forms.reduce(
      (frist: ITableFormrSingle[], next: ITableFormrSingle[]) =>
        frist.concat(next),
      []
    );
  }

  /**
   * 创建参数信息
   */
  private createParams(forms: ITableForm, originData: any) {
    const arr = this.toSingleArray(forms);
    return arr.reduce((frist: any, next: ITableFormrSingle) => {
      frist[next.prop] = next.follow ? next.value : originData[next.prop];

      return frist;
    }, {});
  }

  /**
   * 创建效验规则
   */
  private createRule(forms: ITableForm, params: any = {}) {
    const arr = this.toSingleArray(forms)
      .filter(
        (item: ITableFormrSingle) =>
          item.required ||
          (item.rules && Array.isArray(item.rules) && item.rules.length > 0)
      )
      .map((item: ITableFormrSingle) => ({
        prop: item.prop,
        message: (item.bind || {}).placeholder,
        type: item.type,
        rules: item.rules || [],
        required: item.required || false,
      }));
    const rules = Object.keys(params).reduce((frist: any, next: string) => {
      const single = arr.find((item: any) => item.prop === next);
      frist[next] = [
        {
          required: single ? single.required : false,
          trigger: "blur",
        },
      ];

      return frist;
    }, {});

    return Object.keys(rules).reduce((frist: any, next: string) => {
      const single = arr.find((item: any) => item.prop === next);

      if (single) {
        if (single.type === "selecet") {
          rules[next].push({
            required: true,
            trigger: "change",
          });
        }
        rules[next] = single.rules.concat(
          rules[next].map((rule: any) => {
            return {
              ...rule,
              message: single.message,
            };
          })
        );

        frist[next] = rules[next];
      }

      return frist;
    }, {});
  }

  private downloadFile(url: string) {
    downloadByUrl(url);
  }

  /**
   * 获取参数
   * @bool 是获取的时候进行效验
   */
  private async getParams(bool?: boolean) {
    if (bool) {
      const res = await this.validate();
      if (!res) {
        return null;
      }
    }
    return Object.keys(this.params).reduce((frist: any, key: string) => {
      frist[key] = this.params[key] === undefined ? "" : this.params[key];
      return frist;
    }, {});
  }

  /**
   * 通过prop字段获取options
   */
  private getOptionsByProp(prop: string) {
    if (prop) {
      const single = this.toSingleArray(this.forms).find(
        (item: ITableFormrSingle) => item.prop === prop
      );
      if (single) {
        return single.options;
      } else {
        return [];
      }
    } else {
      return [];
    }
  }

  /**
   * 获取select的选项
   */
  private async getOptions(forms: ITableForm): Promise<ISelectRequestOption[]> {
    const arr = this.toSingleArray(forms).filter(
      (item: ITableFormrSingle) => item.type === "select" && item.source
    );

    const promiesArr = arr.map((item: ITableFormrSingle) => {
      return new Promise((r, j) => {
        if (item.source) {
          const { server, mehotd, params, callBack } = item.source;
          server[mehotd](params)
            .then((res: any) => {
              const data = callBack(res);
              r({
                prop: item.prop,
                data,
              });
            })
            .catch((error: string) => {
              console.warn(error);
              j(false);
            });
        }
      });
    });
    const resuleArray = await Promise.all(promiesArr);
    return resuleArray as ISelectRequestOption[];
  }

  private fullOption(optionsArray: ISelectRequestOption[], forms: ITableForm) {
    return forms.map((row: ITableFormrSingle[]) => {
      return row.map((single: ITableFormrSingle) => {
        const option = optionsArray.find(
          (options: ISelectRequestOption) => options.prop === single.prop
        );
        if (option) {
          delete single.follow;
          single.value = "";
          single.options = option.data;
        }
        return single;
      });
    });
  }
  // 填充数据
  private fillValue(data: IFillValue[]) {
    this.forms = JSON.parse(JSON.stringify(this.forms)).map(
      (row: ITableFormrSingle[]) => {
        return row.map((single: ITableFormrSingle) => {
          const item = data.find(
            (option: IFillValueOptions) => option.prop === single.prop
          );
          if (item) {
            single = {
              ...single,
              follow: "follow" in item ? (item as any).follow : true,
              ...item,
              bind: {
                ...single.bind,
                ...item.bind,
              },
            };
          }
          return single;
        });
      }
    );

    const values = JSON.parse(JSON.stringify(data)).reduce(
      (frist: any, next: IFillValue) => {
        if (next.value !== this.params[next.prop]) {
          frist[next.prop] = next.value;
        }
        return frist;
      },
      {}
    );
    this.params = this.createParams(this.forms, this.params);
    this.autoEventHandler(values, this.forms, this.params);

    this.forms = this.createForms(this.forms);
  }

  // change事件报出
  private changeHandler(single: ITableFormrSingle) {
    if (single.event && single.event.length) {
      const eventInfo = single.event.find(
        (event: IEvent) => event.trigger === "change"
      );
      if (eventInfo) {
        if (eventInfo.emit) {
          this.$emit("eventHandler", eventInfo.emit, {
            ...single,
            value: this.params[single.prop],
          });
        }
      }
    }
  }

  private async validate() {
    return new Promise((r, j) => {
      (this.$refs.form as any).validate((bool: boolean) => {
        r(bool);
      });
    });
  }

  // 初始化的时候事件调用
  private autoEventHandler(params: any, forms: ITableForm, oldParams: any) {
    const singleArray = this.toSingleArray(forms);
    Object.keys(JSON.parse(JSON.stringify(params))).forEach((key: string) => {
      const item = singleArray.find(
        (single: ITableFormrSingle) => single.prop === key
      );
      if (item && item.event) {
        item.event.forEach((event: IEvent) => {
          if (event.trigger === "change") {
            this.changeHandler(item);
          }
        });
      }
    });
  }

  private async refreshSource(prop: string) {
    const singleArray: ITableFormrSingle[] = this.toSingleArray(this.config);
    const single = singleArray.find(
      (item: any) => item.prop === prop && item.type === "select"
    );
    if (single && single.source) {
      const { server, mehotd, params, callBack } = single.source;
      const res = await server[mehotd](params);
      const data = callBack(res) as IOption[];
      const result: ISelectRequestOption[] = [
        {
          prop,
          data,
        },
      ];
      this.forms = this.fullOption(result, this.forms);
    } else {
      console.warn(`刷新source失败,未找到${prop}字段,或者没有source配置参数`);
    }
  }

  private deleteFile(item: ITableFormrSingle, index: number) {
    if (item.event) {
      const eventItem = item.event.find(
        (event: IEvent) => event.trigger === "delete"
      );
      if (eventItem) {
        this.$emit(
          "eventHandler",
          eventItem.emit,
          this.params[item.prop][index],
          (bool: boolean) => {
            if (bool) {
              console.log("确认删除");
              this.params[item.prop].splice(index, 1);
            } else {
              console.log("不删除");
            }
          }
        );
      } else {
        this.params[item.prop].splice(index, 1);
      }
    } else {
      this.params[item.prop].splice(index, 1);
    }
  }

  private async created() {
    this.params = this.createParams(this.config, this.params);
    this.forms = this.createForms(this.config);
    this.rules = this.createRule(this.config, this.params);
    const optionsArray = await this.getOptions(this.config);
    this.forms = this.fullOption(optionsArray, this.forms);
    this.autoEventHandler(this.params, this.forms, {});
  }
}
</script>

<style lang="less" scoped>
.tableForm {
  // padding-top: unit(30 / @fontSize, rem);
  &-select {
    display: flex;
    border: unit(1 / @fontSize, rem) solid #dcdfe6;
    border-radius: unit(4 / @fontSize, rem);
    overflow: hidden;
    /deep/ & > .el-select {
      width: 100%;
      .el-input__inner {
        border: none;
      }
    }
    /deep/& > .el-button {
      border: none;
      border-left: unit(1 / @fontSize, rem) solid #dcdfe6;
      border-radius: 0;
    }
  }
  &-download {
    &__a {
      color: @primary;
      text-decoration: none;
      cursor: pointer;
      font-size: unit(18 / @fontSize, rem);
      &:hover {
        text-decoration: underline;
      }
    }
  }
  &-file {
    &__list {
      min-height: unit(7 / @fontSize, rem);
    }
  }
  .tips {
    color: #999;
    margin-left: unit(30 / @fontSize, rem);
    line-height: unit(20 / @fontSize, rem);
  }
  /deep/ .el-form-item.is-error {
    .tableForm-select {
      border-color: #f56c6c;
    }
  }
  /deep/.el-date-editor {
    width: 100% !important;
  }
}
</style>

猜你喜欢

转载自blog.csdn.net/weixin_41088946/article/details/114404401