1

基础形态
image.png
高级搜索
image.png
搜索完成
image.png

组件的基本代码

Vue.component('ym-search', {
    template: `
        <div class="ym-search-area-wrap" ref="searchWrap" :class="{'show-result': showResult}">
            <div class="ym-search-area" :style="{'height': searchHeight}" v-if="!showResult">
                <slot></slot>
                <div class="ym-high-btn-area">
                    <el-button type="primary" size="small" @click="toSearch(1)">搜索</el-button>      
                    <el-button type="default" size="small" @click="cleanAll">清空</el-button>   
                    <div class="ym-slide-up" @click="slideUp"><i class="el-icon-arrow-up"></i>收起</div>
                </div>
                <div class="ym-search-btn-area" :class="{'hide': hideBtn}">
                    <el-button type="primary" size="small" @click="toSearch(0)">搜索</el-button>
                    <el-button v-if="showHighBtn" size="small" @click="toHighSearch">高级搜索</el-button>
                </div>
            </div>
            <div class="ym-search-result ym-flex-row" v-if="showResult">
                <div class="flex-item" v-for="item in results">{{item.name}}:{{item.value}}</div>
                <div class="results-options" @click="showSearch">
                    <i class="el-icon-arrow-down"></i>展开
                </div>
            </div>
        </div>
    `,
    data() {
        return {
            searchHeight: '52px', // 搜索框的默认高度52
            showHighBtn: false, // 是否显示“高级搜索”按钮
            hideBtn: false,  // 隐藏“搜索”按钮与“高级搜索”按钮
            showResult: false, // 搜索完成后是否显示搜索结果
        }
    },
    props: {
        // 搜索元素的个数
        itemNum: {
            type: Number,
            default: 1
        },
        // 搜索后要显示的结果集合
        results: Array
    },
    watch: {
        // 页面初始化时需要默认一些条件
        results(val) {
            this.$nextTick(()=>{
                if((val.length > 0) && this.showHighBtn) {
                    this.showResult = true
                }
            })
        }
    },
    mounted() {
        // 根据搜索框的宽度以及搜索元素的宽度,计算是否显示“高级搜索”按钮
        this.$nextTick(() => {
            let wrapW = +window.getComputedStyle(this.$refs.searchWrap).width.replace('px', ''),
                itemW = 322,
                btnW = 180,
                n = Math.floor((wrapW - btnW) / itemW)
            if (this.itemNum > n) {
                this.showHighBtn = true
                if (this.results.length > 0) this.showResult = true
            }
        })
    },
    methods: {
        // 清空
        cleanAll() {
            this.$emit('clear')
        },
        // 搜索
        toSearch(tag) {
            this.$emit('search')
            this.$nextTick(() => {
                // 如果是高级中的搜索按钮,点击后显示搜索内容
                if (+tag === 1) {
                    if (this.results.length > 0) {
                        this.showResult = true
                    } else {
                        this.searchHeight = '52px'
                        this.hideBtn = false
                    }
                }
            })
        },
        showSearch() {
            this.showResult = false
            if (this.results.length > 0) {
                this.searchHeight = 'auto'
                this.hideBtn = true
            }
        },
        // 显示高级搜索内容
        toHighSearch() {
            this.searchHeight = 'auto'
            this.hideBtn = true
        },
        // 收起
        slideUp() {
            if (this.results.length > 0) {
                this.showResult = true
            } else {
                this.searchHeight = '52px'
                this.hideBtn = false
            }
        }
    }
})

同时,也写入了全局混入

Vue.mixin({
    data() {
        return {
            // 所有的搜索属性v-model集合
            searchData: {},
            // 搜索结果--用于展示
            searchResults: [],
            // 搜索属性请求数据集合
            requestData: {},
        }
    },
    methods: {
        // 可在调用页面自定义toSearchBefore和toSearchAfter方法
        toSearch() {
            if(!!this.toSearchBefore) {
                this.toSearchBefore.apply(this, arguments)
            }
            let {
                results,
                request
            } = packageRequestAndResults(this.searchData)
            this.searchResults = results
            this.requestData = request
            this.pageChange(1)
            if(!!this.toSearchAfter) {
                this.toSearchAfter.apply(this, arguments)
            }
        },
        clearSearch() {
            this.searchData = {}
            this.requestData = {}
            this.searchResults = []
        }
    }
})

上面代码中涉及到一个packageRequestAndResults方法

// 根据searchBaseData和searchData组装显示results和请求数据request
function packageRequestAndResults(searchData) {
    let results = [] // 搜索结果展示
    let request = {} // 用于请求接口数据
    Object.keys(searchData).forEach(k => {
        let string_empty = Object.prototype.toString.apply(searchData[k]) === '[object String]' && searchData[k] === '',
            array_empty = Object.prototype.toString.apply(searchData[k]) === '[object Array]' && searchData[k].length === 0
        if (!string_empty && !array_empty && !!searchData[k]) {
            let obj = searchBaseData[k],
                name = ''
            if (!!obj) {
                if (obj.type === 'input') {
                    name = searchData[k]
                    request[k] = searchData[k]
                } else if (obj.type === 'date') {
                    name = searchData[k][0] + ' ~ ' + searchData[k][1]
                    request['START' + k] = searchData[k][0]
                    request['END' + k] = searchData[k][1]
                } else {
                    name = obj.getName(searchData[k])
                    request[k] = obj.getVal(searchData[k])
                }
                results.push({
                    name: obj.name,
                    value: name
                })
            }
        }
    })
    return {
        results,
        request
    }
}

上面有一个searchBaseData,下面即将出场

页面使用

<ym-search :item-num="3" @search="toSearch" @clear="clearSearch" :results="searchResults">
    <div>
        <span class="ym-label">微信昵称</span>
        <input type="text" placeholder="请输入微信昵称" v-model="searchData.KEYWORD_SEARCH">
    </div>
    <div>
        <span class="ym-label">关注时间</span>
        <el-date-picker v-model="searchData.SUBSCRIBETIME" type="daterange" range-separator="至"
            start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd">
        </el-date-picker>
    </div>
    <div>
        <span class="ym-label">身份</span>
        <el-select v-model="searchData.FCUSTOMERSTATUS" placeholder="请选择">
            <el-option v-for="item in identityOption" :key="item.value" :label="item.label"
                :value="item.value">
            </el-option>
        </el-select>
    </div>
</ym-search>
item-num必须与搜索item的个数一致,绑定results,clear,search无需改名,因为上面的mixins中已定义,除非不能满足需求,可重定义

v-model的格式须是searchData.xxx,xxx为请求参数的key,如果是日期格式且key为STARTSUBSCRIBETIME和ENDSUBSCRIBETIME,需要去掉START和END,如果不是,下面再讲

下面重要环节,必须声明一个searchBaseData对象,包含每个搜索item

let searchBaseData = {
    // 如果显示与value一致,可直接指定type=input
    KEYWORD_SEARCH: {
        name: '微信昵称',
        type: 'input'
    },
    // 如果请求key以'START'和'END'开头,可直接指定type=date,如果不是,则采用下面的方式
    SUBSCRIBETIME: {
        name: '关注时间',
        type: 'date'
    },
    // 如果显示和value无规律,可指定getName和getVal
    FCUSTOMERSTATUS: {
        name: '身份',
        getName(val) {
            let arr = ['潜客', '车主', '关注', '会员']
            return arr[+val]
        },
        getVal(val) {
            return val
        }
    },
}

代码在此
END


yingmhd
67 声望4 粉丝

路漫漫其修远兮,吾将上下而求索


下一篇 »
节流和防抖