接上篇文章,費心勞神好幾天的專案框架終於可以用了。現在可以開始寫頁面了吧?
既然上司說,UI框架我們自己來寫,那我們就自己寫吧。
雖然答應的時候挺痛快。真到寫的時候,首先就不知道從哪裡開始下手了
那我們就一點點來。先從元件框架開始一點點做。
首先先排布一下UI框架目錄。在於上司聊了許久後,最後決定用這種目錄架構:
紅色箭頭代表業務元件(business components) 存放專案中業務類元件的地方。如頭部導航,個人資訊卡等
綠色箭頭代表業務元件(framework components) 存放專案中基礎框架元件的地方。如按鈕,輸入框,開關等
藍色箭頭是匯出檔案。統一匯出所有元件方便呼叫(文章後面會講到)
好的,那我們先從input開始
input元件的編寫
我們先看看效果圖:
大致就是這個樣子。非常簡潔的UI,功能也算是夠用了。(這裡給UI大哥的作品點個贊)
我把它拆分成了這個樣子(如圖) 每個顏色框內都是不同的slot(具名插槽)
大致程式碼就是這個樣子的存在:
<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">複製程式碼
這樣就大功告成了。這樣父元件呼叫方就可以繫結到輸入框的值了
OK,接下來我們開始做“一鍵清空”功能
由於我們傳出去的值,是走的子元件內的雙向繫結的data。所以理論上我們只需要把繫結在data內的變數清空就行了
this.inputValue = '';複製程式碼
但是這樣會有問題,子元件內已經清空,父元件仍然保留著值。
那我們就模仿上面的做法,再$emit一次~~~
clearInputValue() {
this.inputValue = ''; //清空輸入框的值
this.$nextTick(function () { //在修改資料之後使用 $nextTick,則可以在回撥中獲取更新後的 DOM
this.$emit('input', this.inputValue); //執行傳出操作
});
},複製程式碼
這樣功能就實現了。如下圖( 清除按鈕的顏色太淺。動圖錄制軟體看不見~~~ 抱歉)
好的,那接下來實現一下密碼“顯示”“隱藏“的功能
這個功能也比較有意思。不只是把輸入框的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));
},複製程式碼
控制檯:
自己體會~~~
然後發現了個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就做好了!!功能還算是比較實用