基于element-ui簡(jiǎn)單封裝的form表單

<!--
Template Name: 公用表單
Create author: xingyuelognchen
Create Time  : 2020-03-31
-->
<template>
  <el-form
    ref="form"
    :model="fieldsData"
    :size="options.size"
    :inline="options.inline||false"
    @validate="validated"
  >
    <template v-for="(v,i) in mapFields">
      <el-row :key="i">
        <template v-for="(item,index) in v">
          <el-col :key="index" :span="item.span||24" :xs="item.xs||24">
            <el-form-item
              :label-width="item.labelWidth?item.labelWidth+'px' :'120px'"
              :label="item.label"
              :prop="item.prop"
              :rules="item.rule"
              :required="item.required"
            >
              <template v-if="['text','textarea','number','email'].indexOf(item.type)!==-1">
                <el-input
                  class="input"
                  v-model="fieldsData[item.prop]"
                  inline-message
                  :placeholder="item.placeholder"
                  :readonly="item.readonly"
                  :type="item.type"
                  @change="change(item,index,'change')"
                >
                  <span v-if="item.prepend" slot="prepend" @click.stop="click(item,index)">
                    <template v-if="/^(el-icon|my-icon).*/.test(item.prepend)">
                      <i :class="item.prepend"></i>
                    </template>
                    <template v-else>{{item.prepend}}</template>
                  </span>
                  <span v-if="item.append" slot="append" @click.stop="click(item,index)">
                    <template v-if="/^(el-icon|my-icon).*/.test(item.append)">
                      <i :class="item.append"></i>
                    </template>
                    <template v-else>{{item.append}}</template>
                  </span>
                </el-input>
              </template>
              <template v-if="item.type == 'select'">
                <el-select
                  v-if="!item.readonly"
                  v-model="fieldsData[item.prop]"
                  :disabled="item.disabled"
                  :multiple="item.multiple"
                  :collapse-tags="item.multiple"
                  @change="change(item,index,'change')"
                >
                  <el-option
                    v-for="(k,i) in item.options"
                    :key="i"
                    :disabled="k.disabled"
                    :value-id="k.id"
                    :label="k.label || k[item.config.label]"
                    :value="k.value || k[item.config.value] || k"
                  />
                </el-select>
                <el-input
                  v-else
                  v-model="fieldsData[item.prop]"
                  :disabled="item.disabled"
                  :readonly="item.readonly"
                ></el-input>
              </template>
              <template v-if="item.type == 'switch'">
                <el-switch v-model="fieldsData[item.prop]" :disabled="item.disabled" />
              </template>
              <template v-if="item.type == 'slider'">
                <el-slider
                  v-model="fieldsData[item.prop]"
                  :format-tooltip="item.formatTooltip"
                  :disabled="item.disabled"
                />
              </template>
              <template v-if="item.type == 'checkbox'">
                <template v-if="item.options && item.options.length">
                  <el-checkbox
                    v-model="fieldsData[item.prop]"
                    v-for="(k,i) in item.options"
                    :key="i"
                    :label="k.value ||k[item.config.value] || k.id || k.label"
                    :disabled="k.disabled"
                    :checked="k.checked"
                  >{{k.label || k[item.config.label] }}</el-checkbox>
                </template>
                <el-checkbox
                  v-else
                  v-model="fieldsData[item.prop]"
                  :label="item.options.value"
                  :disabled="item.disabled"
                  :checked="item.checked"
                >{{item.options.label}}</el-checkbox>
              </template>
              <template v-if="item.type == 'cascader'">
                <el-cascader
                  v-if="!item.readonly"
                  v-model="fieldsData[item.prop]"
                  :disabled="item.disabled"
                  :readonly="item.readonly"
                  :checkStrictly="true"
                  :emitPath="false"
                  :options="item.options"
                  :props="item.config || {label:'name',value:'id'}"
                  @change="change(item,index,'change')"
                ></el-cascader>
                <el-input
                  v-else
                  v-model="fieldsData[item.prop]"
                  :disabled="item.disabled"
                  :readonly="item.readonly"
                ></el-input>
              </template>
              <template v-if="item.type == 'checkboxgroup'">
                <el-checkbox-group v-model="fieldsData[item.prop]" :disabled="item.disabled">
                  <el-checkbox
                    v-for="(k,i) in item.options"
                    :label="k.value||k.id||k.label"
                    :key="i"
                    :disabled="k.disabled"
                    :checked="k.checked"
                  >{{k.label || k[item.config.label] }}</el-checkbox>
                </el-checkbox-group>
              </template>
              <template v-if="item.type == 'radio'">
                <template v-if="item.options && item.options.length">
                  <el-radio
                    v-model="fieldsData[item.prop]"
                    v-for="(k,i) in item.options"
                    :key="i"
                    :label="k.value"
                    :disabled="k.disabled"
                  >{{k.label}}</el-radio>
                </template>
                <el-radio
                  v-else
                  v-model="fieldsData[item.prop]"
                  :label="item.options.value"
                  :disabled="item.disabled"
                >{{item.options.label}}</el-radio>
              </template>
              <template v-if="item.type == 'radiogroup'">
                <el-radio-group v-model="fieldsData[item.prop]" :disabled="item.disabled">
                  <el-radio
                    v-for="(k,i) in item.options"
                    :label="k.value"
                    :key="i"
                    :disabled="k.disabled"
                  >{{k.label}}</el-radio>
                </el-radio-group>
              </template>
              <template v-if="item.type == 'button'">
                <div v-if="item.options && item.options.length">
                  <el-button
                    v-for="(k,i) in item.options"
                    :key="i"
                    :type="k.style"
                    @click.stop="click(k,i,item)"
                  >{{k.label}}</el-button>
                </div>
                <div v-else>
                  <el-button
                    :key="index"
                    :type="item.options.style"
                    @click.stop="click(item.options,index,item)"
                  >{{item.options.label}}</el-button>
                </div>
              </template>
              <template v-if="['date','datetime'].indexOf(item.type)>=0">
                <el-date-picker
                  :type="item.type"
                  value-format="yyyy-MM-dd"
                  v-model="fieldsData[item.prop]"
                  :placeholder="item.label"
                  :disabled="item.disabled"
                  :readonly="item.readonly"
                ></el-date-picker>
              </template>
              <template v-if="item.type == 'image'">
                <div :key="key" class="image">
                  <el-input
                    v-model="files[item.prop]"
                    type="text"
                    @paste.native.capture.prevent="onPaste($event,item,index)"
                    placeholder="粘貼截圖上傳"
                  >
                    <el-button slot="append" @click.native.stop="$refs.upload[0].click()">本地上傳</el-button>
                  </el-input>
                  <input
                    ref="upload"
                    type="file"
                    accept="image/png, image/jpg, image/jpeg, image/gif, image/svg"
                    @input="upload($event,item)"
                    v-show="false"
                  />
                  <div
                    v-if="fieldsData[item.prop] && fieldsData[item.prop].length"
                    class="image-box"
                  >
                    <template v-for="(v,i) in fieldsData[item.prop]">
                      <div class="image-item" :key="i">
                        <el-image :src="v" :preview-src-list="fieldsData[item.prop]" fit="cover" />
                        <i class="el-icon-delete" @click="onDelImage(item,i)"></i>
                      </div>
                    </template>
                  </div>
                </div>
              </template>
              <template v-if="item.type == 'upload'">
                <el-button
                  @click="$refs.upload[0].click()"
                  :type="item.style || 'primary'"
                  :icon="item.icon || 'el-icon-upload2'"
                >點(diǎn)擊上傳</el-button>
                <input
                  type="file"
                  ref="upload"
                  @change="onUpload($event,item)"
                  multiple
                  v-show="false"
                />
                <div v-if="fileList && fileList.length">
                  <span class="file-list" v-for="(k,i) in fileList" :key="i">{{k.name}}</span>
                </div>
              </template>
              <template v-if="item.type == 'plan'">
                <el-progress
                  :percentage="fieldsData[item.prop]"
                  text-inside
                  type="line"
                  :stroke-width="25"
                ></el-progress>
              </template>
            </el-form-item>
          </el-col>
        </template>
      </el-row>
    </template>
  </el-form>
</template>
<script>
export default {
  name: "mixForm",
  model: {
    prop: "fieldsData"
  },
  props: {
    fields: {
      type: Array,
      default: () => [
        {
          label: "菜單名稱",
          prop: "title",
          type: "text",
          rule: null,
          append: "arrow-right",
          click: (item, index) => {
            console.log(item);
          }
        },
        {
          label: "菜單名稱",
          prop: "title",
          type: "text",
          rule: null,
          append: "el-icon-arrow-right"
        },
        {
          label: "菜單名稱",
          prop: "title",
          type: "text",
          rule: null,
          prepend: "el-icon-arrow-left"
        },
        { label: "菜單名稱", prop: "switch", type: "switch", rule: null },
        { label: "單選框", prop: "radio", type: "radio", rule: null },
        {
          label: "單選框組",
          prop: "radiogroup",
          type: "radio",
          options: [
            { label: "a", value: "a" },
            { label: "b", value: "b" }
          ],
          rule: null
        },
        { label: "復(fù)選框", prop: "checkbox", type: "checkbox", rule: null },
        {
          label: "復(fù)選框",
          prop: "checkboxgroup",
          type: "checkbox",
          options: [
            { label: "a", value: "a" },
            { label: "b", value: "b" }
          ],
          rule: null
        }
      ]
    },
    fieldsData: {
      type: Object,
      default: () => {
        return {
          title: "false",
          switch: true,
          checkbox: false,
          checkboxgroup: [],
          radio: true,
          radiogroup: []
        };
      }
    },
    options: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  data() {
    return { key: "key", mapFields: [], files: [], fileList: [] };
  },
  watch: {
    fields() {
      this.onMapFields();
    },
    fieldsData() {
      this.key = Math.random();
    }
  },

  created() {
    this.onMapFields();
  },
  methods: {
    async onUpload(event, item) {
      this.fileList = event.target.files || [];
      let formData = new FormData();
      this.fileList.forEach(e => {
        formData.append("files", e);
      });
// 這里需要修改文件上傳接口地址
      let { data } = await this.axios("/admin/Publics/UploadFiles", {
        data: formData
      });
      if (data.code) {
        this.fieldsData[item.prop] = data.data;
      }
    },
    previewFile(file, files) {
      console.log(file, files);
    },
    removeFile(file, files) {
      console.log(file, files);
    },
    // 上傳圖片
    list(item) {
      if (item && item.length) {
        return item.map(e => {
          return e.blob || e;
        });
      }
      return [];
    },
    // 刪除選擇的圖片
    onDelImage(item, i) {
      this.fieldsData[item.prop].splice(i, 1);
      this.key = Math.random();
    },
    // 點(diǎn)擊按鈕上傳
    upload(event, item) {
      this.update(event.target.files[0], event.target.files[0].type, item);
    },
    // 粘貼剪切板截圖
    onPaste(event, item, index) {
      if (!(event.clipboardData && event.clipboardData.items)) {
        this.$message.error("當(dāng)前環(huán)境不支持粘貼上傳站欺,\n請(qǐng)手動(dòng)點(diǎn)擊上傳圖片");
        return;
      }
      if (event.clipboardData.items[0].type.indexOf("image") >= 0) {
        let file = event.clipboardData.items[0].getAsFile();
        let mime = event.clipboardData.items[0].type;
        this.update(file, mime, item);
      }
    },
    // 更新表單數(shù)據(jù)源字段內(nèi)容
    async update(file, mime, item) {
      let obj = new FormData();
      obj.append("files", file);
// 這里需要修改文件上傳接口地址
      let { data } = await this.axios("/admin/Publics/uploadsImage", {
        data: obj
      });
      if (data.code) {
        let blob = window.URL.createObjectURL(file);
        if (this.fieldsData[item.prop] && this.fieldsData[item.prop].length) {
          this.fieldsData[item.prop].push(data.data);
        } else {
          this.fieldsData[item.prop] = [data.data];
        }
        this.key = Math.random();
      }
    },
    onMapFields() {
      this.mapFields = [];
      let arr = [];
      this.fields.forEach(e => {
        if (e.wrap) {
          this.mapFields.push(arr);
          arr = [];
        }
        arr.push(e);
      });
      this.mapFields.push(arr);
    },
    change(item, index, type) {
      let click = item[type];
      if (!click) return;
      this.onClick(click, item, index, type);
    },
    click(item, index, option) {
      let click = item["click"];
      if (!click) return;
      this.onClick(click, item, index, option);
    },
    onClick(click, item, index, option) {
      if (typeof click == "function") {
        click(item, index, option);
        return;
      }
      if (typeof this.$parent[click] == "function") {
        this.$parent[click](item, index, option);
        return;
      }
    },
    // 輸入觸發(fā)驗(yàn)證
    validated(prop) {
      // 驗(yàn)證
      console.log("表單驗(yàn)證字段:", prop);
    },
    // 表單驗(yàn)證
    async validate(prop) {
      try {
        return await this.$refs["form"].validate();
      } catch (error) {
        return error;
      }
    },
    resetFields() {
      this.$refs["form"].resetFields();
    },
    clearValidate() {
      this.$refs["form"].clearValidate();
    },
    resetForm() {
      this.$refs["form"].resetFields();
    }
  }
};
</script>
<style lang='less' scoped>
.el-form {
  border: 1px solid #ccc;
  margin-bottom: 20px;
  padding-top: 20px;
}
.file-list {
  display: block;
  padding: 3px 15px;
  border: 1px solid #eee;
  margin-top: 10px;
  cursor: pointer;
  &:not(:last-child) {
    border-bottom: none;
  }
  &:not(:first-child) {
    border-top: none;
  }
}
.el-col {
  min-width: 280px;
}
.input /deep/ input[readonly="readonly"] {
  border: none;
  background: none;
}
.el-select,
.el-cascader,
.el-date-editor {
  width: 100%;
}
.image-box {
  display: flex;
  flex-wrap: wrap-reverse;
  align-content: center;
  align-items: center;
  .image-item {
    // width: 50px;
    // height: 50px;
    max-width: 150px;
    min-width: 150px;
    border: 1px solid #ccc;
    margin: 5px;
    position: relative;
    overflow: hidden;
    i {
      display: none;
      cursor: pointer;
    }
    &:hover {
      i {
        position: absolute;
        top: 0;
        right: 0;
        margin: auto;
        display: block;
        background: #fff;
      }
    }
  }
}
</style>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末中捆,一起剝皮案震驚了整個(gè)濱河市紧唱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌楞泼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瓜晤,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門腹纳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)痢掠,“玉大人,你說(shuō)我怎么就攤上這事嘲恍∽慊” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵佃牛,是天一觀的道長(zhǎng)淹辞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)俘侠,這世上最難降的妖魔是什么象缀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮爷速,結(jié)果婚禮上央星,老公的妹妹穿的比我還像新娘。我一直安慰自己惫东,他們只是感情好莉给,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著廉沮,像睡著了一般颓遏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滞时,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天叁幢,我揣著相機(jī)與錄音,去河邊找鬼漂洋。 笑死遥皂,一個(gè)胖子當(dāng)著我的面吹牛力喷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播演训,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼弟孟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了样悟?” 一聲冷哼從身側(cè)響起拂募,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎窟她,沒(méi)想到半個(gè)月后陈症,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡震糖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年录肯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吊说。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡论咏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出颁井,到底是詐尸還是另有隱情厅贪,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布雅宾,位于F島的核電站养涮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏眉抬。R本人自食惡果不足惜贯吓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吐辙。 院中可真熱鬧宣决,春花似錦、人聲如沸昏苏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)贤惯。三九已至,卻和暖如春棒掠,著一層夾襖步出監(jiān)牢的瞬間孵构,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工烟很, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颈墅,地道東北人蜡镶。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像恤筛,于是被迫代替她去往敵國(guó)和親官还。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容