🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## Mock数据 ## 评分 ~~~ <template> <div class="star" :class="starType"> <span v-for="(itemClass, key, index) in itemClasses" :class="itemClass" class="star-item" v-bind:key="index"></span> </div> </template> <script> const LENGTH = 5 const CLS_ON = 'on' const CLS_HALF = 'half' const CLS_OFF = 'off' export default { props: { size: { type: Number }, score: { type: Number } }, computed: { starType () { return 'star-' + this.size }, itemClasses () { let result = [] let score = Math.floor(this.score * 2) / 2 let hasDecimal = score % 1 !== 0 let integer = Math.floor(score) // 根据分数整数大小,将CLS_ON放入数组 for (let i = 0; i < integer; i++) { result.push(CLS_ON) } // 若包含小数,将CLS_HALF放入数组 if (hasDecimal) { result.push(CLS_HALF) } // 用CLS_OFF填充剩余空位 while (result.length < LENGTH) { result.push(CLS_OFF) } return result } } } </script> ~~~ ### 向下取0.5倍数 ``` let score = Math.floor(this.score * 2) / 2 ``` ### 判断是否有小数 ~~~ score % 1 !== 0 ~~~ ## 动画 ~~~ <template> <transition name="fade"> .... </transition> </template> <style> &.fade-enter-active, &.fade-leave-active { transition: all .5s; } &.fade-enter, &.fade-leave-active { opacity: 0; background: rgba(7, 17, 27, 0); } </style> ~~~ ~~~ <transition name="move"> <div class="cart-decrease" v-show="food.count>0" @click.stop.prevent="decreaseCart"> <span class="inner icon-remove_circle_outline"></span> </div> </transition> <style> &.move-enter-active, &.move-leave-active { transition: all 0.4s linear; } &.move-enter, &.move-leave-active { opacity: 0; transform: translate3d(24px, 0, 0); .inner { transform: rotate(180deg); } } </style> ~~~ ## 元素置底 ~~~ <template> <div class="detail"> <div class="detail-wrapper clearfix"> <div class="detail-main"> </div> </div> <div class="detail-close"></div> </div> </template> <style lang="scss" scoped> .detail { position: fixed; z-index: 100; top: 0; left: 0; width: 100%; height: 100%; overflow: auto; backdrop-filter: blur(10px); opacity: 1; background: rgba(7, 17, 27, 0.8); .detail-wrapper { width: 100%; min-height: 100%; .detail-main { margin-top: 64px; padding-bottom: 64px; } .detail-close { width: 32px; height: 32px; margin: -64px auto 0 auto; clear: both; font-size: 32px; } } } </style> ~~~ ![](https://box.kancloud.cn/f801ea322c9ef52b43b1ed3b57df806d_373x665.png) ## 属性添加 data中对象没有的属性,要使用Vue.set添加,才能对属性进行数据绑定 ~~~ if (!this.food.count) { Vue.set(this.food, 'count', 1) } else { this.food.count++ } ~~~ ## 方法定义 父组件可以调用子组件方法,子组件不能调用父组件方法。 ~~~ <template> <food :food="selectedFood" ref="food"></food> </template> <script> selectFood (food, event) { this.selectedFood = food this.$refs.food.show() // 获取子组件 } </script> ~~~ 定义方法时,一般内部方法命名时前面加下划线 ~~~ methods : { selectFood () { }, _drop () { } } ~~~ ## 组件点击事件穿透 ~~~ @click.stop.prevent ~~~ ## 元素隐藏后要执行动画(计算元素位置),可将其隐藏也改成transition ## 子组件获取父组件请求接口的数据并调用Better-scroll 在watch中监听获取到的接口数据,在nextTick里初始化 mounted 中也要初始化,防止DOM重新渲染后没有执行。 ~~~ <script> import BScroll from 'better-scroll' export default { mounted () { this.$nextTick(() => { this._initScroll() }) }, props: { seller: { type: Object } }, watch: { 'seller' () { this.$nextTick(() => { this._initScroll() }) } }, methods: { _initScroll () { if (!this.scroll) { this.scroll = new BScroll(this.$refs.seller, { click: true }) } else { this.scroll.refresh() } } } } </script> ~~~ ## data属性扩展 ~~~ created () { this.$http.get('/api/seller?id=' + this.seller.id).then((res) => { res = res.data if (res.errno === ERR_OK) { this.seller = Object.assign({}, this.seller, res.data) } }) }, data () { return { seller: { id: (() => { let queryParam = urlParse() return queryParam.id })() } } }, ~~~ ## 离场动画 转场需要一点时间去完成这系列出场动画,(否则下一个进来的页面就会立刻出现,动画会中止或覆盖) ~~~ <template> <transition name="slide"> <div class="head-field" v-show="appear"> <span class="head-field-pic"> <span class="img-hover" @click.stop="uploadHeadImg"> <img :src="userinfo.headUrl" v-autofix/> </span> </span> </div> </transition> </template> <script> data () { return { appear: false } }, mounted () { this.$nextTick(() => { this.appear = true }) }, beforeRouteLeave (to, from, next) { this.appear = false setTimeout(() => { next() }, 800) } </script> <style lang="scss"> .slide-enter-active, .slide-leave-active { transform: translateY(0); transition: transform 1s; } .slide-enter, .slide-leave-to/* .fade-leave-active in below version 2.1.8 */ { transform: translateY(-50px); } </style> ~~~ ## 通知子组件通知父组件 用于模拟dispatch, 找到父组件, 并且触发父组件方法(无需多次在父组件绑定自定义事件 ) ~~~ // dispatch.js export default { methods: { dispatch (componentName, eventName, params = []) { let parent = this.$parent || this.$root let name = parent.$options._componentTag while (parent && (!name || name !== componentName)) { parent = parent.$parent if (parent) { name = parent.$options._componentTag } } if (parent) { parent.$emit(eventName, params) } } } } ~~~ ~~~ import dispatch from '../../utils/dispatch' export default { name: 'menu-item', mixins: [dispatch], props: { route: { type: String, default: ' ' } }, methods: { handleRoute () { if (this.route) { this.dispatch('my-menu', 'closeByRoute') } } } } ~~~