<template> <div class="layout"> <LayoutHeader class="layout-header" :show-back="true" :title="'\u200E'"></LayoutHeader> <div class="layout-context fixscroll"> <cube-scroll :data="imageList"> <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" class="square-img-container"> <square-img :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 class="qiugou" v-if="isQiugouEnabled" @click="qiugou"></div> </div> <div class="info"> <div class="info-price"> <template v-if="productDetail.least_price >= 0"> ¥{{productDetail.least_price}}</template> <template v-else> </template> </div> <div class="info-name">{{productDetail.product_name}}</div> </div> <a class="banner" v-if="resource" :href="resource.url"> <img-size :src="sizeImg(resource.src)"/> </a> <div class="info"> <div class="info-list"> <div class="info-list-item" v-if="activity && activity.length !== 0" @click="showActivity"> <div class="info-list-name">促销</div> <div class="info-list-value info-promote"> <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> <top-list v-if="topList && topList.length !== 0" :list="topList" @itemClick="gotoProduct" @allClick="gotoBrand" /> <img class="ref-img" v-lazy="prdDetailTip"/> </div> <img class="ref-img" v-lazy="prdDetailImage" /> <div class="recommend" v-if="recommend"><h2>相关推荐</h2> <product-list :list="recommend" /> </div> </cube-scroll> </div> <div class="footer"> <div class="heart"> <div class="icon-fav" v-if="isFav" @click="_toggleFav(false)"> <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> </div> <div class="icon-fav" v-else @click="_toggleFav(true)"> <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> </div> 收藏 </div> <cube-button class="sell" @click="sell">出售</cube-button> <cube-button class="buy active" @click="buy">购买</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" :config="selectSizeConfig" @hidden="onSizeSelectSheetHidden" @select="onSelectTradeProduct" @add="onRequestSize"/> <size-request-sheet v-if="showSizeRequestSheet" @hidden="onSizeRequestHidden" :productId="productId"/> <buy-sheet v-if="showBuySheet" @hidden="onBuyHidden" :productId="productId"/> </div> </template> <script> import { Button, Slide, Scroll, Popup } from 'cube-ui'; import { get } from 'lodash'; import { createNamespacedHelpers, mapGetters } from 'vuex'; import ImgSize from '../../components/img-size'; import { getImgUrl } from '../../common/utils'; import ProductList from '../list/components/productList'; 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'; import SizeRequestSheet from './components/size-request-sheet'; import BuySheet from './components/buy-sheet'; import TopList from './components/top-list'; import SquareImg from './components/square-img'; import stateShortCutsMixins from './mixins'; const { mapActions } = createNamespacedHelpers('product'); export default { name: 'ProductDetail', mixins: [stateShortCutsMixins], components: { SizeSelectSheet, ActivityListSheet, SizeRequestSheet, BuySheet, ImgSize, ProductList, TopList, SquareImg, '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, showActivitySheet: false, showBuySheet: false, showSizeSelectSheet: false, showSizeRequestSheet: false, selectSizeConfig: {}, }; }, computed: { ...mapGetters(['isQiugouEnabled']), 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); }, title() { return get(this.productDetail, 'product_name', '商品详情'); }, }, mounted() { // if (this.isQiugouEnabled === null) { this.$store.dispatch('getSysConfigQiugou'); // } }, activated() { this.loadData(this.productId); }, beforeRouteLeave(to, from, next) { if (this.showSizeRequestSheet) { this.onSizeRequestHidden(); return next(false); } next(); }, methods: { ...mapActions(['fetchProductInfo', 'fetchTop3', 'fetchFav', 'toggleFav', 'updateTradeInfo', 'getSelectedTradeProduct', 'payment', 'resetSelectedSize']), refresh() { this.$refs.slide.refresh(); }, sizeImg(src, width = 360, height = 72) { if (src) { return getImgUrl(src, width, height); } }, createLoading() { return this.$createToast({ time: 0, }); }, loadData(productId = this.productId, loading) { loading && loading.show(); this.fetchTop3({productId}); this.fetchFav({productId}); return this.fetchProductInfo({productId}).then(() => { loading && loading.hide(); setTimeout(() => { this.refresh(); }, 200); }).catch(() => { loading && loading.hide(); }); }, /** * 登录|认证 * needCert: 需要实名认证 */ async auth(needCert = false) { if (needCert) { const authInfo = await this.$yoho.authRealName(); if (authInfo && authInfo.code === 403) { // 此时已经异步登录,当前页面取消业务处理 return; } return authInfo; } return this.$yoho.auth(); }, async _toggleFav(isFav) { const userInfo = await this.auth(); if (!userInfo) { return; } this.toggleFav({productId: this.productId, isFav}).then(() => { const txt = isFav ? '收藏成功' : '取消收藏成功'; this.$createToast({ txt, type: 'txt', }).show(); }); }, gotoProduct(product) { this.$router.push({ name: this.$route.name, params: { productId: product.id, }, }); }, gotoBrand() { // type: 4,品牌;5,系列 const query = { type: 5, }; if (this.productDetail.seriesId && this.productDetail.series_name) { query.series = this.productDetail.seriesId; query.title = this.productDetail.series_name; } else { query.type = 4; query.brand = this.productDetail.brandId; query.title = this.productDetail.brand_name; } this.$router.push({ name: 'List', query, }); }, showActivity() { this.showActivitySheet = true; }, onActivitySheetHidden() { this.showActivitySheet = false; }, onSizeSelectSheetHidden() { this.showSizeSelectSheet = false; }, buy() { this.resetSelectedSize(); this.selectSizeConfig = { dest: 'OrderBuyConfirm', type: 'buy', title: '购买', }; this.showSizeSelectSheet = true; }, onBuyHidden() { this.showBuySheet = false; }, async sell() { // 出售需要实名认证 const userInfo = await this.auth(true); if (!userInfo) { return; } this.resetSelectedSize(); this.selectSizeConfig = { dest: 'OrderSellConfirm', type: 'sell', title: '出售', }; this.showSizeSelectSheet = true; }, async onSelectTradeProduct(tradeProduct) { if (this.selectSizeConfig.type === 'buy') { try { const info = await this.payment({ skup: tradeProduct.skup, }); /** * { * "message": "您有未支付的订单,支付或取消后可提交新的订单", * "code": 512 * } */ if (info.code === 512) { return this.$createDialog({ type: 'confirm', content: info.message, confirmBtn: { text: '查看订单', active: true, disabled: false, href: 'javascript:;' }, cancelBtn: { text: '取消', active: false, disabled: false, href: 'javascript:;' }, onConfirm: () => { this.$router.push({ name: 'OrderList', params: { owner: this.selectSizeConfig.type, }, }); this.showSizeSelectSheet = false; }, }).show(); } } catch (e) { // e } } this.$router.push({ name: this.selectSizeConfig.dest, query: tradeProduct }); this.showSizeSelectSheet = false; }, onRequestSize() { this.showSizeSelectSheet = false; this.showSizeRequestSheet = true; }, onSizeRequestHidden() { this.showSizeRequestSheet = false; }, qiugou() { this.showBuySheet = 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 { .square-img-container { display: block; width: 520px; height: 520px; margin: 0 auto; } .dot { width: 10px; height: 10px; margin: 0 5px; border-radius: 50%; &.active { background-color: #08304b; } } .qiugou { position: absolute; top: 0; right: 58px; width: 58px; height: 48px; background: top left url("~statics/image/product/qiugou@3x.png") no-repeat; background-size: 100% 100%; } } .cube-slide-item { img { margin: 0 auto; } } .banner { padding: 20px 0 0; display: block; img { width: 100%; height: 132px; } } .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: 14px 40px; &-price { color: #d0021b; font-size: 48px; font-weight: bold; text-align: center; i { font-style: normal; font-size: 36px; vertical-align: text-bottom; } } &-name { font-size: 28px; color: #999; text-align: center; line-height: 32px; } &-list { line-height: 104px; // PM: 60 -> 52 white-space: nowrap; &-item { display: flex; justify-content: space-between; border-bottom: 1px solid #eee; font-size: 28px; box-sizing: border-box; } &-name { /* font-family: PingFang-SC-Regular; */ color: #999; } &-value { /* font-family: SFProText-Medium; */ color: #000; letter-spacing: 0; font-weight: bold; } } .ref-img { margin-top: 40px; } } .ref-img { width: 100%; } .recommend { padding-top: 20px; background: #f5f5f5; h2 { font-size: 36px; line-height: 50px; padding: 20px 0 0; margin: 0 40px; } } .footer { display: flex; text-align: center; padding: 16px 30px; @include cube-ufo-btn; .heart { margin-right: auto; text-align: center; width: 3em; color: #888; font-size: 22px; line-height: 32px; svg { width: 48px; height: 48px; color: #888; fill: currentColor; stroke: currentColor; } } .sell, .buy { flex: 0 0 6em; } } </style>