前端專案框架搭建隨筆---input元件的編寫

王聖鬆發表於2018-08-24

接上篇文章,費心勞神好幾天的專案框架終於可以用了。現在可以開始寫頁面了吧?

既然上司說,UI框架我們自己來寫,那我們就自己寫吧。


雖然答應的時候挺痛快。真到寫的時候,首先就不知道從哪裡開始下手了

那我們就一點點來。先從元件框架開始一點點做。


首先先排布一下UI框架目錄。在於上司聊了許久後,最後決定用這種目錄架構:

前端專案框架搭建隨筆---input元件的編寫

紅色箭頭代表業務元件(business components) 存放專案中業務類元件的地方。如頭部導航,個人資訊卡等

綠色箭頭代表業務元件(framework components) 存放專案中基礎框架元件的地方。如按鈕,輸入框,開關等

藍色箭頭是匯出檔案。統一匯出所有元件方便呼叫(文章後面會講到)


好的,那我們先從input開始


input元件的編寫

我們先看看效果圖:

前端專案框架搭建隨筆---input元件的編寫

大致就是這個樣子。非常簡潔的UI,功能也算是夠用了。(這裡給UI大哥的作品點個贊)

我把它拆分成了這個樣子(如圖) 每個顏色框內都是不同的slot(具名插槽)

前端專案框架搭建隨筆---input元件的編寫

大致程式碼就是這個樣子的存在:

<template>
  <div class="input-wrapper">
    <div class="input-content">
      <div class="input__left">
        <slot name="left"></slot> //紅色框插槽
      </div>
      <div class="input__center">
        <input type="text" title="">
        <div class="input__center__tools">
          <i class="iconfont icon-qingchu" v-show="inputValue" @click="clearInputValue"></i> 
         //清除value的地方
        </div>
      </div>
      <div class="input__right">
        <slot name="right"></slot> //input右側的自定義區。可以放置“獲取驗證碼”之類的操作
      </div>
    </div>
    <div class="input-tip">
      <slot name="tip"></slot> //下方提示區域插槽
    </div>
  </div>
</template>複製程式碼

css方面選用flex佈局。字型/圖示大小,元素間距使用rem佈局 class命名使用bem方式

CSS:

<style scoped>
  .input__left .iconfont {
    font-size: 2rem;
  }

  .input-content {
    display: flex;
    flex-direction: row;
  }

  .input__left {
    padding-bottom: 0.4rem;
  }

  .input__left > span {
    font-size: 1.5rem;
  }

  .input__center {
    margin-left: .5rem;
    flex: 1;
    display: flex;
    flex-direction: row;
    padding-bottom: .3rem;
    border-bottom: 1px solid #E9E9E9;
  }

  .input__center > .input__center__tools > .iconfont {
    color: #141414;
    cursor: pointer;
    font-size: 2rem;
  }

  .input__center > input {
    text-indent: .3rem;
    flex: 1;
    border: 0;
    width: 100%;
    height: 100%;
    font-size: 1.3rem;
    color: #3b3b3b;
    outline: none;
  }

  .input__right {
    padding-left: .5rem;
    padding-bottom: .3rem;
    border-bottom: 1px solid #E9E9E9;
  }

  .icon-qingchu {
    color: #E9E9E9 !important;
  }

  .input-tip {
    margin-top: .3rem;
    margin-left: 2.45rem;
    font-size: 1rem;
  }

  .input-tip i {
    font-size: 1rem;
  }

  .input-tip--info {
    color: #BFBFBF;
  }

  .input-tip--success {
    color: #5CD18B;
  }

  .input-tip--warning {
    color: #FFC53D;
  }

  .input-tip--error {
    color: #FF7875;
  }
</style>
複製程式碼


OK,這時候我們的UI畫完了。還能輸入文字...不錯哦

這時候遇到了一個問題:之前我們直接v-model就可以雙向資料繫結input的value。現在input在元件內包著。那我們如何在父元件繫結子元件內的input呢??

找了半天教程,找到了這樣一個操作:

給元件新增 v-model 屬性時,預設會把 value 作為元件的屬性,然後把 'input' 值作為給元件繫結事件時的事件名

啊哈,這樣就好說了。那我們可以這樣去寫:

<input :type="textType" title="" v-model="inputValue"
       @input="$emit('input', inputValue)">

export default{
  data() {
    return { 
     inputValue: "" //子元件內繫結一遍。等會要用
     }
   },
  }

複製程式碼

外部呼叫:

<zb-input v-model="phoneLogin.phone.value">複製程式碼

這樣就大功告成了。這樣父元件呼叫方就可以繫結到輸入框的值了


前端專案框架搭建隨筆---input元件的編寫


OK,接下來我們開始做“一鍵清空”功能

由於我們傳出去的值,是走的子元件內的雙向繫結的data。所以理論上我們只需要把繫結在data內的變數清空就行了

this.inputValue = '';複製程式碼

但是這樣會有問題,子元件內已經清空,父元件仍然保留著值。

那我們就模仿上面的做法,再$emit一次~~~

clearInputValue() {
  this.inputValue = ''; //清空輸入框的值
  this.$nextTick(function () { //在修改資料之後使用 $nextTick,則可以在回撥中獲取更新後的 DOM
    this.$emit('input', this.inputValue); //執行傳出操作
  });
},複製程式碼

這樣功能就實現了。如下圖( 清除按鈕的顏色太淺。動圖錄制軟體看不見~~~ 抱歉)

前端專案框架搭建隨筆---input元件的編寫


好的,那接下來實現一下密碼“顯示”“隱藏“的功能


前端專案框架搭建隨筆---input元件的編寫


這個功能也比較有意思。不只是把輸入框的type換成password那麼簡單。還能還原之前傳入的input type

首先我們先定義一個props:

inputType: { //輸入框型別
  default: 'text'
}
canHide: {  //是否支援隱藏值
  required: false,
  default: false
},

複製程式碼

這個props從父元件接收想要的input型別。如果沒有就預設text

然後copy一份同義的變數到data裡。保持以prop做參考,data負責內部更改~~~

textType: this.inputType //從props裡接收初始值
isHideValue: false //現在是否隱藏輸入框值複製程式碼

然後清除事件:

hideInputValue() {
  if (this.textType === this.inputType) { //如果props內和data內相等。說明是沒隱藏狀態
    this.textType = "password"; //讓他變為password型別
    this.isHideValue = true; //控制隱藏按鈕的類名。更換icon
  } else {
    this.textType = this.inputType; //替換為初始化的type
    this.isHideValue = false;
  }
}複製程式碼

按鈕方面:

<i class="iconfont"
   :class="[{'icon-yanjing_yincang_o':!isHideValue},{'icon-yanjing_xianshi_o':isHideValue}]"
   @click="hideInputValue" v-show="canHide && inputValue"></i>複製程式碼


這樣就大功告成啦!!


既然我們做input了,那就做到底唄~~

讓他支援一下自定義規則驗證


通過和上司商定,暫選了這幾個規則支援:


1. lengthRange: Object 支援max,min最大最小值

2. regular: 標準正規表示式

3. required: Boolean 是否必填


資料格式大概是這樣的:

regex: {
  required:false,
  lengthRange: {
    max: 11,
    min: 1
  },
  regular: /^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$/
}複製程式碼

然後通過props傳進去:

regexObject: { //校驗物件
  required: false
}

:regexObject="regex"複製程式碼


然後準備工作做好了,開始準備校驗工作了

校驗順序按照佇列校驗。分別各自返回校驗結果。沒有指定的放行

是否必填校驗:

reg_required(value) {
  if (this.regexObject.required) { //如果有required這個欄位
     return !!value //返回value是否有值
  } else {
    return null; //代表沒填required這個欄位
  }
},複製程式碼

Tips:" !! " 常常用來做型別判斷,在第一步!(變數)之後再做邏輯取反運算。簡單理解就是判斷這個變數是否存在值。

等價 " value!=null&&typeof(value)!=undefined&&value!='' "

正規表示式校驗:

reg_regular(value) {
  if (this.regexObject.regular) { //如果有regular這個欄位
    return this.regexObject.regular.test(value); //返回校驗的結果
  } else {
    return null; //代表沒填regular這個欄位
  }
},複製程式碼

長度校驗:

reg_lengthRange(value) {
  if (this.regexObject.lengthRange) { //如果有lengthRange這個欄位
    return value.length >= this.regexObject.lengthRange.min //如果value的長度大於等於預定最小值
      && value.length <= this.regexObject.lengthRange.max //如果value的長度小於等於預定最大值
  } else {
    return null; //代表沒填lengthRange這個欄位
  }
},複製程式碼


主入口方法

regex(value) {
  let val = value.toString(); //統一轉成字串。防止沒有length屬性
  let info = {}; //空物件
  info.value = val; //一塊把輸入框的值傳出去
  if (this.regexObject) { //如果props傳了校驗物件
    info.required = this.reg_required(val);
    info.regular = this.reg_regular(val);
    info.lengthRange = this.reg_lengthRange(val);
  }
  return info;
},複製程式碼


最後在輸入框失焦事件(blur)內呼叫了一下:

inputReg() {
  console.log(this.regex(this.inputValue));
},複製程式碼


控制檯:

前端專案框架搭建隨筆---input元件的編寫


自己體會~~~

然後發現了個bug,雖然定義了最大輸入範圍,但超出只是提示不禁止輸入


於是watch監聽一下inputValue

inputValue(){
  if (this.regexObject && this.regexObject.lengthRange) {
    if (this.inputValue.length > this.regexObject.lengthRange.max) { //如果輸入框長度大於了既定最大長度
      this.inputValue = this.inputValue.slice(0, this.regexObject.lengthRange.max);//從0開始擷取。截到最大長度
      return false;
    }
  } else {
    return false;
  }
}複製程式碼

因為html5把maxlength屬性去掉了.....所以只能字串擷取


這樣一個入門級別的Input就做好了!!功能還算是比較實用


下一篇寫Tab元件的實現方式。各位週末愉快~~~週日晚繼續寫


相關文章