百万前端之VUE 2.X + vant 实现关键字搜索内容 搜索记录、热搜

        在前端工作中,一般对于展示数据量比较多的页面来说,都是需要一个搜索功能来给予用户更优质的体验;这次展示的例子来源于我的工作中的摘录。插件安装就不多说了,各位大佬一个都懂npm install了吧。

下面是使用的版本信息:

nameedition
vue2.6.11
vue-router3.2.0
axios0.27.2
vant2.12.48

代码思路:

        看到需求的时候就需要有清晰的思路去编写代码,不然写出来的就是乱糟糟的东西,还需要不断的修改,养成先思考再编写的习惯,不要一股脑对着键盘一顿输出。

        项目需求首先是关键字搜索、搜索记录、热搜榜单,主要就是实现这三个功能。根据需求可以知道具体流程了,进入搜索 => 输入关键字=>产生搜索记录=>记录一次词条搜索记录(热搜榜)=>展示搜索结果(渲染接口数据)。现在思路清晰了,下面搜索前的是效果图:

 

 vue的结构代码:

 <!-- 顶部搜索框 -->
        <div class="topSearch">
            <form action="/">
                <van-search
                        v-model="value"
                        show-action
                        placeholder="请输入搜索关键词"
                        @search="onSearch"
                        @cancel="onCancel"
                />
            </form>
        </div>
        <!-- 搜索前 -->
        <div class="searchBefore" v-if="searchNull == 0">
            <!-- 搜索记录 -->
            <div class="search-records">
                <div style="display: flex;justify-content: space-between;width: 96%;margin: 0 auto;" v-if="this.searchText.length != 0">
                    <div style="font-size: 14px;color: #999999;text-align: left;display: block;width: 50%;height: 0.2rem;line-height: 0.2rem;">
                        历史记录
                    </div>
                    <div style="font-size: 15px;color: #999999;width: 30%;height: 0.2rem;line-height: 0.2rem;text-align: right;">
                        <van-icon name="delete-o" @click="deleteSearch" />
                    </div>
                </div>
                <!-- 添加记录 -->
                <div class="searchText" v-if="this.searchText.length != 0">
                    <!-- 添加多条时未换行bug -->
                    <div v-for="(record,index) in searchText" :key="index" @click="searchTextClisk(index,record)">
                        {{record.keyword}}
                    </div>
                </div>
            </div>
            <!-- 热搜 -->
            <div class="hotSearch">
                <div class="hotSearch-title"><van-icon name="fire" /><div>知识热搜</div></div>
                <div class="hotSearch-list">
                    <ul>
                        <li v-for="(hots,index) in hotList" :key="index" @click="hotcontent(index,hots)">
                            <span><van-icon name="stop" /></span>
                            {{hots.keyword}}
                        </li>
                    </ul>
                </div>
            </div>
        </div>
<!-- 搜索结果 -->
        <div class="article-list" v-if="searchNull == 1">
            <!-- 分类栏 -->
            <div class="article-top">
                <van-tabs animated sticky @click="typeClick">
                    <van-tab  v-for="(titles,index) in searchTitle" :key="index" :title="titles.name">
                        <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="allLoad">
                            <!-- 搜索出来的文章结果 -->
                            <div class="article-con-list">
                                <!-- 普通文章(包括图文、视频、音频) -->
                                <div class="general-articles" v-for="(article,index) in articleList" :key="index" @click="gotoArticle(article.id)">
                                    <div>
                                        <!-- 文章标题 -->
                                        <div class="articles-big-title">{{article.title}}</div>
                                        <!-- 文章发布单位和时间 -->
                                        <div class="articles-timeAndName">
                                            <div>{{article.organ_name}}</div><div>{{article.createtime,time | handleDate}}</div>
                                        </div>
                                    </div>
                                    <div class="articles-image">
                                        <!-- 文章图片 -->
                                        <img :src="url + article.image" alt="">
                                    </div>
                                </div>
                                <van-empty image="search" description="" v-if="total == 0" />
                        </van-list>
                    </van-tab>
                </van-tabs>
            </div>
        </div>

页面全部css样式都在下面,可能有些用不上,大佬们自己调整。

css代码(提供参考,实际工作中自己调整):

<style scoped>
    .content{width: 100%;height: auto;}

    /* 搜索框 */
    .topSearch{width: 100%;height: auto;border-bottom: 1px solid #CCCCCC;}
    .topSearch >>> .van-search .van-cell{padding-right: 15px;}
    .search-records{font-size: 14px;width: 98%;height: auto;margin-top: 0.15rem;}
    .searchText{width: 90%;height: auto;margin: 0 auto;margin-top: 0.1rem;display: flex;justify-content: left;flex-wrap: wrap;}
    .searchText div{font-size: 15px;text-align: left;height: 0.25rem;line-height: 0.14rem;width: auto;margin-right: 0.1rem;background: #dddddd;padding: 0.06rem 0.1rem;border-radius: 2000px;margin-top: 0.1rem;}
    .hotSearch{width:96%;height: auto;margin: 0 auto;margin-top: 0.2rem;}
    .hotSearch-title{font-size: 16px;font-weight: bold;text-align: left;display: flex;justify-content: left;}
    .hotSearch-title >>> .van-icon-fire{color: #ff5e5e;margin-right: 0.05rem;line-height: 0.2rem;}
    .hotSearch-list{font-size: 15px;}
    .hotSearch-list ul{margin-top: 0.1rem;}
    .hotSearch-list ul li{text-align: left;margin-top: 0.1rem;width: 55%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
    .hotSearch-list >>> .van-icon-stop{color: #d66353;margin-right: 0.05rem;}
    /* 搜索结果列表 */
    .article-list{width: 100%;height: auto;margin: 0 auto;}
    .article-top{width: 100%;height: auto;}
    .article-top >>> .van-tabs__nav{background: #f7f7f7;width: 100%;}
    .article-top >>> .van-tabs__content{font-size: 14px;background: #f7f7f7;text-align: left;width:90%;height: auto;margin: 0 auto;}
    .article-con-list{width: 100%;height: auto;}
    .general-articles:nth-child(1){margin-top: 0.2rem;}
    .general-articles{width: 100%;height: 1.1rem;background: white;margin-top: 0.2rem;border-radius: 4px;display: flex;justify-content: space-between;padding: 0.1rem;}
    .general-articles>div:nth-child(1){width: 50%;position: relative;}
    .general-articles>div{width: 40%;}
    .articles-big-title{font-size: 16px;font-weight: bold;background: white;width: 100%;height:0.25rem;line-height: 0.25rem;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
    .articles-timeAndName{width: 100%;display: flex;justify-content: space-between;position: absolute;bottom: 0;font-size: 13px;color: #999999;}
    .articles-timeAndName div:nth-child(1){width: 60%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;text-align: left;line-height: 0.15rem;}
    .articles-timeAndName div:nth-child(2){width: 38%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;text-align: right;line-height: 0.15rem;}
    .articles-image{width: 100%;height: 100%;border-radius: 3px;}
    .articles-image>img{width: 100%;height: 100%;border-radius: 3px;}
</style>

最重要的js核心代码,重要的是理解方法逻辑,理解了就可以举一反三了

js逻辑(代码全部给出了,我获取接口数据的axios都是全局封装好的):

export default{
        data(){
            return{
                value: '', // 输入的搜索内容
                searchText:[],// 搜索记录列表
                hotList:[],// 热搜记录列表
                articleList:[],//搜索结果列表
                page: 1, //列表分页加载
                loading: false, //加载状态结束
                finished: false, //数据全部加载完成
                searchNull:0, //是否开始搜索
                //数据全部加载完成
                searchTitle:[
                    {name:'全部'},
                    {name:'图文'},
                    {name:'视频'},
                    {name:'音频'},
                ],
                url: domain.imgUrl,// 全局图片地址拼接
                time:'', //当前时间
                total:'',//数据总条数
            }
        },
        mounted() {
            this.getSearch()
        },
        methods:{
            //搜索记录和热搜
            getSearch(){
                this.$axios.user_search_history().then(res=>{
                    // 搜索记录列表
                    this.searchText = res.data.data.user_history
                    // 热搜记录列表
                    this.hotList = res.data.data.hot_list
                }).catch(err=>{
                    console.log(err)
                })
            },
            //删除搜索记录
            deleteSearch(){
                this.$axios.delete_search_history().then(res=>{
                    //删除搜索记录列表  需要添加弹窗删除警告
                    this.searchText = []
                })
            },
            //点击历史搜索记录进行搜索
            searchTextClisk(index,record){
                //点击搜索记录,填入搜索框
                this.value = record.keyword
                //触发搜索事件
                this.onSearch()
            },
            //点击热搜进行搜索
            hotcontent(index,hots){
                //点击搜索记录,填入搜索框
                this.value = hots.keyword
                //触发搜索事件
                this.onSearch()
            },
            //确定搜索时触发
            onSearch(val) {
                //搜索时加载
                this.$toast.loading({
                    message: '加载中...',
                    forbidClick: true,
                    loadingType: 'spinner',
                    duration:500,
                });
                //搜索关键字
                this.keyword = val
                //输入搜索内容为空时不给搜索
                if (val != ''){
                    //切换为搜索状态
                    this.searchNull = 1
                    this.$axios.article_list({keyword: this.keyword,file_type:0,page:this.page}).then(res=>{
                        //搜索结果列表
                        this.articleList = res.data.data.data
                        //当前时间
                        this.time =  res.data.time
                        //数据总条数
                        this.total = res.data.data.total
                    }).catch(err=>{
                        console.log(err)
                    })
                }else {
                    //搜索输入内容为空时提示
                    this.$toast('请输入搜索关键字');
                }
            },
            //点击取消按钮时触发
            onCancel() {
                this.$router.go(-1);
            },
            //搜索结果列表加载
            allLoad(){
                this.page++
                //加载请求第二页
                this.$axios.article_list({keyword: this.keyword,file_type:0,page:this.page}).then(res => {
                    const rows = res.data.data.data
                    this.loading = false
                    //列表内容总数
                    this.total = res.data.data.total
                    if (rows == null || rows.length === 0) {
                        // 加载结束
                        this.finished = true
                        return
                    }
                    // 将新数据与老数据进行合并
                    this.articleList = this.articleList.concat(rows)
                    // 如果列表数据条数>=总条数,不再触发滚动加载
                    if (this.articleList.length >= this.total) {
                        this.finished = true
                    }
                }).catch(err => {
                    console.log(err)
                });
            },
            //点击标签栏对搜索内容进行分类 (全部、图文、视频、音频)
            typeClick(index){
                //点击分类时先清空数组,防止切换时出现页面bug
                this.articleList = []
                //切换为搜索状态
                this.searchNull = 1
                this.$axios.article_list({keyword: this.keyword,file_type:index,}).then(res=>{
                    //搜索结果列表
                    this.articleList = res.data.data.data
                    //当前时间
                    this.time =  res.data.time
                    //数据总条数
                    this.total = res.data.data.total
                }).catch(err=>{
                    console.log(err)
                })
            },
            //点击跳转相对应文章
            gotoArticle(id){
                this.$router.push('/learning/articleDetails?id=' + id)
            },
        },
        filters: {
            handleDate(value, serverTime) {
                let date = new Date(value * 1000);
                let serverTimes = new Date(serverTime * 1000);
                let diffValue = serverTimes - date.getTime(); // 时间差
                let mValue = diffValue / (1000 * 60);  // 分
                let hValue = diffValue / (1000 * 60 * 60);  // 小时
                let dayValue = diffValue / (1000 * 60 * 60 * 24);  // 天
                let result = '';
                if (date.getFullYear() !== new Date().getFullYear()) {  // 不同年
                    let date = new Date(value * 1000); // 10位数时间戳需要 * 1000
                    let y = date.getFullYear();
                    let MM = date.getMonth() + 1;
                    MM = MM < 10 ? ('0' + MM) : MM;
                    let d = date.getDate();
                    d = d < 10 ? ('0' + d) : d;
                    let h = date.getHours();
                    h = h < 10 ? ('0' + h) : h;
                    let m = date.getMinutes();
                    m = m < 10 ? ('0' + m) : m;
                    let s = date.getSeconds();
                    s = s < 10 ? ('0' + s) : s;
                    return y + '-' + MM + '-' + d;
                } else {  // 同年
                    if (dayValue > 360) {  // 时间差大于一年
                        let date = new Date(value * 1000); // 10位数时间戳需要 * 1000
                        let y = date.getFullYear();
                        let MM = date.getMonth() + 1;
                        MM = MM < 10 ? ('0' + MM) : MM;
                        let d = date.getDate();
                        d = d < 10 ? ('0' + d) : d;
                        let h = date.getHours();
                        h = h < 10 ? ('0' + h) : h;
                        let m = date.getMinutes();
                        m = m < 10 ? ('0' + m) : m;
                        let s = date.getSeconds();
                        s = s < 10 ? ('0' + s) : s;
                        return '一年前';
                    }else if (dayValue >= 30 && dayValue <= 360) {
                        result = parseInt(dayValue / 30) + '月前';
                    }else if (dayValue >= 7 && dayValue <= 30) {
                        result = parseInt(dayValue / 7) + '周前';
                    } else if (dayValue >= 1 && dayValue <= 6) {  // 时间差一天之外 一周之内
                        result = parseInt(dayValue) + '天前';
                    } else if (hValue >= 1 && hValue <= 24) {
                        result = parseInt(hValue) + '小时前';
                    } else if (mValue >= 1 && mValue <= 60) {
                        result = parseInt(mValue) + '分钟前';
                    } else if (diffValue > 0 && diffValue < mValue) {
                        result = '最新';
                    }
                }
                return result
            },
        },
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值