vuejs2-4 header.vue组件开发
1 简单图解
2 src/App.vue
<template> <div> <v-header :seller="seller"></v-header> <!--路由切换区域--> <div class="tab"> <div class="tab-item"> <router-link to="/goods" :seller="seller">商品</router-link> </div> <div class="tab-item"> <router-link to="/ratings" :seller="seller">评论</router-link> </div> <div class="tab-item"> <router-link to="/seller" :seller="seller">商家</router-link> </div> </div> <!--路由内容区域--> <keep-alive> <router-view></router-view> </keep-alive> </div> </template> <!-- 'router-link' Vuejs2+版本写法 会被解析成<a></a> to="/xxx"指向的路由地址(src/main.js配置的路由一致) 'keep-alive' 组件间切换时保存组件的信息到内存中,切换回来时仍然是之前的状态 'router-view' 路由渲染视图 :seller="seller" 父组件向子组件传递自定义名称的数据 --> <script> import header from 'components/header/header.vue'; // 引入header组件 import {urlParse} from 'common/js/urlParse'; // 引入urlParse.js文件 window.loacation.search操作 const ERR_OK = 0; export default { data() { return { seller: { id: (() => { let queryParam = urlParse(); // 获取地址中的查询字符串 return queryParam.id; })() } }; }, created() { let url = '/api/seller?id=' + this.seller.id; this.$http.get(url).then((response) => { response = response.body; if (response.errno === ERR_OK) { console.log(response); this.seller = Object.assign({}, this.seller, response.data); } }); }, components: { // 注册header组件 'v-header': header } }; </script> <style lang="stylus"> @import "./common/stylus/mixin.styl" .tab display: flex width: 100% height: 40px line-height: 40px border-1px(rgba(7, 17, 27, 0.1)) .tab-item flex: 1 text-align: center & > a display: block font-size: 14px color: rgb(77, 85, 93) &.active color: rgb(240, 20, 20) </style>
3 src/header.vue
<template> <div class="header"> <!-- 商品信息 --> <div class="content-wrapper"> <div class="avatar"> <img alt="" height="64" width="64" :src="seller.avatar"> </div> <div class="content"> <div class="title"> <span class="brand"></span> <span class="name">{{seller.name}}</span> </div> <div class="description"> {{seller.description}}/{{seller.deliveryTime}}分钟送达 </div> <div v-if="seller.supports" class="support"> <span class="icon" :class="classMap[seller.supports[0].type]"></span> <span class="text">{{seller.supports[0].description}}</span> </div> </div> <div v-if="seller.supports" class="support-count" @click="showDetail(1)"> <span class="count">{{seller.supports.length}}个</span> <i class="icon-keyboard_arrow_right"></i> </div> </div> <!-- 公告 --> <div class="bulletin-wrapper" @click="showDetail(1)"> <span class="bulletin-title"></span><span class="bulletin-text">{{seller.bulletin}}</span> <i class="icon-keyboard_arrow_right"></i> </div> <!--背景图片--> <div class="bg"> <img :src="seller.avatar" width="100%" height="100%" alt=""> </div> <!--公告详情--> <!--transition用法变化 css使用变化--> <transition name="fade"> <div v-show="detail_show" class="detail"> <div class="detail-wrapper clearfix"> <div class="detail-main"> <h1 class="name">{{seller.name}}</h1> <div class="star-wrapper"> <star :size="48" :score="seller.score"></star> </div> <div class="title"> <div class="line"></div> <div class="text">优惠信息</div> <div class="line"></div> </div> <ul class="supports" v-if="seller.supports"> <li v-for="(item,index) in seller.supports" class="supports-item"> <span class="icon" :class="classMap[seller.supports[index].type]"></span> <span class="text">{{seller.supports[index].description}}</span> </li> </ul> <div class="title"> <div class="line"></div> <div class="text">商家公告</div> <div class="line"></div> </div> <div class="bulletin">{{seller.bulletin}}</div> </div> </div> <div class="detail-close"> <i class="icon-close" @click="showDetail(0)"></i> </div> </div> </transition> </div> </template> <script type="text/ecmascript-6"> import star from 'components/star/star'; // 引入star组件 export default{ props: { // 获取父组件传递过来的数据seller seller: { type: Object } }, data() { // Vue 实例的数据对象 return { detail_show: false }; }, methods: { // methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。 // 方法中的 this 自动绑定为 Vue 实例 showDetail: function (action) { // 点击显示或隐藏公告详情 if (action) { this.detail_show = true; } else { this.detail_show = false; } } }, created() { // 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置: // 数据观测(data observer),属性和方法的运算, watch/event 事件回调。 // 然而,挂载阶段还没开始,$el 属性目前不可见。 this.classMap = ['decrease', 'discount', 'special', 'invoice', 'guarantee']; }, components: { // 注册组件 1引入2注册,这样组件才能被使用 star } }; </script> <style lang="stylus" rel="stylesheet/stylus"> @import "../../common/stylus/mixin" .header $col = #fff $bg = rgba(7,17,27,0.5) $pad_top = 24px color:$col background:$bg position:relative overflow:hidden .content-wrapper padding:$pad_top 12px 18px $pad_top font-size:0 /*紧贴*/ position:relative .avatar display: inline-block vertical-align:top img border-radius:2px .content margin-left:16px display:inline-block font-size:14px .title margin:2px 0 8px 0 .brand display:inline-block vertical-align:top /*图片与文字顶部对齐*/ width:30px height:18px bg-img("brand") /*按需加载2x1 3x*/ background-size:30px 17px background-repeat:no-repeat .name margin-left:6px font-size:16px color:rgb(255,255,255) line-height:18px font-weight:bold .description margin-bottom:10px font-size:12px; color: rgb(255,255,255) line-height:12px .supports .icon display:inline-block width:12px height:12px margin-right:4px background-size:12px 12px background-repeat: no-repeat vertical-align:top &.decrease bg-img("decrease_1") &.discount bg-img("discount_1") &.invoice bg-img("invoice_1") &.special bg-img("special_1") &.guarantee bg-img("guarantee_1") .text display:inline-block font-size:10px color:rgb(255,255,255) line-height:12px; .support-count position:absolute right:12px bottom:18px height:24px color:rgb(255,255,255) line-height:24px background:rgba(0,0,0,.2) padding:0px 8px text-align:center border-radius:12px .count vertical-align:top font-size:10px .icon-keyboard_arrow_right font-size:10px line-height:24px margin-left:2px .bulletin-wrapper height:28px line-height:28px padding:0 22px 0 12px white-space: nowrap overflow:hidden text-overflow:ellipsis position:relative background-color:rgba(7,17,27,.2) .bulletin-title display:inline-block height:12px width:22px bg-img('bulletin') background-size:22px 12px background-repeat:no-repeat vertical-align:top margin-top:8px .bulletin-text font-size:10px margin:0 4px vertical-align:top .icon-keyboard_arrow_right position:absolute top:8px right:12px font-size:10px .bg position:absolute left:0 top:0 width:100% height:100% z-index:-1 filter: blur(10px) .detail position:fixed left:0 top:0 z-index:100 width:100% height:100% overflow:auto opacity: 1 background: rgba(7, 17, 27, 0.8) backdrop-filter: blur(10px) &.fade-enter-active,&.fade-leave-active transition:all 0.5s &.fade-enter,&.fade-leave-active opacity:0 background:rgba(7,17,27,0) .detail-wrapper min-height:100% .detail-main margin-top:64px padding-bottom:64px .name font-size:16px font-weight:700 color:rgb(255,255,255) line-height:16px text-align:center .star-wrapper margin-top:18px padding:2px 0 text-align:center .title display:flex width:80% margin:30px auto 24px auto .line flex:1 position:relative top: -6px border-bottom: 1px solid rgba(255,255,255,.2) .text padding:0 12px font-weight:700 font-size:14px .supports width:80% margin:0 auto .supports-item padding:0 12px margin-bottom: 12px font-size:0 &:last-child margin-bottom:0 .icon display:inline-block width:16px height:16px margin-right:6px background-size:16px background-repeat:no-repeat vertical-align:top &.decrease bg-img("decrease_2") &.discount bg-img("discount_2") &.invoice bg-img("invoice_2") &.special bg-img("special_2") &.guarantee bg-img("guarantee_2") .text font-size:12px font-weight:200 color: rgb(255,255,255) line-height:16px .bulletin width:80% margin:0 auto font-size:12px font-weight:200 color: rgb(255,255,255) line-height:24px .detail-close position: relative width:32px height:32px margin:-64px auto 0 auto clear:both font-size:32px </style>
4 使用star组件
<!--并传递两个属性size score给star组件--> <star :size="48" :score="seller.score"></star>
5 star组件 src/components/star.vue
<template> <div class="star" :class="starType"> <span v-for="(itemClass,index) in itemClasses" :class="itemClass" class="star-item" v-bind:key="itemClass.index"></span> </div> </template> <!--使用v-for遍历提示加上key区别tra--> <script type="text/ecmascript-6"> const LENGTH = 5; // 假设总共有五位 const CLS_ON = 'on'; // const CLS_HALF = 'half'; const CLS_OFF = 'off'; export default { props: { // 接收header.vue以属性传递过来的数据 size: { type: Number }, score: { type: Number } }, computed: { // 计算属性 starType() { return 'star-' + this.size; }, itemClasses() { let result = []; let score = Math.floor(this.score * 2) / 2; // 保证是整数或是x.5的小数 向下取整(4.9、4.1==>4) let hasDecimal = score % 1 !== 0; let integer = Math.floor(score); // 整数或 x.5小数都取整 for (let i = 0; i < integer; i++) { // 每位添加全星 result.push(CLS_ON); // ['on','on'] }; if (hasDecimal) { // x.5的小数 末尾有星星的位添加一个半心 result.push(CLS_HALF); // ['on','on', 'half'] }; while (result.length < LENGTH) { // 剩下的空星位用空星补齐 result.push(CLS_OFF); // ['on','on', 'half', 'off', 'off', 'off'] }; return result; } } }; </script> <style lang="stylus" rel="stylesheet/stylus"> @import "../../common/stylus/mixin" .star font-size: 0 .star-item display: inline-block background-repeat: no-repeat &.star-48 .star-item width:20px height:20px margin-right:22px background-size: 20px 20px &:last-child margin-right:0 &.on bg-img('star48_on') &.half bg-img('star48_half') &.off bg-img('star48_off') &.star-36 .star-item width:15px height:15px margin-right:8px background-size: 15px 15px &:last-child margin-right:0 &.on bg-img('star36_on') &.half bg-img('star36_half') &.off bg-img('star36_off') &.star-24 .star-item width:10px height:10px margin-right:3px background-size: 10px 10px &:last-child margin-right:0 &.on bg-img('star24_on') &.half bg-img('star24_half') &.off bg-img('star24_off') </style>