【VUE】仿豆瓣(二)创建公共组件

公共组件都存放在components目录下,项目地址是:GitHub - Ercyao/VUE-douban: 仿豆瓣vue项目

头部效果图

head.jpg
search.jpg

头部组件header.vue,通过v-if判断头部导航栏或搜索界面的显示和隐藏,input文本框的@keyup.enter是监听键盘回车事件

<template>
    <div>
        <template v-if="isShow">
        <div id="header-nav">
        <router-link :to="{name:'Home'}" ><img src="../assets/img/title.png" class="title"/></router-link>
        <ul class="nav" >
            <li @click="gotoAddress({name:'Movie'})" style="color: #2384E8;">电影</li>
            <li @click="gotoAddress({name:'Books'})" style="color: #9F7860;">图书</li>
            <li @click="gotoAddress({name:'Broadcast'})" style="color: #E4A813;">广播</li>
            <li @click="gotoAddress({name:'Group'})" style="color: #2AB8CC;">小组</li>
        </ul>
        <span class="search" @click="showSearch"></span>
        </div>
        </template>
        <template v-else>
        <div class="search_box">
            <div class="search_input">
              <input type="text" v-model.trim="keyword" placeholder="请输入关键字" @keyup.enter="goSearch()"/>
              <span class="close"  @click="closeSearch()">关闭</span>
            </div>
            <ul class="search_class" >
              <li v-for="item in SearchClass">
                <a :href="item.url">
                    <p class="name" :style="{color:item.color}">{{item.name}}</p>
                    <p class="des">{{item.des}}</p>
                </a>
              </li>
            </ul>
        </div>
        </template>
    </div>
</template>

<script>
import {getSearchClass} from '@/store/API'
export default {
    data () {
      return {
        keyword:'',
        isShow:true,
        SearchClass:null,
      }
     },
    created(){
        this.getSearchClass();
    },
    methods:{
        async getSearchClass(){
            this.SearchClass = await getSearchClass().then(res => res.json());
        },
        gotoAddress(name){
            this.$router.push(name)
        },
        showSearch(){
            this.isShow = false;
        },
        closeSearch(){
            this.isShow = true;
        },
        goSearch(){
            this.$router.push({path:'/search',query:{q:this.keyword}})
        }
    } 
}
</script>

<style lang="less" scoped>
@import '../assets/style/less.less';
#header-nav{
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    z-index: 2;
    max-width: 650px;
    margin: 0 auto;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-align: center;
    align-items: center;
    height: 3.5rem;
    padding: 0 18px;
    background: #fff;
    border-bottom: .1rem solid #f3f3f3;
    .title{
        -ms-flex: 1;
        flex: 1;
        max-width: 3.5rem;
        word-break: break-all;
    }
    .nav{
        display: -ms-flexbox;
        display: flex;
        -ms-flex: 1;
        flex: 1;
        -ms-flex-pack: end;
        justify-content: flex-end;
        li{
            display: inline-block;
            font-size: 1.1rem;
            margin-right: 1rem;
        }
    }
    .search{
        display: inline-block;
        width: 2rem;
        height: 1.5rem;
        margin-top: .4rem;
        background: url(../assets/img/search.png) no-repeat;
        background-size: cover;
    }
}

.search_box{
    position: fixed;
    top: 0;
    left: 0;
    z-index: 999;
    width: 100%;
    height: 100%;
    background: #FFFFFF;
    .search_input{
        width: 100%;
        height: 2.5rem;
        padding: 15px;
        border-bottom: 1px solid #f3f3f3;
        input{
            float: left;
            width: 75%;
            height: 2.5rem;
            font-size: 14px;
            background: #F1F1F1;
            margin-right: 10px;
            border: 0;
            border-radius: 5px;
            padding:0 10px;
        }
        .close{
            color: #42bd56;
            font-size: 16px;
            margin-top: 5px;
        }
    }
    .search_class{
        margin: 20px 0;
        li{
            float: left;
            width: 20%;
            height: 4rem;
            margin: 10px 6%;
            text-align: center;
            .name{
                font-weight: 400;
                font-size: 24px;
                line-height: 28px;
            }
            .des{
                color: #999;
                height: 17px;
                font-size: 12px;
                letter-spacing: 1px;
            }
        }
    }
}
</style>

底部效果图

drownapp.jpg

底部组件drownapp.vue

<template>
    <div id="drown-app">
        <div class="logo">
            <div class="left"><img src="../assets/img/logo.png" /></div>
            <div class="right"><p>豆瓣</p><p>我们的精神角落</p></div>
        </div>
        <a href="https://www.douban.com/doubanapp/card/log?category=group_home&cid=&action=click_download&ref=http%3A//www.douban.com/doubanapp/app%3Fchannel%3Dcard_group_home%26direct_dl%3D1">去App Store 免费下载IOS客户端 </a>
        
    </div>
</template>

<script>

export default {
    data () {
     return {
      }
  },
    methods:{
        gotoAddress(name){
            this.$router.push(name)
        }
    }   
}
</script>

<style lang="less" scoped>
@import '../assets/style/less.less';
#drown-app{
    margin:3rem 0 2rem;
    .logo{
        width:11rem;
        margin:0 auto;
        display: flex;
        -webkit-justify-content: space-between;
        .left{
            img{
                width:3.5rem
            }
        }
        .right{
            p:first-child{font-size: 1.6rem;font-weight: normal;}
        }
    }
    a{display:block;width:100%;text-align: center;color: @green;margin: 1rem 0;}
}

</style>

加载中的效果图

load.jpg

等待加载组件loading.vue

 <template>
    <div class="loading_container">
      <div class="file"></div>
      <span>Loading</span>
    </div>
</template>
<script>
    export default {
        data(){
            return{
            }
       }
    }
</script>

<style lang="less" scoped>
.loading_container{
    position: absolute;
    z-index: 99;
    top: 40%;
    left: 40%;
    width: 80px;
    height: 80px;
    border: 1px solid #5CB85C;
    border-radius: 50%;
    background:#5CB85C;
    overflow: hidden;
    span{
        position: absolute;
        top: 30px;
        z-index: 999;
        width: 100%;
        text-align: center;
    }
    .file{
        position: absolute;
        top: -100px;
        left: -40px;
        width: 160px;
        height: 160px;
        border-radius: 40%;
        background:#fff;
        animation: file 3s ease infinite;
    }
    @keyframes file{
        0% {transform: translate(0%, 10px) rotateZ(0deg);}
        50% {transform: translate(-10%, -10px) rotateZ(180deg);}
        100% {transform: translate(5%, 10px) rotateZ(360deg);}
    }
}

</style>

columBox的效果图

imgCover.jpg
txtCover.jpg

电影页和图书页引用的公共组件column.vue

<template>
    <div class="column-box">
        <p class="title"><span>{{title}}</span><span><a href="#">更多</a></span></p>
        <slot name="promItem"></slot>
        <ul class="ImgCover" v-if="type === 'ImgCover'">
            <li v-for="item in items">
                <a :href="item.alt">
                    <img :src="item.cover.url" />
                    <p class="name">{{item.title}}</p>
                    <rating v-if="item.rating" :rating="item.rating.value"></rating>
                    <p v-else style="color: #AAAAAA;">¥{{item.price}}</p>
                </a>
            </li>
        </ul>
        <ul class="TextCover" v-if="type === 'TextCover'">
            <template  v-for="item in items">
                <li v-if="item.name"><a :href="item.url" :style="{color:item.color,borderColor:item.color}"> {{item.name}}</a></li>
                <li v-else class="line"></li>
            </template>
        </ul>
    </div>
</template>

<script>
    import Rating  from './rating'

    export default{
        props: ['title', 'type', 'items'],
        data(){
            return {
            }
        },
        components: { Rating },
    }
</script>

<style lang="less" scoped>
    @import '../assets/style/less.less';
   
    .column-box{
        margin:1rem 0 1.5rem;
        .title{
            display: flex;
            justify-content: space-between;
            span:first-child{
                font-size: 1.2rem;
            }
        }
        .ImgCover{
            .name{
                margin-top: .7rem;
                line-height: 1.1rem;
                font-size: 1.1rem;
                color: #111;
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
            }
            li {
                display: inline-block;
                width: 7.5rem;
                padding-right: .5rem;
                img{
                    height: 12rem;
                }
            }
        }
        ul{
            width: 106%;
            padding: .8rem 0;
            overflow-x: auto;
            white-space: nowrap;
             -webkit-overflow-scrolling: touch;
            
        } 
        .TextCover{
            .line {
                width: 100%;
                display: block;
                height: 1px;
                border: 0;
                margin: 0;
            }
            li{
                margin: 0 8px 8px 0;
                font-size: .94rem;
                display: inline-block;
                a {
                    height: 50px;
                    line-height: 50px;
                    padding: 0 1.55rem;
                    letter-spacing: .1em;
                    overflow: auto;
                    display: block;
                    text-align: center;
                    border-radius: .25rem;
                     border: solid 1px;
                }
            }
        } 
    }
</style>

星级评分rating.vue,根据评分实现星级

<template>
<div class="rating">
    <div v-if="rating == 0">
      <span>暂无评分</span>
    </div>
    <div v-else>
      <template v-for="n in full">
        <span class="star star-full"></span>
      </template>
      <template v-for="n in gray">
        <span class="star star-gray"></span>
      </template>
      <span class="value">{{rating}}</span>
    </div>
</div>
</template>

<script>
    export default {
        props: ['rating'],
        data () {
            return {
              full: 0,
              gray: 0
            }
        },
        created () {
            let value = this.rating;
            this.full = parseInt(value / 2);
            this.gray = 5 - this.full;
        }
    }
</script>

<style lang="less" scoped>
.rating{
    margin: 5px 0;
    span{display: inline-block;}
}
.star{
    margin: 0 -3px;
    border-style: solid;
    border-top-width: 5px;
    border-right-width: 10px;
    border-left-width: 10px;
    height: 0;
    position: relative;
    width: 0;
    transform: scale(0.7);
}
.star:before,
.star:after {
    border-style: solid;
    border-top-width: 5px;
    border-right-width: 10px;
    border-left-width: 10px;
    content: '';
    display: block;
    height: 0;
    left: -10px;
    position: absolute;
    top: -5px;
    width: 0;
}
.star:before {
    transform: rotate(70deg);
}
.star:after {
    transform: rotate(-70deg);
}

.star-full {
  border-color: #fd4 transparent transparent transparent;
}
.star-full:before,
.star-full:after {
    border-color: #fd4 transparent transparent transparent;
}
.star-gray {
  border-color: #C0C0C0 transparent transparent transparent;
}
.star-gray:before,
.star-gray:after {
    border-color: #C0C0C0 transparent transparent transparent;
}

</style>

分类浏览的效果图

分类

分类浏览组件category.vue

<template>
    <div class="category-box">
        <p class="title">分类浏览</p>
        <ul>
            <li v-for="item in items"><a :href="item.url">{{item.name}}<span class="arrow"></span></a></li>
        </ul>
    </div>
</template>

<script>
    export default {
         props: ['items'],
        data () {
            return {
            }
        }
    }
</script>

<style lang="less" scoped>
    @import '../assets/style/less.less';
    .category-box{
        .title{font-size:1.2rem;}
        width: 110%;
        ul{
            margin-top: 1.6rem;
            overflow: hidden;
            .border(.1rem,0,0,0);
            li{
                float: left;
                box-sizing: border-box;
                width: 50%;
                padding-right: 1.5rem;
                height: 3rem;
                line-height: 3rem;
                font-size: 1.1rem;
                .border(0,.1rem,0,.1rem);
                a{color: @green;padding-left: .5rem;}
            }
            .arrow{
                float:right;
                width: .5rem;
                height: .5rem;
                margin-top: 1rem;
                .border(.2rem,0,0,.2rem);
                transform: rotate(45deg);
            }
        }
    }
</style>

小组和搜索结果的效果图

小组

搜索结果

小组和搜索结果引用的公共组件team.vue

<template>
    <div class="team-box">
        <p class="title">{{title}}</p>
        <div  v-if="type === 'groupsCover'">
        <ul class="content">
            <li v-for="item in items">
                <a :href="item.url">
                    <p class="avatar"><img :src="item.avatar" class="img"/><span class="name">{{item.name}}</span><span class="member">{{item.member_count}}人</span></p>
                    <p class="des">{{item.desc_abstract}}</p>
                </a>
            </li>
        </ul>
        <a href="#" class="more_group">更多相关小组</a>
        </div>
        <div v-if="type === 'searchCover'">
        <ul class="content">
            <li v-for="item in items">
                <a :href="item.alt">
                    <div class="search_avatar">
                        <template v-if="item.image">
                        <img :src="item.image" class="img"/>
                        </template>
                        <template v-else>
                        <img :src="item.images.small" class="img"/>
                        </template>
                        <div class="search_des">
                            <p class="title">{{item.title}}</p>
                            <rating v-if="item.rating" class="price" :rating="item.rating.average"></rating>
                        </div>
                    </div>
                </a>
            </li>
        </ul>
        <a href="#" class="more_group" v-if="items">查看更多结果</a>
        <a href="#" class="no_group" v-if="!items">暂无结果</a>
        </div>
    </div>
</template>

<script>
    import Rating  from './rating'
    
    export default{
        props: ['title','type','items'],
        data(){
            return {}
        },
        components: { Rating },
    }
</script>

<style lang="less" scoped>
@import '../assets/style/less.less';
.team-box{
    padding: 15px 0 30px;
    .title{
        font-size: 18px;
        color: #111;
    }
    .content{
        li{.border(0, 0.1rem, 0, 0)}
        a{
            font-size: 18px;
            display: block;
            padding: 15px 0;
            color: #111;
            .avatar{height: 3rem;line-height: 3rem;}
            .img{   
                border-radius: 3px;
                width: 40px;
                height: 40px;
                border: 1px solid #f1f1f1;
                vertical-align: baseline;
            }
            .name{
                -webkit-box-flex: 1;
                -webkit-flex: 1;
                -ms-flex: 1;
                flex: 1;
                width: 60%;
                padding-left: 10px;
                padding-right: 10px;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            .member{
                float: right;
                color: #CCCCCC;
                font-size: 1rem;
            }
            .des{
                margin-top: 10px;
                font-size: 15px;
                color: #aaa;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                .search_img{
                    width: 40px;
                    height: 40px;
                }
                .search_text{
                    
                }
            }
        }
    }
    .search_des{
        display: inline-block;
        margin-left: 10px;
        width: 80%;
        overflow: hidden;
        .title{margin-bottom: 5px;}
        .price{
            color: #aaa;
            font-size: 12px;
            height: 14px;
            margin: 0;
        }
    }
}
.more_group{
    display: block;
    width: 100%;
    height: 3.2rem;
    line-height: 3.2rem;
    font-size: 1.2rem;
    text-align: center;
    color: @green;
    .border(0, 0.1rem, 0, 0)
}
.no_group{
    display: block;
    width: 100%;
    height: 3.2rem;
    line-height: 3.2rem;
    font-size: 1.1rem;
    text-align: center;
    color: #AAAAAA;
    .border(0, 0.1rem, 0, 0)
}
</style>

VUE仿豆瓣(一)跨域配置、路由配置以及API封装
VUE仿豆瓣(二)创建公共组件
VUE仿豆瓣(三)最后建立各个页面

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容

  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    小姜先森o0O阅读 9,495评论 0 72
  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    王喂马_阅读 6,455评论 1 77
  • UI组件 element- 饿了么出品的Vue2的web UI工具套件 Vux- 基于Vue和WeUI的组件库 m...
    流觞小菜鸟阅读 1,764评论 2 8
  • 基于Vue的一些资料 内容 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 element★...
    尝了又尝阅读 1,154评论 0 1
  • Vue2.0+组件库总结 UI组件 element - 饿了么出品的Vue2的web UI工具套件 Vux - 基...
    szch阅读 1,977评论 1 52