手寫(radio)element-ui元件

三隻萌新發表於2019-03-03

前言

在使用者使用過程中提出一鍵匯入的功能,需求如下:點選匯入按鈕顯示提示框,然後是單選框以及上傳按鈕。pc端常使用element-ui元件,但是這個專案是vue1的老專案,並且沒有element-ui元件。所以需要自己動手實現單選功能和上傳功能。

需求

radio 屬性及方法

  • name: 用於定義同一型別的 radio 同一 name 的 radio 只能選中一個(單選實現)
  • id: 用於和 label 標籤關聯起來 實現點選 label 標籤內的元素也能選中 radio
  • value:單選按鈕的值,選中某個單選按鈕相當於拿到 value 值 tip:用於識別組中的哪個單選按鈕被選中。
  • checked 用於設定預設選中的 radio
  • v-model 建立雙向資料繫結。 會忽略所有表單元素的 value、checked、selected 特性的初始值而總是將 Vue 例項的資料作為資料來源。
// html
 <div v-for="day in weekSelectList"
    :key="day.id"
    class="select__day">
    <input type="radio"
      name="week"
      :id="day.label"
      :value="day.value"
      v-model="selectedDay">
    <label :for="day.label">{{day.label}}({{day.value}})</label>
  </div>
複製程式碼
// 暫定的資料
data(){
  return {
  weekSelectList: [
      { label: `週一`, value: `2018-12`, id: 1 },
      { label: `週二`, value: `2018-13`, id: 2 },
      { label: `週三`, value: `2018-14`, id: 3 },
      { label: `週四`, value: `2018-15`, id: 4 },
      { label: `週五`, value: `2018-16`, id: 5 }
    ]
  },
  selectedDay: `2018-12`,
}
複製程式碼

通過 v-model 繫結 selectedDay,匹配到相同的值會將該 radio 選中,當改變 radio 的選擇,selectedDay 也會動態的變更成選中的 radio 的 value

上傳檔案

屬性

  • accept 屬性接受一個(多個值時)逗號分隔的字串 如:accept=”image/png, image/jpeg”
  • id
  • name
  • 注意:accept 屬性並不會驗證選中檔案的型別只是在使用者瀏覽時只顯示指定檔案型別

缺點

  1. 在未上傳檔案時,顯示”未選擇檔案”,使用者介面不友好,不可配置
  2. 同一個檔名即使內容改變了,重新上傳也不會觸發 change 事件
  3. 使用者如果在上傳過程中點選了“取消”,已經上傳的檔案會被移除

解決方式

  <div class="upload__button"
    :class="{`upload__button--uploaded`:isUploaded}"
    @click="onUploadClick">點選上傳</div>
  <input type="file"
    class="upload__file"
    v-el:upload
    accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
    @change="onFileChange" />
複製程式碼
methods:{
  onUploadClick() {
    if (!this.isUploaded) {
      this.$els.upload.click()
    }
  },
  onFileChange(e) {
  const file = e.target.files[0]
  if (file === undefined) {
    return
  }
  this.fileName = file.name
  const result = /[xls|xlsx]$/.test(this.fileName)
    if (result) {
      this.isUploaded = true
      this.showAlert(`上傳成功`)
      this.$els.upload.value = null
    } else {
      this.showAlert(`檔案格式錯誤,請確認後重試。`)
    }
  },
}
複製程式碼

當點選上傳按鈕觸發 onUploadClick 事件後,獲取到 upload 繫結的 DOM (由於是老的專案使用的是$els,vue2 使用 ref)手動觸發 click 事件並且可以在change事件中預設接收一個檔案資訊物件其中target.files[0]包含檔案的更多資訊,如下圖:

手寫(radio)element-ui元件

判斷檔案型別

可以看到 change 事件的返回值包含著檔案屬性,這裡我們需要判斷是檔名是否為 excel,使用正則的 test 方法。

重置change事件

在最後 this.$refs.uploadFile.value = null; 移除檔案,可以保證上傳同樣的檔案時,也會觸發 change 事件

優化樣式

至此關於表單方面的功能都已經實現了,由於原始的radio樣式比較醜,而且不能更改。下面我們就想辦法將它做的漂亮些。

// template
 <label v-for="(item,index) in radioList"
        :key="item.value"
        :for="item.linkLabel"
        :accesskey="index">
        <span class="content__input">
          <span class="radio__replace"
            :class="{`radio__replace--checked`:selectedRadio===item.value,`radio__replace--disable`:item.isDisabled}">
          </span>
          <input v-model="selectedRadio"
            type="radio"
            class="radio__button"
            name="radio"
            :id="item.linkLabel"
            :tabindex="index"
            :value="item.value"
            :disabled="item.isDisabled"
            @focus="item.isFocus = true"
            @blur="item.isFocus = false" />
        </span>
        <span class="content__text">{{item.label}}</span>
      </label>
複製程式碼
// data
  data() {
    return {
      radioList: [
        {
          linkLabel: `label1`,
          value: `1`,
          isDisabled: false,
          isFocus: false,
          label: `標籤1`
        },
        {
          linkLabel: `label2`,
          value: `2`,
          isDisabled: false,
          isFocus: false,
          label: `標籤2`
        },
        {
          linkLabel: `label3`,
          value: `3`,
          isDisabled: true,
          isFocus: false,
          label: `標籤3`
        }
      ],
      selectedRadio: `1`
    }
複製程式碼
  1. 這裡使用遍歷的方式在data中定義多個radio,在前面我們講到過radio的基本用法,使用label的for屬性和input的for屬性實現關聯起來。(這裡我將input放在label內,這樣點選整個label都會選中,沒有label和radio元素之間的間隙)。
  2. name相同的radio會實現單選效果tabindex代表使用"Tab"鍵的遍歷順序 ,value是選中時v-model繫結的selectedRadio也就會跟著變更
  3. 實現個性化樣式的關鍵在於結構就是用一個類名content__input標籤將類名radio__replace和radio包起來。設定定位層級(相當於radio被覆蓋了,然而只要點選到labelradio就會被選中)
  4. 通過selectedRadio選中的值和當前radio值做對比,以及isDisabled這些Boolean值動態繫結class實現我們自定義的radio樣式切換

效果如下:

手寫(radio)element-ui元件

其實radio__replace類名對應的標籤就是我們自定義的radio,其中的白色原點是通過偽類生成的css程式碼放在最後,感興趣可以看下

偽類樣式修改

如果想通過類名來改變白色原點的樣式,可以通過權重來改變。如下通過isShow來給外層新增test類名 而起始的時候設定的權重為兩層,之後新增一層可以起到修改樣式的效果。(ps:偽類不能通過預先設定好的類名來修改樣式)例子程式碼如下:

 <div :class="{test:isShow}"
      @click="onRedClick">
      <div class="text__item"></div>
    </div>
複製程式碼
.text__item {
  &:after {
    content: ``;
    width: 30px;
    height: 30px;
    background-color: #f00;
    position: absolute;
    bottom: 20px;
  }
}
.test {
  .text__item {
    &:after {
      background-color: #ff0;
    }
  }
}
複製程式碼
// css
 .radio {
        &__replace {
          border: 1px solid #dcdfe6;
          border-radius: 100%;
          width: 14px;
          height: 14px;
          background-color: #fff;
          position: relative;
          cursor: pointer;
          display: inline-block;
          box-sizing: border-box;
          z-index: 999;
          transition: 0.15s ease-in;
          &--checked {
            border-color: #409eff;
            background-color: #409eff;
          }
          &--disable {
            cursor: not-allowed;
          }
          &:after {
            width: 4px;
            height: 4px;
            border-radius: 100%;
            background-color: #fff;
            content: ``;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
          }
        }
        &__button {
          opacity: 0;
          outline: none;
          position: absolute;
          z-index: -1;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          margin: 0;
        }
      }
複製程式碼

總結

  1. 介紹了radio基本屬性,使用案例並優化了radio的樣式
  2. 原始上傳檔案元素的缺點以及改善方法

相關文章