product-detail.vue 9.82 KB
<template>
  <div class="layout">
    <LayoutHeader class="layout-header" :show-back="true">
        <template>
          <transition name="fade">
           <img-size v-show="headThumbnailVisible" class="title-thumbnail" :src="imageList[0] && imageList[0].image_url" :width="300" :height="300"/>
          </transition>
        </template>

      <template #opts>
        <cube-button class="icon-fav" :light="true" v-if="productDetail.isFav" @click="_toggleFav(true)">
          <svg id="icon-heart" style="width: 1rem; height: 1rem;" viewBox="0 0 28 28">
            <title>heart</title>
            <path d="M14 26c-0.25 0-0.5-0.094-0.688-0.281l-9.75-9.406c-0.125-0.109-3.563-3.25-3.563-7 0-4.578 2.797-7.313 7.469-7.313 2.734 0 5.297 2.156 6.531 3.375 1.234-1.219 3.797-3.375 6.531-3.375 4.672 0 7.469 2.734 7.469 7.313 0 3.75-3.437 6.891-3.578 7.031l-9.734 9.375c-0.187 0.187-0.438 0.281-0.688 0.281z"></path>
          </svg>
        </cube-button>
        <cube-button class="icon-fav" :light="true" v-else  @click="_toggleFav(false)">
          <svg id="icon-heart-o" style="width: 1rem; height: 1rem;" viewBox="0 0 28 28">
            <title>heart-o</title>
            <path d="M26 9.312c0-4.391-2.969-5.313-5.469-5.313-2.328 0-4.953 2.516-5.766 3.484-0.375 0.453-1.156 0.453-1.531 0-0.812-0.969-3.437-3.484-5.766-3.484-2.5 0-5.469 0.922-5.469 5.313 0 2.859 2.891 5.516 2.922 5.547l9.078 8.75 9.063-8.734c0.047-0.047 2.938-2.703 2.938-5.563zM28 9.312c0 3.75-3.437 6.891-3.578 7.031l-9.734 9.375c-0.187 0.187-0.438 0.281-0.688 0.281s-0.5-0.094-0.688-0.281l-9.75-9.406c-0.125-0.109-3.563-3.25-3.563-7 0-4.578 2.797-7.313 7.469-7.313 2.734 0 5.297 2.156 6.531 3.375 1.234-1.219 3.797-3.375 6.531-3.375 4.672 0 7.469 2.734 7.469 7.313z"></path>
          </svg>
        </cube-button>
      </template>
    </LayoutHeader>
    <div class="layout-context fixscroll">
      <cube-scroll
        :scroll-events="['scroll']"
        @scroll="handleScroll">

      <div class="slide">
        <cube-slide ref="slide" :data="imageList">
          <cube-slide-item v-for="(item, index) in imageList" :key="index">
            <a click="javascript:void 0">
              <img-size :src="item.image_url" :width="300" :height="300"/>
            </a>
          </cube-slide-item>
          <template slot="dots" slot-scope="props">
            <span class="dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots">{{index + 1}}</span>
          </template>
        </cube-slide>
      </div>
      <div class="info">
        <div class="info-price">¥{{productDetail.least_price||''}}</div>
        <div class="info-name">{{productDetail.product_name}}</div>
        <a class="banner" :href="resource.url">
          <img-size :src="sizeImg(resource.src)" :width="300" :height="60"/>
        </a>
        <div class="info-list">
          <div class="info-list-item">
            <div class="info-list-name">促销</div>
            <div class="info-list-value info-promote"
              v-if="activity && activity.length !== 0"
              @click="showActivity"
              >
              <span>{{activity[0].promotionTypeStr}}</span>
              <i class="cubeic-arrow"></i>
            </div>
          </div>
          <div class="info-list-item" v-for="(desc, index) in productDec" :key="index">
            <div class="info-list-name">{{desc.text}}</div>
            <div class="info-list-value">{{desc.value}}</div>
          </div>
        </div>
        <img class="ref-img" v-lazy="prdDetailTip"/>
      </div>
      <img class="ref-img"  v-lazy="prdDetailImage" />
      </cube-scroll>
    </div>
    <div class="footer">
      <cube-button class="sell" @click="sell">出售</cube-button>
      <cube-button class="buy" @click="buy">购买 <span>¥{{ productDetail.least_price || '-' }}</span></cube-button>
    </div>
    <activity-list-sheet v-if="showActivitySheet" :list="activity" @hidden="onActivitySheetHidden"/>
    <size-select-sheet v-if="showSizeSelectSheet" :list="sizeList" :product="productDetail" :image-list="imageList" @hidden="onSizeSelecctSheetHidden"/>
  </div>
</template>

<script>
import { Button, Slide, Scroll, Popup } from 'cube-ui';
import { get } from 'lodash';
import { createNamespacedHelpers } from 'vuex';

import ImgSize from '../../components/img-size';
import { getImgUrl } from '../../common/utils';

import ActivityListSheet from './components/activity-list-sheet';
import prdDetailTip from '../../statics/image/product/prdDetailTip.png';
import prdDetailImage from '../../statics/image/product/prdDetailImage.png';
import SizeSelectSheet from './components/size-select-sheet';

const { mapActions, mapGetters } = createNamespacedHelpers('product');

export default {
  name: 'ProductDetail',
  components: {
    SizeSelectSheet,
    ActivityListSheet,
    ImgSize,
    'cube-button': Button,
    'cube-slide': Slide,
    'cube-slide-item': Slide.Item,
    'cube-scroll': Scroll,
    'cube-popup': Popup,
  },
  props: {
    productId: {
      required: true,
    },
  },
  data() {
    return {
      prdDetailTip,
      prdDetailImage,
      headThumbnailVisible: false,

      showActivitySheet: false,
      showSizeSelectSheet: false,
    };
  },
  computed: {
    ...mapGetters(['productDetail', 'imageList', 'resource', 'activity']),
    productDec() {
      const goods = get(this.productDetail, 'goods_list[0]', {});

      return [
        {
          text: '颜色',
          value: goods.goods_name || '',
        }, {
          text: '品牌',
          value: this.productDetail.brand_name
        }, {
          text: '系列',
          value: this.productDetail.series_name
        }, {
          text: '发售时间',
          value: this.productDetail.sale_time
        }, {
          text: '发售价格',
          value: this.productDetail.offer_price
        }, {
          text: '货号',
          value: this.productDetail.product_code
        }
      ];
    },
    sizeList() {
      return get(this.productDetail, 'goods_list[0].size_list', null);
    },
  },
  beforeRouteUpdate(to, from, next) {
    const loading = this.createLoading();

    this.loadData(to.params.productId, loading).then(next);
  },
  mounted() {
    this.loadData(this.productId);
    this.imageHideThreadhold = -window.innerWidth * 0.8;
  },
  methods: {
    ...mapActions(['fetchProductInfo', 'toggleFav']),
    handleScroll(e) {
      this.headThumbnailVisible = e.y < this.imageHideThreadhold;
    },
    sizeImg(src, width = 360, height = 72) {
      if (src) {
        return getImgUrl(src, width, height);
      }
    },
    createLoading() {
      return this.$createToast({
        time: 0,
      });
    },
    loadData(id, loading) {
      loading && loading.show();

      this.fetchProductInfo({productId: this.productId}).then(() => {
        loading && loading.hide();
      }).catch(() => {
        loading && loading.hide();
      });
    },
    _toggleFav(isFav) {
      this.toggleFav({productId: this.productId, isFav}).then(() => {
        const txt = isFav ? '收藏成功' : '取消收藏成功';

        this.$createToast({
          txt,
          type: 'txt',
        }).show();
      });
    },
    showActivity() {
      this.showActivitySheet = true;
    },
    onActivitySheetHidden() {
      this.showActivitySheet = false;
    },
    onSizeSelecctSheetHidden() {
      this.showSizeSelectSheet = false;
    },
    gotoProduction(productId) {
      this.$router.push({name: this.$route.name, params: {productId}});
    },
    buy() {
    },
    sell() {
      this.showSizeSelectSheet = true;
    },
  },
};
</script>

<style lang="scss" scoped>
  @import "./product-detail";

  .cube-btn {
    border-radius: 0;
  }

  .fade-enter, .fade-leave-to {
    opacity: 0;
  }

  .fade-enter-active, .fade-leave-active {
    transition: opacity 300ms linear;
  }

  .icon-fav {
    box-shadow: none;
    background: none;
  }

  .slide {
    height: 100vw;
    padding: 38px;

    .dot {
      width: 10px;
      height: 10px;
      margin: 0 5px;
      border-radius: 50%;

      &.active {
        background-color: #08304b;
      }
    }
  }

  .banner {
    padding: 20px 0 0;
    display: block;

    img {
      width: 100%;
    }
  }

  .banner-title {
    font-family: PingFang-SC-Light;
    border: 1px solid #000;
    font-size: 30px;
    line-height: 100px;
    text-align: center;
  }

  .info-promote {
    display: flex;
    align-items: center;
    justify-content: right;

    span {
      display: inline-block;
      border: 1px solid #f00;
      color: #f00;
      line-height: 1.8;
      margin-right: 15px;
      font-size: 0.8em;
      padding: 0 1.2em;
    }

    i {
      color: #999;
    }
  }

  .info {
    padding: 0 40px;

    &-price {
      color: #08304b;
      font-size: 40px;
      font-weight: bold;
    }

    &-name {
      font-size: 30px;
      line-height: 1.4;
    }

    &-list {
      line-height: 120px;
      white-space: nowrap;

      &-item {
        display: flex;
        justify-content: space-between;
        border-bottom: 1px solid #eee;

        &:last-child {
          border-bottom: 0;
        }
      }

      &-name {
        font-family: PingFang-SC-Regular;
        font-size: 28px;
        color: #999;
        letter-spacing: 0;
      }

      &-value {
        font-family: SFProText-Medium;
        font-size: 28px;
        color: #000;
        letter-spacing: 0;
        text-align: right;
        font-weight: bold;
      }
    }
  }

  .ref-img {
    width: 100%;
  }

  .title-thumbnail {
    height: 100%;
  }

  .footer {
    display: flex;
    text-align: center;

    .sell,
    .buy {
      flex-basis: auto;
      border-top: 1px solid $primary-color;
      line-height: 60px;
    }

    .sell {
      flex: 1;
      color: $primary-color;
      background: #fff;
    }

    .buy {
      border-left: 1px solid $primary-color;
      color: #fff;
      flex: 2;
      background-color: $primary-color;

      span {
        color: $sub-color;
      }
    }
  }
</style>