Browse Source

优惠券版本页面绘制完成

喻文俊 3 năm trước cách đây
mục cha
commit
54e9d5c409

+ 0 - 7
common/global.component.js

@@ -1,7 +0,0 @@
-import scrollTop from '@/components/cm-module/scrollTop/scrollTop.vue'
-
-const install = Vue => {
-    Vue.component('scroll-top', scrollTop)
-}
-
-export default install

+ 221 - 0
components/cm-module/cm-coupon-list/cm-coupon-list.vue

@@ -0,0 +1,221 @@
+<template>
+    <view class="cm-coupon-list">
+        <view class="mask" v-if="visible"></view>
+        <view class="main-content">
+            <uni-transition mode-class="slide-bottom" :show="visible">
+                <view class="content">
+                    <!-- 关闭弹框按钮 -->
+                    <view class="iconfont icon-iconfontguanbi close" @click="$emit('close')"></view>
+                    <!-- 弹框标题 -->
+                    <view class="title">{{ title }}</view>
+                    <view class="other" v-if="listType !== 'receive'">
+                        <!-- 勾选不使用优惠券 -->
+                        <view class="unset" v-if="listType === 'use'">
+                            <text>不使用优惠券</text> <text class="radio-flag iconfont icon-xuanze"></text>
+                        </view>
+                        <!-- tab切换 -->
+                        <view class="tabs" v-if="listType === 'search'">
+                            <view
+                                class="tab"
+                                :class="{ active: currentTab === index }"
+                                v-for="(tab, index) in tabs"
+                                :key="index"
+                                @click="checkedTab(index)"
+                            >
+                                <text>{{ tab.name }}</text> <text>({{ 1 }})</text>
+                            </view>
+                        </view>
+                    </view>
+                    <view class="list" :class="['scroll-' + listType]">
+                        <!-- 优惠券列表 -->
+                        <cm-coupon :key="i" v-for="i in 6"></cm-coupon>
+                        <!-- 使用优惠券按钮 -->
+                        <view class="btn" @click="confirm" v-if="listType === 'use'">确定</view>
+                        <!-- IPhoneX 以上版本适配 -->
+                        <view :style="{ height: isIphoneX ? '40rpx' : 0 }"></view>
+                    </view>
+                </view>
+            </uni-transition>
+        </view>
+    </view>
+</template>
+
+<script>
+import CmCoupon from '@/components/cm-module/cm-coupon/cm-coupon.vue'
+import { mapGetters } from 'vuex'
+
+export default {
+    name: 'cm-coupon-list',
+    components: {
+        CmCoupon
+    },
+    props: {
+        // 列表标题
+        title: {
+            type: String,
+            default: '优惠券'
+        },
+        // 优惠券列表
+        couponList: {
+            type: Object,
+            default: () => {}
+        },
+        // 显示弹窗
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        // 列表类型 receive 领取 use 使用  search 查看
+        listType: {
+            type: String,
+            default: 'receive'
+        }
+    },
+    data() {
+        return {
+            currentTab: 0,
+            tabs: [
+                {
+                    name: '已领取优惠券'
+                },
+                {
+                    name: '可领取优惠券'
+                }
+            ]
+        }
+    },
+    computed: {
+        ...mapGetters(['isIphoneX'])
+    },
+    methods: {
+        // 确认需要把优惠券对象返回
+        confirm() {
+            this.$emit('confirm')
+        },
+        checkedTab(index) {
+            this.currentTab = index
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+$grid: 24rpx;
+$title-top: 50rpx;
+$title-bottom: 28rpx;
+$title-line-height: 50rpx;
+$other-line-height: 40rpx;
+$tabs-height: 80rpx;
+$main-height: 1028rpx;
+$base-scroll-height: $main-height - $title-top - $title-bottom - $title-line-height;
+.mask {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.5);
+    z-index: 99998;
+}
+.cm-coupon-list {
+    .main-content {
+        width: 750rpx;
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        z-index: 99999;
+        .content {
+            position: relative;
+            width: 100%;
+            height: $main-height;
+            border-radius: 16rpx 16rpx 0px 0px;
+            background: #fff;
+            .close {
+                position: absolute;
+                top: 36rpx;
+                right: 36rpx;
+                font-size: 34rpx;
+                color: #b2b2b2;
+            }
+            .title {
+                padding: $title-top 0 $title-bottom;
+                text-align: center;
+                font-size: 34rpx;
+                font-weight: 500;
+                line-height: $title-line-height;
+                color: #333333;
+            }
+            .other {
+                padding: 0 $grid $grid;
+                line-height: $other-line-height;
+                .unset {
+                    display: flex;
+                    justify-content: space-between;
+                    align-items: center;
+                    padding-right: 30rpx;
+                    font-size: 30rpx;
+                    color: #333333;
+                    .radio-flag {
+                        color: #ff457b;
+                    }
+                }
+                .tabs {
+                    height: $tabs-height;
+                    line-height: $tabs-height;
+                    border-bottom: 1px solid #e1e1e1;
+                    display: flex;
+                    justify-content: space-around;
+                    align-items: center;
+                    .tab {
+                        position: relative;
+                        font-size: 30rpx;
+                        color: #333333;
+                        &::after {
+                            content: '';
+                            position: absolute;
+                            width: 100%;
+                            height: 4rpx;
+                            bottom: 0;
+                            left: 0;
+                            background-color: #fff;
+                        }
+                        &.active {
+                            color: #ff457b;
+                            &::after {
+                                background-color: #ff457b;
+                            }
+                        }
+                    }
+                }
+            }
+            .list {
+                overflow-y: scroll;
+                // 查看优惠券
+                &.scroll-search {
+                    height: $base-scroll-height - $tabs-height;
+                }
+                // 领取优惠券
+                &.scroll-receive {
+                    height: $base-scroll-height;
+                }
+                // 使用优惠券
+                &.scroll-use {
+                    height: $base-scroll-height - $other-line-height - $grid;
+                }
+                .btn {
+                    width: 600rpx;
+                    height: 90rpx;
+                    margin: $grid auto;
+                    background: linear-gradient(90deg, #fc32b4 0%, #f83c6c 100%);
+                    opacity: 1;
+                    border-radius: 45rpx;
+                    line-height: 90rpx;
+                    text-align: center;
+                    font-size: 30rpx;
+                    color: #ffffff;
+                }
+            }
+        }
+    }
+}
+</style>

+ 226 - 0
components/cm-module/cm-coupon/cm-coupon.vue

@@ -0,0 +1,226 @@
+<template>
+    <view class="coupon off">
+        <view class="content cover-bg" :class="[cover]">
+            <view class="header">
+                <view class="tag">优惠券标签</view> <view class="price"><text>¥</text>100</view>
+                <view class="tip">满600可用</view>
+            </view>
+            <view class="center desc">
+                <view class="row bold">618全场满600减100</view> <view class="row">全商城商品通用</view>
+                <view class="end-time">截止日期:2021-10-28</view>
+            </view>
+            <view class="footer">
+                <view class="btn" @click="handleBtnClick">领取</view>
+                <!-- <view class="btn" :class="{ plain: i === 2 }">{{ i === 2 ? '去使用' : '领取' }}</view> -->
+            </view>
+            <!-- <text class="radio-flag iconfont icon-weixuanze"></text> -->
+            <text class="radio-flag iconfont icon-xuanze" v-if="chooseAble" @click="choose"></text>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    props: {
+        // 优惠券数据
+        couponData: {
+            type: Object,
+            default: () => {}
+        },
+        // 设置优惠券是否可选
+        chooseAble: {
+            type: Boolean,
+            default: false
+        },
+        // 显示已领取图标
+        showReceived: {
+            type: Boolean,
+            default: false
+        }
+    },
+    filters: {
+        // 优惠券标签格式化
+        formatTag(val) {
+            const tags = {
+                1: '活动券',
+                2: '用户专享券',
+                3: '新用户券',
+                4: '好友分享券',
+                5: '好友消费券'
+            }
+            return tags[val] || '未知券'
+        }
+    },
+    computed: {
+        cover() {
+            if(!this.couponData) return
+            let name = ''
+            switch (this.couponData.flag) {
+                case 1:
+                    name = 'received'
+                    break
+                case 2:
+                    name = 'expired'
+                    break
+                case 3:
+                    name = 'used'
+                    break
+                default:
+                    name = ''
+                    break
+            }
+            return name
+        }
+    },
+    methods: {
+        // 点击勾选按钮
+        choose() {
+            this.$emit('choose', this.couponData)
+        },
+        // 领取优惠券 TODO
+        receive() {
+            console.log('领取优惠券')
+        },
+        // 按钮点击
+        handleBtnClick(){
+            console.log('领取 or 使用')
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+$grid: 24rpx;
+$color: #ff457b;
+$coupon-width: 702rpx;
+$coupon-height: 200rpx;
+$coupon-bg-on: url(https://static.caimei365.com/app/mini-hehe/icon/icon-coupon-bg-on.png);
+$coupon-bg-off: url(https://static.caimei365.com/app/mini-hehe/icon/icon-coupon-bg-off.png);
+$coupon-bg-received: url(https://static.caimei365.com/app/mini-hehe/icon/icon-coupon-received.png); // 已领取
+$coupon-bg-expired: url(https://static.caimei365.com/app/mini-hehe/icon/icon-coupon-expired.png); // 已失效
+$coupon-bg-used: url(https://static.caimei365.com/app/mini-hehe/icon/icon-coupon-used.png); // 已使用
+.coupon {
+    width: $coupon-width;
+    height: $coupon-height;
+    padding: 6rpx;
+    margin: $grid;
+    box-sizing: border-box;
+    background: #ffffff no-repeat center;
+    background-size: $coupon-width $coupon-height;
+    .content {
+        position: relative;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+    }
+    .cover-bg {
+        background-position: 580rpx 16rpx;
+        background-size: 90rpx 90rpx;
+        background-repeat: no-repeat;
+        &.received {
+            background-image: $coupon-bg-received;
+        }
+        &.expired {
+            background-image: $coupon-bg-expired;
+        }
+        &.used {
+            background-image: $coupon-bg-used;
+        }
+    }
+    &.on {
+        background-image: $coupon-bg-on;
+    }
+    &.off {
+        filter: grayscale(1);
+        opacity: 0.7;
+        background-image: $coupon-bg-off;
+    }
+    .header,
+    .center,
+    .footer {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+    }
+    .header {
+        align-items: center;
+        position: relative;
+        width: 206rpx;
+        height: 188rpx;
+        .tag {
+            position: absolute;
+            top: 0;
+            left: 0;
+            height: 32rpx;
+            padding: 0 6rpx;
+            background: linear-gradient(90deg, #fc32b4 0%, #f83c6c 100%);
+            border-radius: 10rpx 0px 10rpx 0px;
+            font-size: 22rpx;
+            color: #ffffff;
+            text-align: center;
+            line-height: 32rpx;
+        }
+        .price {
+            font-size: 56rpx;
+            font-weight: 600;
+            color: $color;
+            text {
+                font-size: 24rpx;
+            }
+        }
+        .tip {
+            margin-top: 6rpx;
+            font-size: 24rpx;
+            color: #404040;
+        }
+    }
+    .center {
+        margin-left: 36rpx;
+        .row {
+            width: 250rpx;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            font-size: 26rpx;
+            color: #333333;
+            margin-bottom: 16rpx;
+            &.bold {
+                font-weight: 600;
+            }
+        }
+        .end-time {
+            font-size: 20rpx;
+            color: #999999;
+        }
+    }
+    .footer {
+        align-items: center;
+        width: 128rpx;
+        margin-right: $grid;
+        .btn {
+            width: 128rpx;
+            height: 48rpx;
+            background: linear-gradient(270deg, #f83c6c 0%, #fc32b4 100%);
+            border-radius: 28rpx;
+            box-sizing: border-box;
+            border: 1rpx solid transparent;
+            text-align: center;
+            line-height: 46rpx;
+            font-size: 26rpx;
+            color: #ffffff;
+            &.plain {
+                border: 1rpx solid $color;
+                background: transparent;
+                color: $color;
+            }
+        }
+    }
+    .radio-flag {
+        position: absolute;
+        top: $grid;
+        right: $grid;
+        color: $color;
+        font-size: 32rpx;
+    }
+}
+</style>

+ 162 - 0
components/cm-module/cm-drawer/cm-drawer.vue

@@ -0,0 +1,162 @@
+<template>
+    <view class="cm-drawer">
+        <view class="mask" v-if="visible" :style="[offsetSize, { zIndex: zIndex - 1 }]"></view>
+        <view class="drawer-content" :class="[position]" :style="[offsetSize, { zIndex: zIndex }]">
+            <uni-transition :mode-class="modeClass" :show="visible">
+                <view class="content">
+                    <!-- 关闭弹框按钮 -->
+                    <view class="iconfont icon-iconfontguanbi close" @click="$emit('close')"></view>
+                    <view class="title">{{ title }}</view>
+                    <!-- 自定义插槽 -->
+                    <view><slot></slot></view>
+                </view>
+            </uni-transition>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'cm-drawer',
+    props: {
+        title: {
+            type: String,
+            default: 'title'
+        },
+        visible: {
+            type: Boolean,
+            default: false
+        },
+        position: {
+            type: String,
+            default: 'center'
+        },
+        offset: {
+            type: Number,
+            default: 0
+        },
+        zIndex: {
+            type: Number,
+            default: 99999
+        }
+    },
+    computed: {
+        offsetSize() {
+            if (this.position === 'center' || this.offset === 0) {
+                return {}
+            }
+            return {
+                [this.position]: this.offset + 'rpx'
+            }
+        },
+        modeClass() {
+            let name = 'fade'
+            switch (this.position) {
+                case 'center':
+                    name = 'fade'
+                    break
+                case 'top':
+                    name = 'slide-top'
+                    break
+                case 'right':
+                    name = 'slide-right'
+                    break
+                case 'bottom':
+                    name = 'slide-bottom'
+                    break
+                case 'left':
+                    name = 'slide-left'
+                    break
+                default:
+                    name = 'fade'
+                    break
+            }
+            return name
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+$drawer-size: 100rpx;
+
+.mask {
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background: rgba(0, 0, 0, 0.5);
+}
+.cm-drawer {
+    .drawer-content {
+        position: fixed;
+        &.bottom {
+            left: 0;
+            bottom: 0;
+            .content {
+                border-radius: 16rpx 16rpx 0 0;
+            }
+        }
+        &.top {
+            top: 0;
+            left: 0;
+            .content {
+                border-radius: 0 0 16rpx 16rpx;
+            }
+        }
+        &.right {
+            top: 0;
+            right: 0;
+            .content {
+                border-radius: 16rpx 0 16rpx 0;
+            }
+        }
+        &.left {
+            top: 0;
+            left: 0;
+            .content {
+                border-radius: 0 16rpx 16rpx 0;
+            }
+        }
+        &.center {
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            .content {
+                width: 702rpx;
+                min-height: 600rpx;
+                max-height: 800rpx;
+                overflow-y: scroll;
+                border-radius: 16rpx;
+            }
+        }
+        .content {
+            position: relative;
+            width: 750rpx;
+            min-height: 480rpx;
+            background: #fff;
+            box-sizing: border-box;
+            padding: 24rpx;
+            .close {
+                position: absolute;
+                top: 36rpx;
+                right: 36rpx;
+                font-size: 34rpx;
+                color: #b2b2b2;
+            }
+            .title {
+                width: 100%;
+                word-break: break-all;
+                overflow: hidden;
+                line-height: 50rpx;
+                height: 50rpx;
+                text-overflow: ellipsis;
+                text-align: center;
+                font-size: 34rpx;
+                color: #333333;
+            }
+        }
+    }
+}
+</style>

+ 56 - 0
components/cm-module/cm-empty/cm-empty.vue

@@ -0,0 +1,56 @@
+<template>
+    <view class="cm-empty" :style="[offsetValue]">
+        <image :src="image" mode="widthFix"></image> <text>{{ message }}</text>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'cm-empty',
+    props: {
+        message: {
+            type: String,
+            default: ''
+        },
+        image: {
+            type: String,
+            default: ''
+        },
+        offset: {
+            type: [String, Number],
+            default: 0
+        }
+    },
+    computed: {
+        offsetValue() {
+            if (typeof this.offset === 'string') {
+                return {
+                    transform: `translateY(${this.offset})`
+                }
+            }else{
+                return {
+                    transform: `translateY(${this.offset}%)`
+                }
+            }
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.cm-empty {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-direction: column;
+    image {
+        width: 50%;
+    }
+    text {
+        font-size: 26rpx;
+        color: #999999;
+    }
+}
+</style>

+ 19 - 0
components/cm-module/cm-tabs/cm-tabs.vue

@@ -0,0 +1,19 @@
+<template>
+    <view class="cm-tabs"> 
+        <view class=""></view>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'cm-tabs',
+    props: {
+        data: {
+            type: Array,
+            default: () => []
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 1 - 2
components/cm-module/createOrder/address.vue

@@ -33,7 +33,7 @@
 
 <script>
 	export default{
-		name:"address",
+		name:'address',
 		props:{
 			addressData:{
 				type:Object
@@ -89,7 +89,6 @@
 	.address-section {
 		width: 100%;
 		height: auto;
-		float: left;
 		position: relative;
 		
 		.address-empty{

+ 4 - 5
components/cm-module/createOrder/goodsList.vue

@@ -55,7 +55,7 @@
 
 <script>
 	export default{
-		name:"goods",
+		name:'goods',
 		props:{
 			goodsData:{
 				type:Array
@@ -71,7 +71,7 @@
 		},
 		filters:{
 			NumFormat(value) {//处理金额
-				return Number(value).toFixed(2);
+				return Number(value).toFixed(2)
 			},
 		},	
 		watch: {
@@ -107,13 +107,12 @@
 <style lang="scss">
 	.goods-template{
 		width: 100%;
-		height: auto;
+		// height: auto;
 		background: #FFFFFF;
-		float: left;
 		margin-top: 24rpx;
 		.goods-list{
 			width: 100%;
-			height: auto;	
+			// height: auto;	
 			background:#F7F7F7;
 			.goods-item{
 				width: 702rpx;

+ 28 - 29
components/cm-module/createOrder/sellerFreight.vue

@@ -46,7 +46,7 @@
 
 <script>
 	export default{
-		name:"freight",
+		name:'freight',
 		props:{
 			freightDatas:{
 				type:Object
@@ -78,40 +78,40 @@
 				// console.log(data)
 				switch(data.freePostFlag){
 					case 1:
-						this.current = 0;
-						this.freightData.freePostFlag = data.freePostFlag;
+						this.current = 0
+						this.freightData.freePostFlag = data.freePostFlag
 						if(data.freight == 0){
 							this.hanldFreight = ''
 						}else{
 							this.hanldFreight = data.freight
 						}
-						this.freightData.freight = data.freight;
-						this.freightMoney = this.hanldFreight;
+						this.freightData.freight = data.freight
+						this.freightMoney = this.hanldFreight
 						this.freightText = ''
-						break;
+						break
 					case 0:
-						this.current = 1;
-						this.freightData.freePostFlag = data.freePostFlag;
+						this.current = 1
+						this.freightData.freePostFlag = data.freePostFlag
 						this.freightText = '包邮'
-						break;
+						break
 					case -1:
-						this.current = 2;
-						this.freightData.freePostFlag = data.freePostFlag;
+						this.current = 2
+						this.freightData.freePostFlag = data.freePostFlag
 						this.freightText = '到付'
-						break;
+						break
 				}
 			},
 			freightConfim(){//提交完成运费选择
 				switch(this.freightData.freePostFlag){
 					case 1:
 						this.choiceaFreightFirst(this.freightData.freePostFlag)
-						break;
+						break
 					case 0:
 						this.choiceaFreightFirst(this.freightData.freePostFlag)
-						break;
+						break
 					case -1:
 						this.choiceaFreightFirst(this.freightData.freePostFlag)
-						break;	
+						break	
 				}		
 			},
 			choiceaFreightFirst(index){//校验运费形式及运费价
@@ -129,35 +129,35 @@
 				this.hideSpec()
 			},
 			showTip(){//显示运费弹窗
-				this.$emit('showFreightAlert');
+				this.$emit('showFreightAlert')
 			},
 			hideSpec() {//关闭选择数量确认弹窗
-				this.specClass = 'hide';
+				this.specClass = 'hide'
 				setTimeout(() => {
-					this.specClass = 'none';
-				}, 200);
+					this.specClass = 'none'
+				}, 200)
 			},
 			hanldOperationConfim(data){//显示选择数量确认弹窗
 				// this.specClass = 'show';
 				// this.freightMoney = this.hanldFreight
 			},
 			radioChange(e) {//运费选择切换
-				this.freightData.freePostFlag = parseInt(e.target.value);
+				this.freightData.freePostFlag = parseInt(e.target.value)
 				switch(this.freightData.freePostFlag){
 					case 1:
 						this.freightText = '不包邮'
-						break;
+						break
 					case 0:
 						this.freightText = '包邮'
-						break;
+						break
 					case -1:
 						this.freightText = '到付'
-						break;	
+						break	
 				}		
 				for (let i = 0; i < this.freightList.length; i++) {
 					if (this.freightList[i].value === this.freePostFlag) {
-						this.current = i;
-						break;
+						this.current = i
+						break
 					}
 				}
 			},
@@ -171,8 +171,8 @@
 				}
 			},
 			orderPriceToFixed (num){
-				let price ='';
-				price = parseInt(num).toFixed(2);
+				let price =''
+				price = parseInt(num).toFixed(2)
 				return price
 			},
 			discard(){
@@ -187,8 +187,7 @@
 		width: 100%;
 		height: auto;
 		background: #FFFFFF;
-		float: left;
-		margin: 24rpx 0 34rpx 0;
+		margin: 24rpx 0 24rpx 0;
 		.invoice-freight{
 			width: 702rpx;
 			padding: 0 24rpx;

+ 2 - 2
components/cm-module/scrollTop/scrollTop.vue

@@ -11,7 +11,7 @@
 
 <script>
 	export default{
-		name:'scrollTop',
+		name:'ScrollTop',
 		props:{
 			bottom:{
 				type:Number,
@@ -41,7 +41,7 @@
 				uni.pageScrollTo({
 				    scrollTop: 0,
 				    duration: 600
-				});
+				})
 			},
 			handleContact(e){
 				console.log(e.detail.path)

+ 84 - 0
components/message-popup/message-popup.vue

@@ -0,0 +1,84 @@
+<template>
+    <view class="message-popup">
+        <view class="mask" v-if="mask && visible"></view>
+        <view class="content-popup">
+            <uni-transition mode-class="fade" :show="visible">
+                <view class="popup">
+                    <view class="title"> {{ title }}<slot name="title"></slot> </view>
+                    <view class="content">{{ content }} <slot name="content"></slot> </view>
+                    <text class="close iconfont icon-iconfontguanbi" @click="$emit('close')"></text>
+                </view>
+            </uni-transition>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'message-popup',
+    props: {
+        title: {
+            type: String,
+            default: ''
+        },
+        content: {
+            type: String,
+            default: ''
+        },
+        mask: {
+            type: Boolean,
+            default: true
+        },
+        visible: {
+            type: Boolean,
+            default: false
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.message-popup {
+    .mask {
+        position: fixed;
+        width: 100%;
+        height: 100%;
+        top: 0;
+        left: 0;
+        z-index: 9998;
+        background: rgba(0, 0, 0, 0.6);
+    }
+    .content-popup{
+        position: fixed;
+        z-index: 9999;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%,-70%);
+    }
+    .popup {
+        width: 580rpx;
+        // min-height: 540rpx;
+        box-sizing: border-box;
+        padding: 60rpx 26rpx;
+        background: #fff;
+        border-radius: 16rpx;
+        .close {
+            position: absolute;
+            right: 24rpx;
+            top: 24rpx;
+            font-size: 32rpx;
+            color: #d4dd4;
+        }
+        .title {
+            text-align: center;
+            font-size: 30rpx;
+            color: #333333;
+            margin-bottom: 54rpx;
+        }
+        .content {
+            font-size: 26rpx;
+            color: #666666;
+        }
+    }
+}
+</style>

+ 151 - 0
components/sui-search/sui-search.vue

@@ -0,0 +1,151 @@
+<template>
+    <!-- 搜索框 -->
+    <view class="search-input-box dis-flex" :style="{ background: bgColor }">
+        <view
+            class="search-input dis-flex flex-y-center"
+            @tap="gosearch"
+            :style="{ background: background, borderRadius: radius }"
+        >
+            <text class="iconfont icon-sousuo col-9"></text>
+            <input
+                :disabled="disabled"
+                v-model="keyword"
+                @blur="search"
+                :focus="focus"
+                :placeholder="placeholder"
+                :placeholderStyle="placeStyle"
+                type="text"
+            />
+            <text class="iconfont icon-iconfontguanbi clear" v-if="showClose" @click="clearKeyword"></text>
+            <slot name="right"></slot>
+        </view>
+    </view>
+</template>
+
+<script>
+export default {
+    name: 'search',
+    props: {
+        // 是否禁止输入
+        disabled: {
+            type: Boolean,
+            default: false
+        },
+        // 是否自动聚焦
+        focus: {
+            type: Boolean,
+            default: false
+        },
+        // 输入框值
+        value: {
+            type: [Number, String],
+            default: ''
+        },
+        // 搜索栏Placeholder
+        placeholder: {
+            type: String,
+            default: ''
+        },
+        // 搜索栏Placeholder样式
+        placeStyle: {
+            type: String,
+            default: 'color:#999;font-size:24rpx;'
+        },
+        // 输入框背景颜色
+        background: {
+            type: String,
+            default: '#f7f7f7'
+        },
+        // 搜索栏圆角
+        radius: {
+            type: [Number, String]
+        },
+        bgColor: {
+            type: String,
+            default: '#fff'
+        },
+        clearable: {
+            type: Boolean,
+            default: true
+        }
+    },
+    watch: {
+        value: {
+            immediate: true,
+            handler(newVal) {
+                this.keyword = newVal
+            }
+        }
+    },
+    computed: {
+        showClose() {
+            return this.clearable && this.keyword
+        }
+    },
+    data() {
+        return {
+            keyword: ''
+        }
+    },
+    methods: {
+        // input是否禁止输入时触发事件
+        gosearch() {
+            this.$emit('gosearch', this.keyword)
+        },
+        // input失去焦点时触发事件
+        search() {
+            this.$emit('search', this.keyword)
+        },
+        clearKeyword() {
+            this.keyword = ''
+            this.$emit('clear')
+        }
+    }
+}
+</script>
+
+<style lang="scss">
+/* 搜索框 */
+$searchbar-height: 60rpx;
+.dis-flex {
+    display: flex;
+}
+.flex-y-center {
+    align-items: center;
+}
+.col-9 {
+    color: #999;
+    margin-right: 8px;
+}
+.search-input-box {
+    height: $searchbar-height;
+    padding: 32rpx 24rpx;
+    overflow: hidden;
+}
+.search-input {
+    width: 100%;
+    background: #f5f5f5;
+    border-radius: 30rpx;
+    padding: 0 26rpx;
+    text-align: left;
+    box-sizing: border-box;
+    overflow: hidden;
+    .icon {
+        margin-right: 24rpx;
+    }
+}
+.search-input input {
+    flex: 1;
+    font-size: 24rpx;
+    height: $searchbar-height;
+    line-height: $searchbar-height;
+}
+.clear {
+    width: 48rpx;
+    height: 48rpx;
+    color: #999;
+    font-size: 24rpx;
+    line-height: 48rpx;
+    text-align: center;
+}
+</style>

+ 1 - 1
components/thorui/tui-tabs/tui-tabs.vue

@@ -170,7 +170,7 @@
 		box-sizing: border-box;
 		display: flex;
 		align-items: center;
-		justify-content: space-between;
+		justify-content: space-around;
 		z-index: 9999;
 	}
 

+ 128 - 0
components/uni-components/uni-transition/createAnimation.js

@@ -0,0 +1,128 @@
+// const defaultOption = {
+// 	duration: 300,
+// 	timingFunction: 'linear',
+// 	delay: 0,
+// 	transformOrigin: '50% 50% 0'
+// }
+// #ifdef APP-NVUE
+const nvueAnimation = uni.requireNativePlugin('animation')
+// #endif
+class MPAnimation {
+	constructor(options, _this) {
+		this.options = options
+		this.animation = uni.createAnimation(options)
+		this.currentStepAnimates = {}
+		this.next = 0
+		this.$ = _this
+
+	}
+
+	_nvuePushAnimates(type, args) {
+		let aniObj = this.currentStepAnimates[this.next]
+		let styles = {}
+		if (!aniObj) {
+			styles = {
+				styles: {},
+				config: {}
+			}
+		} else {
+			styles = aniObj
+		}
+		if (animateTypes1.includes(type)) {
+			if (!styles.styles.transform) {
+				styles.styles.transform = ''
+			}
+			let unit = ''
+			if(type === 'rotate'){
+				unit = 'deg'
+			}
+			styles.styles.transform += `${type}(${args+unit}) `
+		} else {
+			styles.styles[type] = `${args}`
+		}
+		this.currentStepAnimates[this.next] = styles
+	}
+	_animateRun(styles = {}, config = {}) {
+		let ref = this.$.$refs['ani'].ref
+		if (!ref) return
+		return new Promise((resolve, reject) => {
+			nvueAnimation.transition(ref, {
+				styles,
+				...config
+			}, res => {
+				resolve()
+			})
+		})
+	}
+
+	_nvueNextAnimate(animates, step = 0, fn) {
+		let obj = animates[step]
+		if (obj) {
+			let {
+				styles,
+				config
+			} = obj
+			this._animateRun(styles, config).then(() => {
+				step += 1
+				this._nvueNextAnimate(animates, step, fn)
+			})
+		} else {
+			this.currentStepAnimates = {}
+			typeof fn === 'function' && fn()
+			this.isEnd = true
+		}
+	}
+
+	step(config = {}) {
+		// #ifndef APP-NVUE
+		this.animation.step(config)
+		// #endif
+		// #ifdef APP-NVUE
+		this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
+		this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
+		this.next++
+		// #endif
+		return this
+	}
+
+	run(fn) {
+		// #ifndef APP-NVUE
+		this.$.animationData = this.animation.export()
+		this.$.timer = setTimeout(() => {
+			typeof fn === 'function' && fn()
+		}, this.$.durationTime)
+		// #endif
+		// #ifdef APP-NVUE
+		this.isEnd = false
+		let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
+		if(!ref) return
+		this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
+		this.next = 0
+		// #endif
+	}
+}
+
+
+const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
+	'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
+	'translateZ'
+]
+const animateTypes2 = ['opacity', 'backgroundColor']
+const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
+animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
+	MPAnimation.prototype[type] = function(...args) {
+		// #ifndef APP-NVUE
+		this.animation[type](...args)
+		// #endif
+		// #ifdef APP-NVUE
+		this._nvuePushAnimates(type, args)
+		// #endif
+		return this
+	}
+})
+
+export function createAnimation(option, _this) {
+	if(!_this) return
+	clearTimeout(_this.timer)
+	return new MPAnimation(option, _this)
+}

+ 277 - 0
components/uni-components/uni-transition/uni-transition.vue

@@ -0,0 +1,277 @@
+<template>
+	<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
+</template>
+
+<script>
+import { createAnimation } from './createAnimation'
+
+/**
+ * Transition 过渡动画
+ * @description 简单过渡动画组件
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=985
+ * @property {Boolean} show = [false|true] 控制组件显示或隐藏
+ * @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
+ *  @value fade 渐隐渐出过渡
+ *  @value slide-top 由上至下过渡
+ *  @value slide-right 由右至左过渡
+ *  @value slide-bottom 由下至上过渡
+ *  @value slide-left 由左至右过渡
+ *  @value zoom-in 由小到大过渡
+ *  @value zoom-out 由大到小过渡
+ * @property {Number} duration 过渡动画持续时间
+ * @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
+ */
+export default {
+	name: 'uniTransition',
+	emits:['click','change'],
+	props: {
+		show: {
+			type: Boolean,
+			default: false
+		},
+		modeClass: {
+			type: [Array, String],
+			default() {
+				return 'fade'
+			}
+		},
+		duration: {
+			type: Number,
+			default: 300
+		},
+		styles: {
+			type: Object,
+			default() {
+				return {}
+			}
+		},
+		customClass:{
+			type: String,
+			default: ''
+		}
+	},
+	data() {
+		return {
+			isShow: false,
+			transform: '',
+			opacity: 1,
+			animationData: {},
+			durationTime: 300,
+			config: {}
+		}
+	},
+	watch: {
+		show: {
+			handler(newVal) {
+				if (newVal) {
+					this.open()
+				} else {
+					// 避免上来就执行 close,导致动画错乱
+					if (this.isShow) {
+						this.close()
+					}
+				}
+			},
+			immediate: true
+		}
+	},
+	computed: {
+		// 生成样式数据
+		stylesObject() {
+			let styles = {
+				...this.styles,
+				'transition-duration': this.duration / 1000 + 's'
+			}
+			let transform = ''
+			for (let i in styles) {
+				let line = this.toLine(i)
+				transform += line + ':' + styles[i] + ';'
+			}
+			return transform
+		},
+		// 初始化动画条件
+		transformStyles() {
+			return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
+		}
+	},
+	created() {
+		// 动画默认配置
+		this.config = {
+			duration: this.duration,
+			timingFunction: 'ease',
+			transformOrigin: '50% 50%',
+			delay: 0
+		}
+		this.durationTime = this.duration
+	},
+	methods: {
+		/**
+		 *  ref 触发 初始化动画
+		 */
+		init(obj = {}) {
+			if (obj.duration) {
+				this.durationTime = obj.duration
+			}
+			this.animation = createAnimation(Object.assign(this.config, obj),this)
+		},
+		/**
+		 * 点击组件触发回调
+		 */
+		onClick() {
+			this.$emit('click', {
+				detail: this.isShow
+			})
+		},
+		/**
+		 * ref 触发 动画分组
+		 * @param {Object} obj
+		 */
+		step(obj, config = {}) {
+			if (!this.animation) return
+			for (let i in obj) {
+				try {
+					if(typeof obj[i] === 'object'){
+						this.animation[i](...obj[i])
+					}else{
+						this.animation[i](obj[i])
+					}
+				} catch (e) {
+					console.error(`方法 ${i} 不存在`)
+				}
+			}
+			this.animation.step(config)
+			return this
+		},
+		/**
+		 *  ref 触发 执行动画
+		 */
+		run(fn) {
+			if (!this.animation) return
+			this.animation.run(fn)
+		},
+		// 开始过度动画
+		open() {
+			clearTimeout(this.timer)
+			this.transform = ''
+			this.isShow = true
+			let { opacity, transform } = this.styleInit(false)
+			if (typeof opacity !== 'undefined') {
+				this.opacity = opacity
+			}
+			this.transform = transform
+			// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
+			this.$nextTick(() => {
+				// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
+				this.timer = setTimeout(() => {
+					this.animation = createAnimation(this.config, this)
+					this.tranfromInit(false).step()
+					this.animation.run()
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				}, 20)
+			})
+		},
+		// 关闭过度动画
+		close(type) {
+			if (!this.animation) return
+			this.tranfromInit(true)
+				.step()
+				.run(() => {
+					this.isShow = false
+					this.animationData = null
+					this.animation = null
+					let { opacity, transform } = this.styleInit(false)
+					this.opacity = opacity || 1
+					this.transform = transform
+					this.$emit('change', {
+						detail: this.isShow
+					})
+				})
+		},
+		// 处理动画开始前的默认样式
+		styleInit(type) {
+			let styles = {
+				transform: ''
+			}
+			let buildStyle = (type, mode) => {
+				if (mode === 'fade') {
+					styles.opacity = this.animationType(type)[mode]
+				} else {
+					styles.transform += this.animationType(type)[mode] + ' '
+				}
+			}
+			if (typeof this.modeClass === 'string') {
+				buildStyle(type, this.modeClass)
+			} else {
+				this.modeClass.forEach(mode => {
+					buildStyle(type, mode)
+				})
+			}
+			return styles
+		},
+		// 处理内置组合动画
+		tranfromInit(type) {
+			let buildTranfrom = (type, mode) => {
+				let aniNum = null
+				if (mode === 'fade') {
+					aniNum = type ? 0 : 1
+				} else {
+					aniNum = type ? '-100%' : '0'
+					if (mode === 'zoom-in') {
+						aniNum = type ? 0.8 : 1
+					}
+					if (mode === 'zoom-out') {
+						aniNum = type ? 1.2 : 1
+					}
+					if (mode === 'slide-right') {
+						aniNum = type ? '100%' : '0'
+					}
+					if (mode === 'slide-bottom') {
+						aniNum = type ? '100%' : '0'
+					}
+				}
+				this.animation[this.animationMode()[mode]](aniNum)
+			}
+			if (typeof this.modeClass === 'string') {
+				buildTranfrom(type, this.modeClass)
+			} else {
+				this.modeClass.forEach(mode => {
+					buildTranfrom(type, mode)
+				})
+			}
+
+			return this.animation
+		},
+		animationType(type) {
+			return {
+				fade: type ? 1 : 0,
+				'slide-top': `translateY(${type ? '0' : '-100%'})`,
+				'slide-right': `translateX(${type ? '0' : '100%'})`,
+				'slide-bottom': `translateY(${type ? '0' : '100%'})`,
+				'slide-left': `translateX(${type ? '0' : '-100%'})`,
+				'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
+				'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
+			}
+		},
+		// 内置动画类型与实际动画对应字典
+		animationMode() {
+			return {
+				fade: 'opacity',
+				'slide-top': 'translateY',
+				'slide-right': 'translateX',
+				'slide-bottom': 'translateY',
+				'slide-left': 'translateX',
+				'zoom-in': 'scale',
+				'zoom-out': 'scale'
+			}
+		},
+		// 驼峰转中横线
+		toLine(name) {
+			return name.replace(/([A-Z])/g, '-$1').toLowerCase()
+		}
+	}
+}
+</script>
+
+<style></style>

+ 0 - 3
main.js

@@ -4,7 +4,6 @@ import App from './App'
 import ServiceApi from './services/index.js'
 import Api from './common/utilsTools.js'
 import Common from './common/common.js'
-import GlobalComponent from './common/global.component.js'
 import Util from './common/util.js'
 
 //友盟+小程序统计
@@ -17,8 +16,6 @@ Vue.config.productionTip = false
 // 静态文件base路径
 Vue.prototype.$Static = 'https://static.caimei365.com/app/mini-hehe/icon/'
 
-// 注册全局组件 
-Vue.use(GlobalComponent)
 // 工具方法1
 Vue.use(Util)
 // API工具方法

+ 21 - 1
pages.json

@@ -78,6 +78,13 @@
                 "navigationBarTitleText": "商品列表",
                 "enablePullDownRefresh": true
             }
+        },
+        {
+            "path": "pages/goods/good-coupon-list",
+            "style": {
+                "navigationBarTitleText": "优惠券商品列表",
+                "enablePullDownRefresh": true
+            }
         }
     ],
     "subPackages": [{
@@ -166,6 +173,18 @@
                     "enablePullDownRefresh": true,
                     "onReachBottomDistance": 50
                 }
+            },
+            {
+                "path": "activity/coupon-find-list",
+                "style": {
+                    "navigationBarTitleText": "领券中心"
+                }
+            },
+            {
+                "path": "activity/coupon-list",
+                "style": {
+                    "navigationBarTitleText": "优惠券"
+                }
             }
         ]
     }],
@@ -200,7 +219,8 @@
         "autoscan": true,
         "custom": {
             "tui-(.*)": "@/components/thorui/tui-$1/tui-$1.vue",
-            "uni-(.*)": "@/components/uni-components/uni-$1/uni-$1.vue"
+            "uni-(.*)": "@/components/uni-components/uni-$1/uni-$1.vue",
+            "scroll-top": "@/components/cm-module/scrollTop/scrollTop.vue"
         }
     },
     "globalStyle": {

+ 117 - 7
pages/goods/cart.vue

@@ -26,6 +26,11 @@
                 <view class="delBtn" @tap.stop="showDelManager">删除</view>
             </view>
             <view v-if="!isEmpty" class="container-cart">
+                <!-- TODO -->
+                <view class="receive-coupon">
+                    <view class="tip-text">还差¥200可用“满200元减50元”优惠券</view>
+                    <view class="btn" @click="couponVisible = true">领券</view>
+                </view>
                 <view class="cart-content" :style="{ paddingBottom: isIphoneX ? '130rpx' : '100rpx' }">
                     <view class="goods-list">
                         <view v-for="(item, index) in goodsList" :key="index" class="goods-item clearfix">
@@ -159,8 +164,20 @@
 
                         <view class="sum">
                             <view v-if="!isshowDelbtn" class="sum-price">
-                                总价:<text class="money-sign">¥</text
-                                ><text class="money">{{ allPrice | NumFormat }}</text>
+                                <view class="row">
+                                    <text>总价:</text>
+                                    <text class="money-sign">¥</text>
+                                    <text class="money">{{ allPrice | NumFormat }}</text>
+                                </view>
+                                <view class="row">
+                                    <text>共减:</text>
+                                    <text class="discounted-price">¥200.00</text>
+                                    <text @click="showDiscountedDetail">优惠明细</text>
+                                    <text
+                                        class="iconfont"
+                                        :class="showDitail ? 'tui-icon-arrowdown' : 'tui-icon-arrowup'"
+                                    ></text>
+                                </view>
                             </view>
                         </view>
                     </view>
@@ -197,6 +214,27 @@
         ></tui-modal>
         <!-- 促销活动弹窗 -->
         <activi-popup :product="handlerPros" :popupShow="popupShow"></activi-popup>
+        <!-- 优惠券列表 TODO-->
+        <cm-coupon-list title="优惠券" listType="search" :visible="couponVisible" @close="closeCouponList"></cm-coupon-list>
+        <!-- 优惠明细 -->
+        <cm-drawer
+            title="优惠明细"
+            :visible="showDitail"
+            position="bottom"
+            :offset="150"
+            @close="showDitail = false"
+            zIndex="99"
+        >
+            <template>
+                <view class="discounted-ditail">
+                    <view class="row"> <text>商品总额</text> <text>¥450.00</text> </view>
+                    <view class="row"> <text>促销满减</text> <text class="red">-¥180.00</text> </view>
+                    <view class="row"> <text>优惠券</text> <text class="red">-¥20.00</text> </view>
+                    <view class="row total"> <text>总计</text> <text>¥250.00</text> </view>
+                    <view class="tip"> 实际订单金额以结算页为准 </view>
+                </view>
+            </template>
+        </cm-drawer>
         <!-- 透明模态层 -->
         <modal-layer v-if="modallayer"></modal-layer>
     </view>
@@ -206,6 +244,8 @@ import authorize from '@/common/authorize.js'
 import HeaderCart from '@/components/cm-module/headerNavbar/header-cart.vue' //顶部自定义胶囊'
 import activiPopup from '@/components/cm-module/productDetails/cm-activipopu'
 import modalLayer from '@/components/cm-module/modal-layer/modal-layer'
+import CmCouponList from '@/components/cm-module/cm-coupon-list/cm-coupon-list'
+import CmDrawer from '@/components/cm-module/cm-drawer/cm-drawer.vue'
 import { mapGetters, mapActions, mapMutations } from 'vuex'
 import { cartList } from '@/common/json/data.json.js' //本地数据
 
@@ -213,7 +253,9 @@ export default {
     components: {
         modalLayer,
         HeaderCart,
-        activiPopup
+        activiPopup,
+        CmCouponList,
+        CmDrawer
     },
     data() {
         return {
@@ -238,7 +280,9 @@ export default {
             scrollHeight: 'auto',
             modal: false,
             contentModalText: '',
-            deleteType: 0
+            deleteType: 0,
+            couponVisible: false,
+            showDitail: false
         }
     },
     onLoad() {
@@ -312,6 +356,14 @@ export default {
                 this.skeletonShow = false
             })
         },
+        // 关闭优惠券弹窗
+        closeCouponList(){
+            this.couponVisible = false
+        },
+        // 优惠明细 TODO
+        showDiscountedDetail() {
+            this.showDitail = true
+        },
         // 活动价弹窗
         clickPopupShow(pros, type) {
             if (pros.ladderList.length > 0) {
@@ -540,11 +592,57 @@ export default {
 }
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 page {
     background: #f7f7f7;
     height: auto;
 }
+.discounted-ditail {
+    padding: 52rpx 8rpx 16rpx;
+    .row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 12rpx 0;
+        text {
+            font-size: 30rpx;
+            color: #333333;
+            &.red {
+                color: #f94b4b;
+            }
+        }
+        &.total{
+            font-size: 30rpx;
+            font-weight: 600;
+        }
+    }
+    .tip {
+        padding: 12rpx 0;
+        font-size: 26rpx;
+        color: #999999;
+    }
+}
+.receive-coupon{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 24rpx;
+    background: #fff;
+    .tip-text{
+        font-size: 26rpx;
+        color: #FF457B;
+    }
+    .btn{
+        width: 88rpx;
+        height: 42rpx;
+        background: linear-gradient(270deg, #F83C6C 0%, #FC32B4 100%);
+        border-radius: 28rpx;
+        font-size: 26rpx;
+        color: #FFFFFF;
+        text-align: center;
+        line-height: 42rpx;
+    }
+}
 .cart-content {
     position: relative;
 }
@@ -1106,6 +1204,7 @@ page {
     position: fixed;
     bottom: 0rpx;
     z-index: 100;
+    border-top: 1px solid #EFEFEF;
     .footer-le {
         width: 520rpx;
         height: 100%;
@@ -1133,10 +1232,12 @@ page {
             box-sizing: border-box;
             padding: 0 10rpx;
             .sum-price {
-                text-align: right;
+                display: flex;
+                justify-content: center;
+                align-items: flex-end;
+                flex-direction: column;
                 width: 100%;
                 height: 80rpx;
-                line-height: 80rpx;
                 font-size: $font-size-30;
                 color: $text-color;
                 float: left;
@@ -1148,6 +1249,15 @@ page {
                     font-size: $font-size-24;
                     color: $color-system;
                 }
+                .discounted-price{
+                    margin-right: 32rpx;
+                }
+                .row{
+                    &:nth-child(2){
+                        font-size: 24rpx;
+                        color: #FF457B;
+                    }
+                }
             }
         }
     }

+ 357 - 0
pages/goods/good-coupon-list.vue

@@ -0,0 +1,357 @@
+<template>
+    <view class="container clearfix tui-skeleton">
+        <!-- 骨架 -->
+        <tui-skeleton v-if="skeletonShow" :isLoading="true" loadingType="2"></tui-skeleton>
+        <!-- 搜索 -->
+        <sui-search placeholder="请输入商品关键词" :radius="30" @search="searchBlur" @clear="searchClear"></sui-search>
+        <!-- 商品列表 -->
+        <view class="container-section clearfix">
+            <!-- 商品列表区域 -->
+            <view class="product-list" v-for="(pro, index) in productList" :key="index" @click.stop="Details(pro)">
+                <view class="product-list-image">
+                    <image class="product-image" :src="pro.mainImage" mode=""></image>
+                    <!-- 推荐 -->
+                    <image
+                        class="product-icon"
+                        :src="StaticUrl + 'recommend.png'"
+                        mode=""
+                        v-if="pro.recommend === '1'"
+                    ></image>
+                </view>
+                <view class="product-list-msg">
+                    <view class="product-msg-name">{{ pro.name }}</view>
+                    <view class="product-list-tag " v-if="pro.activeStatus == 1">
+                        <text class="tag tag-02">活动价</text>
+                    </view>
+                    <view class="product-list-pri">
+                        <view class="price">¥{{ pro.price | PriceFormat }}</view>
+                        <view class="carts" @click.stop="handAddCarts(pro)">
+                            <view class="carts-add"> <text class="iconfont icon-gouwuche"></text> </view>
+                        </view>
+                    </view>
+                </view>
+            </view>
+        </view>
+        <tui-loadmore :index="2" :visible="isRequest"></tui-loadmore>
+        <tui-nomore :text="loadingText" :visible="!isRequest"></tui-nomore>
+        <!-- 侧边 -->
+        <scroll-top :isScrollTop="isScrollTop" :bottom="160"></scroll-top>
+    </view>
+</template>
+
+<script>
+import authorize from '@/common/authorize.js'
+import banner from '@/components/cm-module/homeIndex/banner.vue'
+import SuiSearch from '@/components/sui-search/sui-search.vue'
+import { mapGetters, mapActions } from 'vuex'
+export default {
+    components: {
+        banner,
+        SuiSearch,
+    },
+    data() {
+        return {
+            StaticUrl: this.$Static,
+            title: '', // 页面标题
+            floorId: '', //楼层id
+            skeletonShow: true,
+            productList: [], //商品列表
+            pageNum: 1,
+            pageSize: 10,
+            hasNextPage: false,
+            isScrollTop: false,
+            isRequest: true,
+            keywords: '',
+            isRefresh: false
+        }
+    },
+    filters: {
+        //处理金额
+        PriceFormat: function(text) {
+            return Number(text).toFixed(2)
+        }
+    },
+    onLoad(options) {
+        this.floorId = options.floorId
+        this.fetchProductList()
+    },
+    computed: {
+        ...mapGetters(['hasLogin', 'userInfo', 'userId']),
+        loadingText() {
+            return this.hasNextPage ? '上拉加载' : '没有更多了'
+        }
+    },
+    methods: {
+        ...mapActions('cart', ['addToCart']),
+        //初始化商品数据列表
+        fetchProductList() {
+            this.isRequest = true
+            this.ProductService.QueryProductList({
+                pageNum: this.pageNum,
+                pageSize: this.pageSize,
+                // floorId: this.floorId
+                floorId: 2
+            })
+                .then(response => {
+                    let data = response.data
+                    this.pageNum++
+                    this.hasNextPage = data.hasNextPage
+                    // 获取购物车商品数量
+                    // this.GetCartNumber();
+                    this.skeletonShow = false
+                    if (this.isRefresh) {
+                        this.productList = data.list
+                    } else {
+                        this.productList = [...this.productList, ...data.list]
+                    }
+                })
+                .catch(error => {
+                    this.$util.msg(error.msg, 2000)
+                })
+                .finally(() => {
+                    this.isRequest = false
+                    if(this.isRefresh){
+                        setTimeout(() => {
+                            uni.stopPullDownRefresh()
+                            this.isRefresh = false
+                        }, 500)
+                    }
+                })
+        },
+        // 商品详情
+        Details(pro) {
+            this.$api.navigateTo(`/pages/goods/product?productId=${pro.productId}`)
+        },
+        // 添加到购物车
+        handAddCarts(pro) {
+            this.addToCart({ productId: pro.productId })
+        },
+        // 搜索框失去焦点
+        searchBlur(val) {
+            this.keywords = val
+            console.log('搜索框清空', this.keywords)
+        },
+        // 搜索框清空
+        searchClear() {
+            this.keywords = ''
+            console.log('搜索框清空', this.keywords)
+        }
+    },
+    // 监听页面滚动事件
+    onPageScroll(e) {
+        this.isScrollTop = e.scrollTop > 400
+    },
+    onPullDownRefresh() {
+        //下拉刷新
+        this.pageNum = 1
+        this.isRefresh = true
+        this.fetchProductList()
+    },
+    onReachBottom() {
+        if (!this.hasNextPage) return
+        this.fetchProductList()
+    },
+    onShareAppMessage(res) {
+        //分享转发
+        if (res.from === 'button') {
+            // 来自页面内转发按钮
+        }
+        return {
+            title: '国内外知名美容院线护肤品线上商城~',
+            path: 'pages/tabBar/index/index',
+            imageUrl: 'https://static.caimei365.com/app/mini-hehe/icon/icon-index-share.jpg'
+        }
+    }
+}
+</script>
+
+<style lang="scss">
+page {
+    background-color: #f7f7f7;
+}
+.container {
+    width: 750rxp;
+    height: auto;
+}
+.navbar-wrap {
+    position: fixed;
+    width: 100%;
+    top: 0;
+    z-index: 100000;
+    box-sizing: border-box;
+    background-image: linear-gradient(0deg, #f83c6c 0%, #fa55bf 100%);
+    background-size: cover;
+    border-bottom: none;
+    &.bgnone {
+        background: rgba(255, 255, 255, 0);
+    }
+    &.bgclass {
+        background: #f94a9b;
+    }
+}
+.navbar-text {
+    font-size: 30rpx;
+    color: #000000;
+    font-weight: 500;
+}
+.navbar-text.center {
+    text-align: center;
+}
+.navbar-text.left {
+    text-align: left;
+    padding-left: 45px;
+}
+.navbar-icon {
+    position: fixed;
+    display: flex;
+    box-sizing: border-box;
+}
+.navbar-icon .iconfont {
+    display: inline-block;
+    overflow: hidden;
+    font-size: 44rpx;
+    padding-right: 40rpx;
+    margin-top: 1px;
+}
+.navbar-icon .icon-iconfonticonfontsousuo1 {
+    color: #000000;
+}
+.navbar-icon view {
+    height: 18px;
+    border-left: 0.5px solid rgba(0, 0, 0, 0.3);
+    margin-top: 6px;
+}
+.navbar-loading {
+    background: #fff;
+    text-align: center;
+}
+.container-section {
+    width: 100%;
+    height: auto;
+    background-color: #f7f7f7;
+    box-sizing: border-box;
+    padding: 0 24rpx;
+    .product-list {
+        width: 339rpx;
+        height: 532rpx;
+        float: left;
+        margin-right: 24rpx;
+        margin-top: 24rpx;
+        background-color: #ffffff;
+        border-radius: 16rpx;
+        &:nth-child(2n) {
+            margin-right: 0;
+        }
+        .product-list-image {
+            width: 100%;
+            height: 339rpx;
+            float: left;
+            position: relative;
+            .product-image {
+                width: 100%;
+                height: 100%;
+                display: block;
+                border-radius: 16rpx 16rpx 0 0;
+            }
+            .product-icon {
+                width: 68rpx;
+                height: 55rpx;
+                display: block;
+                position: absolute;
+                top: 0;
+                left: 34rpx;
+            }
+        }
+        .product-list-msg {
+            width: 100%;
+            height: 193rpx;
+            box-sizing: border-box;
+            padding: 16rpx 24rpx;
+            float: left;
+            position: relative;
+            .product-msg-name {
+                width: 100%;
+                height: 72rpx;
+                line-height: 35rpx;
+                text-overflow: ellipsis;
+                overflow: hidden;
+                display: -webkit-box;
+                -webkit-line-clamp: 2;
+                line-clamp: 2;
+                -webkit-box-orient: vertical;
+                font-size: $font-size-26;
+                color: #333333;
+                text-align: justify;
+                float: left;
+            }
+            .product-list-tag {
+                position: relative;
+                z-index: 9;
+                width: 100%;
+                height: 30rpx;
+                margin-top: 8rpx;
+                float: left;
+                .tag {
+                    display: inline-block;
+                    height: 32rpx;
+                    font-size: 22rpx;
+                    line-height: 30rpx;
+                    text-align: center;
+                    color: #f83c6c;
+                    float: left;
+                    margin-right: 10rpx;
+                    &.tag-02 {
+                        width: 80rpx;
+                        background: url(https://static.caimei365.com/app/mini-hehe/icon/icon-active.png) top center
+                            no-repeat;
+                        background-size: contain;
+                    }
+                    &.tag-01 {
+                        width: 56rpx;
+                        color: #fff;
+                        background-color: #f83c6c;
+                        border-radius: 4rpx;
+                    }
+                }
+            }
+            .product-list-pri {
+                width: 100%;
+                height: 44rpx;
+                float: left;
+                position: absolute;
+                bottom: 16rpx;
+                left: 0;
+                box-sizing: border-box;
+                padding: 0 24rpx;
+                .price {
+                    float: left;
+                    font-size: $font-size-26;
+                    color: #f83c6c;
+                    font-weight: bold;
+                    line-height: 44rpx;
+                }
+                .carts {
+                    float: right;
+                    .carts-add {
+                        width: 44rpx;
+                        height: 44rpx;
+                        text-align: center;
+                        line-height: 44rpx;
+                        background-color: #ff457b;
+                        border-radius: 50%;
+                        .iconfont {
+                            font-size: 32rpx;
+                            color: #ffffff;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+.clearfix::after {
+    content: '';
+    display: block;
+    clear: both;
+}
+</style>

+ 56 - 2
pages/goods/product.vue

@@ -124,6 +124,15 @@
                         <text class="title">参数:</text> <text class="name">品牌 分类...</text>
                         <text class="iconfont icon-chakangengduo"></text>
                     </view>
+                    <!-- 优惠券  TODO -->
+                    <view class="product-coupon" @click="couponVisible = true">
+                        <view class="title">优惠券:</view>
+                        <view class="coupon-tags">
+                            <text class="tag">满5000减100</text> <text class="tag">满5000减100</text>
+                            <text class="tag">满5000减100</text>
+                        </view>
+                        <text class="iconfont icon-chakangengduo"></text>
+                    </view>
                 </view>
                 <view class="product-details product-details1">
                     <!-- 商品详情 -->
@@ -281,6 +290,8 @@
                 <scroll-top :isScrollTop="isScrollTop" :bottom="200"></scroll-top>
             </view>
         </template>
+        <!-- 优惠券列表 TODO-->
+        <cm-coupon-list title="获取优惠券" listType="receive" :visible="couponVisible" @close="closeCouponList"></cm-coupon-list>
     </view>
 </template>
 
@@ -291,6 +302,7 @@ import cmPrice from '@/components/cm-module/productDetails/cm-price.vue' //价
 import cmAttributes from '@/components/cm-module/productDetails/cm-attributes.vue' //规格信息
 import parser from '@/components/jyf-Parser/index' //富文本处理
 import cmService from '@/components/cm-module/productDetails/cm-service' //服务项目
+import CmCouponList from '@/components/cm-module/cm-coupon-list/cm-coupon-list'
 import authorize from '@/common/authorize.js'
 import wxLogin from '@/services/wxLogin.js'
 import { debounce } from '@/common/common.js'
@@ -301,7 +313,8 @@ export default {
         parser,
         cmPrice,
         cmAttributes,
-        cmService
+        cmService,
+        CmCouponList
     },
     data() {
         return {
@@ -356,7 +369,8 @@ export default {
             scrollTopArray: [],
             sectionTopRangeArr: [],
             winHeight: '',
-            isShowButton: false
+            isShowButton: false,
+            couponVisible: false
         }
     },
     onLoad(option) {
@@ -384,6 +398,9 @@ export default {
     },
     methods: {
         ...mapActions('cart', ['addToCart', 'getCartNumber']),
+        closeCouponList() {
+            this.couponVisible = false
+        },
         initData() {
             // 初始化商品详情查询
             this.ProductService.QueryProductDetils({
@@ -734,6 +751,7 @@ page {
     bottom: 30rpx;
     right: 0;
 }
+
 .product-wrap {
     width: 100%;
     height: auto;
@@ -1030,6 +1048,42 @@ page {
         margin-left: 10rpx;
     }
 }
+// 选择优惠券
+.product-coupon {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-left: 24rpx;
+    padding-right: 16rpx;
+    height: 90rpx;
+    color: #ff457b;
+    .title {
+        width: auto !important;
+        font-size: 28rpx;
+        color: #ff457b;
+    }
+    .coupon-tags {
+        flex: 1;
+        margin-right: 24rpx;
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+        .tag {
+            height: 32rpx;
+            padding: 2rpx 8rpx;
+            background: #fff3f7;
+            border: 1rpx solid #ff457b;
+            border-radius: 10rpx;
+            font-size: 20rpx;
+            color: #ff457b;
+            margin-left: 16rpx;
+        }
+    }
+    .iconfont {
+        font-size: 32rpx;
+    }
+}
+
 .product-parameter {
     width: 702rpx;
     height: 90rpx;

+ 122 - 9
pages/tabBar/cart/index.vue

@@ -9,13 +9,18 @@
         ></tui-skeleton>
         <view class="container-cart-main tui-skeleton" :style="{ paddingTop: isshowDelbtn ? '0rpx' : '80rpx' }">
             <view class="foot-check-delbtn" v-if="!isshowDelbtn && goodsList.length > 0">
-                <view class="foot-text"
-                    >共<text>{{ kindCount }}</text
+                <view class="foot-text">
+                    共<text>{{ kindCount }}</text
                     >件商品</view
                 >
                 <view class="delBtn" @tap.stop="showDelManager">删除</view>
             </view>
             <view v-if="!isEmpty" class="container-cart">
+                <!-- TODO -->
+                <view class="receive-coupon">
+                    <view class="tip-text">还差¥200可用“满200元减50元”优惠券</view>
+                    <view class="btn" @click="couponVisible = true">领券</view>
+                </view>
                 <view class="cart-content" :style="{ paddingBottom: isIphoneX ? '130rpx' : '100rpx' }">
                     <view class="goods-list">
                         <view v-for="(item, index) in goodsList" :key="index" class="goods-item clearfix">
@@ -148,8 +153,18 @@
 
                         <view class="sum">
                             <view v-if="!isshowDelbtn" class="sum-price">
-                                总价:<text class="money-sign">¥</text
-                                ><text class="money">{{ allPrice | NumFormat }}</text>
+                                <view class="row">
+                                    <text>总价:</text> <text class="money-sign">¥</text>
+                                    <text class="money">{{ allPrice | NumFormat }}</text>
+                                </view>
+                                <view class="row">
+                                    <text>共减</text> <text class="discounted-price">¥200.00</text>
+                                    <text @click="showDiscountedDetail">优惠明细</text>
+                                    <text
+                                        class="iconfont"
+                                        :class="showDitail ? 'tui-icon-arrowdown' : 'tui-icon-arrowup'"
+                                    ></text>
+                                </view>
                             </view>
                         </view>
                     </view>
@@ -186,6 +201,32 @@
         ></tui-modal>
         <!-- 促销活动弹窗 -->
         <activi-popup :product="handlerPros" :popupShow="popupShow"></activi-popup>
+        <!-- 优惠券列表 TODO-->
+        <cm-coupon-list
+            title="优惠券"
+            listType="search"
+            :visible="couponVisible"
+            @close="closeCouponList"
+        ></cm-coupon-list>
+        <!-- 优惠明细 -->
+        <cm-drawer
+            title="优惠明细"
+            :visible="showDitail"
+            position="bottom"
+            :offset="100"
+            @close="showDitail = false"
+            zIndex="99"
+        >
+            <template>
+                <view class="discounted-ditail">
+                    <view class="row"> <text>商品总额</text> <text>¥450.00</text> </view>
+                    <view class="row"> <text>促销满减</text> <text class="red">-¥180.00</text> </view>
+                    <view class="row"> <text>优惠券</text> <text class="red">-¥20.00</text> </view>
+                    <view class="row total"> <text>总计</text> <text>¥250.00</text> </view>
+                    <view class="tip"> 实际订单金额以结算页为准 </view>
+                </view>
+            </template>
+        </cm-drawer>
         <!-- 透明模态层 -->
         <modal-layer v-if="modallayer"></modal-layer>
     </view>
@@ -194,12 +235,16 @@
 import authorize from '@/common/authorize.js'
 import activiPopup from '@/components/cm-module/productDetails/cm-activipopu'
 import modalLayer from '@/components/cm-module/modal-layer/modal-layer'
+import CmCouponList from '@/components/cm-module/cm-coupon-list/cm-coupon-list'
+import CmDrawer from '@/components/cm-module/cm-drawer/cm-drawer.vue'
 import { mapGetters, mapActions, mapMutations } from 'vuex'
 
 export default {
     components: {
         activiPopup,
-        modalLayer
+        modalLayer,
+        CmCouponList,
+        CmDrawer
     },
     data() {
         return {
@@ -217,7 +262,9 @@ export default {
             hasNextPage: false,
             modal: false,
             contentModalText: '',
-            deleteType: 0
+            deleteType: 0,
+            couponVisible: false,
+            showDitail: false
         }
     },
     onLoad() {
@@ -291,6 +338,10 @@ export default {
                 this.skeletonShow = false
             })
         },
+        // 关闭优惠券弹窗
+        closeCouponList() {
+            this.couponVisible = false
+        },
         // 活动价弹窗
         clickPopupShow(pros, type) {
             if (pros.ladderList.length > 0) {
@@ -298,6 +349,10 @@ export default {
                 this.handlerPros = pros
             }
         },
+        // 优惠明细 TODO
+        showDiscountedDetail() {
+            this.showDitail = true
+        },
         // 勾选单个失效商品
         ischeckFailure(failure) {
             this.selectFailure({
@@ -504,11 +559,57 @@ export default {
 }
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 page {
     background: #f7f7f7;
     height: auto;
 }
+.discounted-ditail {
+    padding: 52rpx 8rpx 0;
+    .row {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 12rpx 0;
+        text {
+            font-size: 30rpx;
+            color: #333333;
+            &.red {
+                color: #f94b4b;
+            }
+        }
+        &.total{
+            font-size: 30rpx;
+            font-weight: 600;
+        }
+    }
+    .tip {
+        padding: 12rpx 0;
+        font-size: 26rpx;
+        color: #999999;
+    }
+}
+.receive-coupon {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 24rpx;
+    background: #fff;
+    .tip-text {
+        font-size: 26rpx;
+        color: #ff457b;
+    }
+    .btn {
+        width: 88rpx;
+        height: 42rpx;
+        background: linear-gradient(270deg, #f83c6c 0%, #fc32b4 100%);
+        border-radius: 28rpx;
+        font-size: 26rpx;
+        color: #ffffff;
+        text-align: center;
+        line-height: 42rpx;
+    }
+}
 .cart-content {
     position: relative;
 }
@@ -1072,6 +1173,7 @@ page {
     position: fixed;
     bottom: 0rpx;
     z-index: 100;
+    border-top: 1px solid #EFEFEF;
     .footer-le {
         width: 520rpx;
         height: 100%;
@@ -1099,10 +1201,12 @@ page {
             box-sizing: border-box;
             padding: 0 10rpx;
             .sum-price {
-                text-align: right;
+                display: flex;
+                justify-content: center;
+                align-items: flex-end;
+                flex-direction: column;
                 width: 100%;
                 height: 80rpx;
-                line-height: 80rpx;
                 font-size: $font-size-30;
                 color: $text-color;
                 float: left;
@@ -1114,6 +1218,15 @@ page {
                     font-size: $font-size-24;
                     color: $color-system;
                 }
+                .discounted-price {
+                    margin-right: 32rpx;
+                }
+                .row {
+                    &:nth-child(2) {
+                        font-size: 24rpx;
+                        color: #ff457b;
+                    }
+                }
             }
         }
     }

+ 43 - 43
pages/tabBar/index/index.vue

@@ -85,7 +85,7 @@
             @click="activeClick"
             @closed="activeClosed"
         ></active-popup>
-        <!-- 领取优惠券提 -->
+        <!-- 领取优惠券提 -->
         <selector-coupons
             :top="CustomBar"
             :visible="tipVisible"
@@ -152,9 +152,9 @@ export default {
         this.wechatlogin()
         this.GetHomeBanner()
         this.GetHomeProductList()
-        setTimeout(()=>{
+        setTimeout(() => {
             this.tipVisible = false
-        },5000)
+        }, 5000)
     },
     computed: {
         ...mapGetters(['hasLogin', 'userIdentity', 'userId'])
@@ -301,46 +301,6 @@ page {
     background: #fff;
     text-align: center;
 }
-.search-input {
-    width: 100%;
-    height: 110rpx;
-    padding: 20rpx 24rpx;
-    box-sizing: border-box;
-    .gosearch-btn {
-        width: 100%;
-        height: 100%;
-        border-radius: 40rpx;
-        background: #f0f0f0;
-        margin: 0 auto;
-        font-size: 28rpx;
-        line-height: 70rpx;
-        color: #8a8a8a;
-        background: #ffffff;
-        position: relative;
-        box-sizing: border-box;
-        padding-left: 80rpx;
-        .search-icon {
-            width: 80rpx;
-            height: 70rpx;
-            position: absolute;
-            left: 0;
-            top: 0;
-            text-align: center;
-            line-height: 70rpx;
-            .icon-iconfonticonfontsousuo1 {
-                margin: 0 6rpx;
-                font-size: $font-size-34;
-                color: #8a8a8a;
-                z-index: 10;
-            }
-        }
-        .search-text {
-            font-size: $font-size-24;
-            line-height: 70rpx;
-            color: #8a8a8a;
-        }
-    }
-}
 .container-home {
     background: #f7f7f7 url(https://static.caimei365.com/app/mini-hehe/icon/icon-index-bg.png) top center no-repeat;
     background-size: contain;
@@ -470,6 +430,46 @@ page {
         }
     }
 }
+.search-input {
+    width: 100%;
+    height: 110rpx;
+    padding: 20rpx 24rpx;
+    box-sizing: border-box;
+    .gosearch-btn {
+        width: 100%;
+        height: 100%;
+        border-radius: 40rpx;
+        background: #f0f0f0;
+        margin: 0 auto;
+        font-size: 28rpx;
+        line-height: 70rpx;
+        color: #8a8a8a;
+        background: #ffffff;
+        position: relative;
+        box-sizing: border-box;
+        padding-left: 80rpx;
+        .search-icon {
+            width: 80rpx;
+            height: 70rpx;
+            position: absolute;
+            left: 0;
+            top: 0;
+            text-align: center;
+            line-height: 70rpx;
+            .icon-iconfonticonfontsousuo1 {
+                margin: 0 6rpx;
+                font-size: $font-size-34;
+                color: #8a8a8a;
+                z-index: 10;
+            }
+        }
+        .search-text {
+            font-size: $font-size-24;
+            line-height: 70rpx;
+            color: #8a8a8a;
+        }
+    }
+}
 .tui-group-name {
     width: 100%;
     height: 92rpx;

+ 127 - 0
pages/user/activity/coupon-find-list.vue

@@ -0,0 +1,127 @@
+<template>
+    <!-- TODO -->
+    <view class="coupon-find-list">
+        <!-- 顶部提示 -->
+        <view class="top-tip">
+            <view class="tip-text">如何领取更多优惠券?</view>
+            <view class="tip-btn" @click="popupVisible = true">去了解</view>
+        </view>
+        <!-- 优惠券列表 -->
+        <view class="coupon-list">
+            <cm-coupon :key="i" v-for="i in 3"></cm-coupon>
+        </view>
+        <!-- 如何获取更多优惠券 -->
+        <message-popup @close="closePopup" :visible="popupVisible" class="message-popup">
+            <template v-slot:title>
+                <view class="title"> 如何获取更多优惠券? </view>
+            </template>
+            <template v-slot:content>
+                <view class="content">
+                    <view class="row">
+                        <view class="dt">好友分享券</view>
+                        <view class="dd">
+                            登录用户可通过分享商城给好友,当好友成功登录后,您可以领取一张好友分享券。
+                        </view>
+                    </view>
+                    <view class="row">
+                        <view class="dt">好友消费券</view>
+                        <view class="dd">
+                            登录用户可通过分享商城给好友,当好友成功消费一笔订单后,您将可以领取一张好友消费券。
+                        </view>
+                    </view>
+                </view>
+            </template>
+        </message-popup>
+    </view>
+</template>
+
+<script>
+import MessagePopup from '@/components/message-popup/message-popup.vue'
+import CmCoupon from '@/components/cm-module/cm-coupon/cm-coupon.vue'
+export default {
+    components: {
+        MessagePopup,
+        CmCoupon
+    },
+    data() {
+        return {
+            popupVisible: true
+        }
+    },
+    computed: {},
+    onLoad() {},
+    methods: {
+        // 关闭信息弹窗
+        closePopup() {
+            this.popupVisible = false
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+$tip-height: 80rpx;
+$grid: 24rpx;
+
+.top-tip {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 750rpx;
+    height: $tip-height;
+    padding: 0 $grid;
+    background: #fff3f7;
+    box-sizing: border-box;
+    .tip-text {
+        font-size: 26rpx;
+        color: #ff457b;
+    }
+    .tip-btn {
+        width: 128rpx;
+        height: 48rpx;
+        border: 1rpx solid #ff457b;
+        border-radius: 24rpx;
+        text-align: center;
+        line-height: 48rpx;
+        font-size: 26rpx;
+        color: #ff457b;
+    }
+}
+.message-popup {
+    .title {
+        font-size: 30rpx;
+        font-weight: 600;
+        color: #333333;
+    }
+    .content {
+        .row {
+            padding-left: 32rpx;
+            margin: $grid 0;
+            .dt {
+                position: relative;
+                font-size: 26rpx;
+                font-weight: 600;
+                line-height: 40rpx;
+                color: #333333;
+                &::before {
+                    content: '';
+                    display: block;
+                    position: absolute;
+                    left: -$grid;
+                    top: 50%;
+                    transform: translateY(-50%);
+                    width: 10rpx;
+                    height: 10rpx;
+                    background: #ff457b;
+                }
+            }
+            .dd {
+                font-size: 26rpx;
+                line-height: 40rpx;
+                color: #666666;
+                margin-top: $grid;
+            }
+        }
+    }
+}
+</style>

+ 113 - 0
pages/user/activity/coupon-list.vue

@@ -0,0 +1,113 @@
+<template>
+    <!-- TODO -->
+    <view class="coupon-find-list">
+        <!-- tabs -->
+        <tui-tabs
+            :tabs="tabs"
+            :currentTab="currentTab"
+            @change="tabChange"
+            :sliderWidth="118"
+            color="#333333"
+            selectedColor="#FF457B"
+            sliderBgColor="#FF457B"
+        ></tui-tabs>
+        <!-- 优惠券列表 -->
+        <swiper
+            :indicator-dots="false"
+            :autoplay="false"
+            class="swiper"
+            :style="{ height: swiperHeight + 'px' }"
+            :current="currentTab"
+            @change="swiperChange"
+        >
+            <swiper-item class="coupon-list"> <cm-coupon :key="i" v-for="i in 15"></cm-coupon> </swiper-item>
+            <swiper-item class="coupon-list"> <cm-coupon :key="i" v-for="i in 3"></cm-coupon> </swiper-item>
+            <swiper-item class="coupon-list"> 
+                <cm-empty message="暂无任何优惠券~" :image="baseUrl + 'icon-coupon-empty.png'" :offset="-12"></cm-empty>
+            </swiper-item>
+        </swiper>
+        <!-- 跳转领券中心 -->
+        <view class="footer"> <view class="btn">领券中心</view> </view>
+    </view>
+</template>
+
+<script>
+import CmCoupon from '@/components/cm-module/cm-coupon/cm-coupon.vue'
+import CmEmpty from '@/components/cm-module/cm-empty/cm-empty.vue'
+import { mapGetters } from 'vuex'
+export default {
+    components: {
+        CmCoupon,
+        CmEmpty
+    },
+    data() {
+        return {
+            baseUrl: this.$Static,
+            tabs: [
+                {
+                    name: '未使用10'
+                },
+                {
+                    name: '已使用10'
+                },
+                {
+                    name: '已失效10'
+                }
+            ],
+            currentTab: 0
+        }
+    },
+    computed: {
+        ...mapGetters(['windowHeight', 'isIphoneX']),
+        swiperHeight() {
+            if (this.isIphoneX) {
+                return this.windowHeight - 120
+            }
+            return this.windowHeight - 80
+        }
+    },
+    onLoad() {},
+    methods: {
+        tabChange(e) {
+            this.currentTab = e.index
+        },
+        swiperChange(e){
+            this.currentTab = e.detail.current
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+page{
+    width: 100%;
+    height: 100%;
+    background: #f7f7f7;
+}
+.swiper {
+    height: 800rpx;
+    background: #f7f7f7;
+    .coupon-list{
+        height: 100%;
+        overflow-y: scroll;
+    }
+}
+.footer {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    background: #f7f7f7;
+    padding-top: 20rpx;
+    .btn {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: 600rpx;
+        height: 90rpx;
+        background: linear-gradient(90deg, #fc32b4 0%, #f83c6c 100%);
+        border-radius: 45rpx;
+        font-size: 30rpx;
+        color: #ffffff;
+    }
+}
+</style>

+ 48 - 3
pages/user/order/create-order.vue

@@ -9,6 +9,15 @@
             :goodsData="goodsData"
             @handleGoodList="handChangeInputGoodsList"
         ></goodsList>
+        <!-- 选择优惠券 TODO-->
+        <view class="select-coupon" @click="couponVisible = true">
+            <view class="left-title">优惠券</view>
+            <view class="right-content">
+                <view class="coupon-amount">-¥20.00</view> <view class="iconfont icon-chakangengduo"></view>
+            </view>
+        </view>
+        <!-- 优惠券列表 TODO-->
+        <cm-coupon-list title="优惠券" listType="use" :visible="couponVisible" @close="closeCouponList"></cm-coupon-list>
         <!-- 运费 -->
         <seller-freight ref="freight" v-if="isRequest" :freightDatas="freightData"> </seller-freight>
         <!-- 底部 -->
@@ -29,20 +38,23 @@
         </view>
     </view>
 </template>
- 
+
 <script>
 import authorize from '@/common/authorize.js'
 import choiceAddress from '@/components/cm-module/createOrder/address'
 import goodsList from '@/components/cm-module/createOrder/goodsList'
 import sellerFreight from '@/components/cm-module/createOrder/sellerFreight'
+import CmCouponList from '@/components/cm-module/cm-coupon-list/cm-coupon-list'
 export default {
     components: {
         choiceAddress,
         goodsList,
-        sellerFreight
+        sellerFreight,
+        CmCouponList
     },
     data() {
         return {
+            couponVisible: false,
             isSubLoading: false,
             productIds: '', //获取上一级页面商品信息
             productCount: '', //获取上一级页面商品数量
@@ -102,6 +114,9 @@ export default {
         }
     },
     methods: {
+        closeCouponList(){
+            this.couponVisible = false
+        },
         getInitCrearOrder(params) {
             //确认订单初始化信息
             this.OrderService.QueryOrderConfirm(params)
@@ -239,7 +254,7 @@ export default {
 }
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 page {
     height: auto;
     background: #f7f7f7;
@@ -277,6 +292,36 @@ page {
         color: #2a81ff;
     }
 }
+
+.select-coupon {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 0 24rpx;
+    margin: 24rpx 0;
+    height: 90rpx;
+    background: #fff;
+    .left-title {
+        font-weight: bold;
+        color: #333333;
+        font-size: 28rpx;
+    }
+    .right-content {
+        display: flex;
+        justify-content: flex-start;
+        align-items: center;
+        .coupon-amount {
+            font-size: 28rpx;
+            color: #ff457b;
+            margin-right: 12rpx;
+        }
+        .iconfont {
+            font-size: 28rpx;
+            color: #b2b2b2;
+        }
+    }
+}
+
 .invoice-balance {
     width: 702rpx;
     height: auto;

+ 1 - 0
store/getters.js

@@ -3,6 +3,7 @@ const getters = {
     isIphoneX: state => state.app.isIphoneX,
     isIphone: state => state.app.isIphone,
     isActivity: state => state.app.isActivity,
+    windowHeight: state => state.app.windowHeight,
     // 用户授权
     isWxAuthorize: state => state.user.isWxAuthorize,
     userInfo: state => state.user.userInfo,

+ 4 - 0
store/modules/cart.js

@@ -1,3 +1,7 @@
+/**
+ * 用户购物车仓库
+ */
+
 import { productService } from '@/services/index.js'
 import { msg as showMsg } from '@/common/util.js'
 import { navigateTo } from '@/common/utilsTools.js'

+ 35 - 0
store/modules/coupon.js

@@ -0,0 +1,35 @@
+/**
+ * 用户优惠券仓库
+ */
+
+const state = {
+    activeCouponList: [], // 未使用
+    expiredCouponList: [], // 已过期
+    usedCouponList: [] // 已使用
+}
+const mutations = {
+    // 更新未使用优惠券
+    updateActive(state, list) {
+        state.activeCouponList = list
+    },
+    // 更新已过期优惠券
+    updateExpired(state, list) {
+        state.expiredCouponList = list
+    },
+    // 更新已使用优惠券
+    updateUsed(state, list) {
+        state.usedCouponList = list
+    }
+}
+const actions = {
+    // 查询优惠券
+    fetchCouponList() {},
+    // 领取优惠券
+    receiveCoupon() {}
+}
+export default {
+    namespaced: true,
+    state,
+    mutations,
+    actions
+}