Vue使用element-ui所遇BUG與需求集結(二)

Shyla發表於2019-01-04

第二版啦(^U^)ノ~YO
由於專案的功能越來越多,開始注重細節的優化和可延展性,
主要方向是將複用的程式碼整合一個元件。

以下資料都是在vue(^2.5)+vuex(^2.3)+element-ui(^2.3)+webpack(^3.7)+axios(v0.16)環境下測試。

混入(Mixins)

由於有很多table都是要請求列表總數,包括請求列表也是千篇一律。一開始用的是Eventbus註冊了共用元件,後來感覺對非列表的元件來說是累贅,才改用mixins,既做到了通用,還能複寫。
雖然這個方法個人還是不太滿意。。。求更好idea

in @/componens/mixins/TableList.js

export default{
    data(){
        return {
            isLoading: false
        }
    },
    methods: {
        getTotal(response){
            // 加個loading狀態
            this.isLoading = !this.isLoading
            // 開始請求列表
            this.getTableList()
        },
        getList(response){
             // 結束loading狀態
            this.isLoading = !this.isLoading
        },
        getTableListCount(total){
            this.$http.get(total.api[0],{}).then((response) => {
                if(response.data && typeof response.data === `object`){
                    // 成功獲取數量回撥
                    this.getTotal(response)
                }
            }).catch(() => {})
        },
        getTableList(){
            this.$http.get(total.api[1],{}).then((response) => {
                if(response.data && typeof response.data === `object`){
                    // 成功獲取列表回撥
                    this.getList(response)
                }
            }).catch(() => {})
        }
    }
};

在元件內引用:

import TableListMX from `@/components/mixins/TableList`;
export default{
    mixins: [TableListMX],
    methods: {
        getStart(){
            this.getTableListCount({
                api: [`list_count`, `list`]
            })
        }
    }
}

自定義指令

專案後臺當然少不了表單,聯絡到資料輸入,這時候限制輸入內容顯得尤為必要。如果每個輸入框都在輸入時做提示就顯得累贅,所以用了指令去限制輸入。
這裡舉例一個限制小數點位數的自定義指令(參考大佬地址

in @directives/InputNumDigit

/*
 @directive 輸入框限制範圍:小數點個數 or 整數
        @param {data-index} 如果是陣列要加入index
        @param {data-dotrange}
*/

// 尋找當前dom
let FindElement = (parent, selector) => {
    return parent.tagName.toLowerCase() === selector ? parent : parent.querySelector(selector);
};

// 設定元件中的指定屬性的值
let setValue = function(exp, value, context) {
    value = isNaN(value) ? `` : value
    new Function(`context`, `value`, `context.${exp} = value`)(context, value)
};

export default{
    bind: function(el, { expression }, { context }){
        let $input = FindElement(el, `input`);
        el.$input = $input;

        // 初始化lastValue
        $input.lastValue = $input.value

        // 通過dataset 判斷是否允許小數點
        let allowDot = !!$input.dataset.dotrange,
            keys = $input.dataset.keys || -1,// 如果是陣列則加入索引
            dotRange = $input.dataset.dotrange || `{0,2}`, // 預設
            pattern = `^[0-9]+${allowDot ? `(.[0-9]${dotRange})?` : ``}$`,
            new_expression = expression;
        if (!expression) {
            throw new TypeError(`請繫結expression`)
        }
        // 迴圈
        if(keys !== -1){
            new_expression = expression.replace(/[.*?]/, `[${keys}]`)
        }
        console.log(new_expression)
        $input.handleInputEvent = function(e) {
            setTimeout(() => {
                if (e.target.value === ``) {
                    setValue(new_expression, ``, context)
                    // 遇到非法數值,則重置
                    e.target.value = ``
                } else if (e.target.value !== `` && !new RegExp(pattern).test(e.target.value)) {
                    setValue(new_expression, parseFloat(e.target.lastValue), context)
                    // 遇到非法數值,則重置為lastValue
                    e.target.value = e.target.lastValue
                    if (allowDot) {
                        $input.title = `小數點後最多${dotRange.replace(/[}{]/g, ``).split(`,`)[1]}位`
                    }
                }
                e.target.lastValue = e.target.value
            }, 0)
        }
        $input.addEventListener(`input`, $input.handleInputEvent, false)
    },
    unbind(el) {
        el && el.$input.removeEventListener(`input`, el.$input.handleInputEvent, false)
    }
};

全域性註冊:

import InputNumDigit from `@/directives/InputNumDigit`
Vue.directive(`num-digit`, InputNumDigit)

引用:

<!-- 最多隻能輸入三位小數 -->
<el-input v-model.number.trim="rate" v-num-digit="setForm.rate" data-dotrange="{0,3}" type="number"></el-input>

有個瑕疵,這個指令只相容了第一層for迴圈時的情況,沒有考慮到更復雜的情況


自定義過濾器

比如,將數字轉換成千分位

let ToThousands = (val) => {// 數字轉換成千分位
    if(!val || val === 0 || isNaN(val)){
        return val
    }else{
        let num = 0;
        if(val.toString().indexOf (`.`) !== -1){// 帶小數點
            num = val.toLocaleString()
        }else{
            num = val.toString().replace(/(d)(?=(?:d{3})+$)/g, `$1,`);
        }
        return num;
    }
};
Vue.filter(`ToThousands`, ToThousands);

// or 全域性通用
Vue.prototype.$toThousands = ToThousands;

常用表單驗證

表單驗證少不了,列幾個在後臺常用的,放在store可以隨時呼叫

let validator = {// 驗證資訊
    mobile: (rule, value, callback) => {// 手機號碼
        let reg = /^1[3|4|5|6|7|8|9][0-9]{9}$/;
        if(!reg.test(value)){
            callback(new Error("請輸入正確手機號碼"))
        }else{
            callback()
        }
    },
    idcard: (rule, value, callback) => {// 身份證號碼
        let dalu_reg = /(^d{15}$)|(^d{18}$)|(^d{17}(d|X|x)$)/,// 大陸
            xianggang_reg = /[A-Z]{1,2}[0-9]{6}([0-9A])/,// 香港
            aomen_reg = /^[1|5|7][0-9]{6}([0-9Aa])/,// 澳門
            taiwan_reg = /[A-Z][0-9]{9}/;// 臺灣
        if(!dalu_reg.test(value) && !xianggang_reg.test(value) && !aomen_reg.test(value) && !taiwan_reg.test(value)){
            callback(new Error("請輸入正確身份證號碼"))
        }else{
            callback()
        }
    },
    noChinese: (rule, value, callback) => {// 英文和數字
        let reg = /^[A-Za-z0-9]+$/g;
        if(!reg.test(value)){
            callback(new Error("不能輸入中文!"))
        }else{
            callback()
        }
    },
    limitNumber: (rule, value, callback) => {// 限制數字大小
        if(value < 0 || value >= 10000000){
            callback(new Error("最多輸入7位有效數字"))
        }else if(isNaN(value)){
            callback(new Error("請輸入數字"))
        }else{
            callback()
        }
    },
    limitPercent: (rule, value, callback) => {// 限制百分比
        if(value < 0 || value > 100){
            callback(new Error("請輸入0~100之間的數字"))
        }else{
            callback()
        }
    }
};

常用日期限制範圍

let dateLimit = {
    beforeToday: {// 今天之前:不包含今天
        disabledDate(time) {
            return time.getTime() > Date.now() - 8.64e7;
        }
    },
    beforeTomorrow: {// 明天之前:包含今天
        disabledDate(time) {
            return time.getTime() > Date.now();
        }
    },
    afterToday: {// 今天之後:包含今天
        disabledDate(time) {
            return time.getTime() < Date.now() - 8.64e7;
        }
    },
    afterTomorrow: {// 明天之後:不包含今天
        disabledDate(time) {
            return time.getTime() < Date.now();
        }
    }
};

分頁自定義頁數

<!-- 通過`page-size`與輸入框同一個引數控制 -->
<el-pagination class="sl-page"
layout="prev, pager, next, jumper, total, slot"
@current-change="handleCurrentChange"
:current-page.sync="params.pager"
:page-size="params.count"
:total="TOTAL">
    <span class="resize">
        <span>每頁記錄數:</span>
        <el-input type="number" size="small" 
        v-model.number="params.count" 
        @change="handleSearch"></el-input>
    </span>
</el-pagination>
export defalt{
    data(){
        return {
            TOTAL: 100,
            params: {
                pager: 1,
                count: 10
            }
        }
    },
    methods: {
        handleSearch(){//頁數改變
            if(this.parmas.count <= 0 || !this.parmas.count || String(this.parmas.count).indexOf(".") !== -1){
                //小於等於零or為空or小數點時不重新整理資料
                return false;
            }
            this.parmas.pager = 1;
            this.getList()
        },
        handleCurrentChange(){//頁碼改變
            this.getList()
        }
    }
};
.sl-page {
  text-align: center;
  padding-top: 20px;
}

.sl-page .resize {
  width: 60px;
}

.sl-page .resize .el-input__inner {
  height: 28px;
  padding: 0 5px;
}

偽Radio · 真 · CheckBox

單個radio可以不勾選效果

複合:

<div class="fake-checkbox">
    <el-radio v-model="isCheck" :label="1">是否勾選</el-radio>
    <el-checkbox v-model="isCheck" :false-label="0" :true-label="1" @change="handleSearch">是否勾選</el-checkbox>
</div>
.fake-checkbox{
  position: relative;
  display: inline-block;
}
.fake-checkbox .el-checkbox,
.fake-checkbox .el-radio{
  width: 55px;
}
.fake-checkbox .el-radio__label{
  padding-left: 5px;
}
.fake-checkbox .el-checkbox{
  position: absolute;
  top: 0;
  right: 0;
  opacity: 0;
}

樣式修改

1. 下拉箭頭位置偏差

.el-select-dropdown .popper__arrow {
    transform: none !important;
}

2. 表單偽必填

.sl-required .el-form-item__label:before {
  content: `*`;
  color: #fa5555;
  margin-right: 4px;
}
<el-form-item class="sl-required">...</el-form-item>

3. 去除輸入框number型別的箭頭

.non-arrow input[type="number"]::-webkit-outer-spin-button,
.non-arrow input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
<el-input class="non-arrow" type="number"></el-input>

相關文章