Authored by TaoHuang

Merge remote-tracking branch 'origin/develop' into develop

... ... @@ -29,7 +29,7 @@
<ScrollNav :list="navList" :current="active" @transfer="getIndex"></ScrollNav>
</div>
<div class="list-wrap" :style="{minHeight: total + 'px'}">
<ProductList :list="productList.list" v-if="productList.list.length > 0"></ProductList>
<ProductList ref="product" :list="productList.list" :yasParams="listYasParams" v-if="productList.list.length > 0"></ProductList>
<UfoNoItem :tip="`暂无数据`" v-else></UfoNoItem>
</div>
</div>
... ... @@ -59,6 +59,8 @@ export default {
data() {
return {
PAGE_URL: '',
listYasParams: {},
homeYasParams: [],
options: {
pullUpLoad: true,
// pullDownRefresh: true,
... ... @@ -142,18 +144,17 @@ export default {
this.navTop = this.$refs.topSource.offsetHeight;
this.navHeight = get(this.$refs, 'scrollNav.offsetHeight') || 0;
this.total = this.$refs.scroll.$el.offsetHeight - this.navHeight;
this.refreshProductList(this.active);
this.getReportEle()
this.reportYas()
});
},
// 曝光上报
getReportEle(scrollY) {
this.homeYasParams = [];
this.channelList.list.map((item, index) => {
let scrollHeight = this.$refs.scroll.$el.offsetHeight; // 滚动区高度
let eleHeight = 0;
let eleTop = 0;
let reportParams = {};
if(item.template_name !== 'guessLike') {
eleHeight = this.$refs[index][0].$el.offsetHeight;
eleTop = this.$refs[index][0].$el.offsetTop;
... ... @@ -165,40 +166,85 @@ export default {
if(scrollHeight + scrollY > eleTop && item.template_name === 'guessLike') {
// console.log('guessLike report')
item.data.forEach((val, i) => {
this.reportYas(val.reportParams)
reportParams = {...val.reportParams,PAGE_URL: this.PAGE_URL}
this.homeYasParams.push(reportParams)
})
}
if(scrollY < (eleTop + eleHeight) && item.template_name !== 'guessLike') {
// console.log(item.template_name)
item.data.forEach((val, i) => {
this.reportYas(val.reportParams)
reportParams = {...val.reportParams,PAGE_URL: this.PAGE_URL}
this.homeYasParams.push(reportParams)
})
}
} else { // 默认进入时
if(eleTop < scrollHeight) { // 获取每个元素距顶部高度 小于可视区高度即曝光
// console.log(item.template_name)
item.data.forEach((val, i) => {
this.reportYas(val.reportParams)
reportParams = {...val.reportParams,PAGE_URL: this.PAGE_URL}
this.homeYasParams.push(reportParams)
})
}
}
})
},
reportYas(reportParams) {
reportYas(scrollY) {
this.getReportEle(scrollY)
console.log(this.homeYasParams)
this.$store.dispatch('reportYas', {
params: {
param: {...reportParams, PAGE_URL: this.PAGE_URL },
param: this.homeYasParams,
appop: 'XY_UFO_SHOW_EVENT'
}
});
},
getIndex(index) {
setYasParam: function(params) {
let {total, list} = this.productList;
if (params && typeof params === 'object' && Object.keys(params).length) {
let P_NAME = 'XY_UFOSearchList', TYPE_ID = 1, P_PARAM = '', PRD_LIST = [];
for (let item of list) {
PRD_LIST.push(item.id);
}
for (let key in params) {
if (key === 'brand') {
P_NAME = 'XY_UFOBrandList';
TYPE_ID = 3;
P_PARAM = params[key]
}
if (key === 'series') {
P_NAME = 'XY_UFOSeriesList';
TYPE_ID = 2;
P_PARAM = params[key]
}
if (key === 'productPool') {
P_NAME = 'XY_UFOProductPoolList';
TYPE_ID = 4;
P_PARAM = params[key]
}
}
this.listYasParams = Object.assign({},{
P_NAME,
P_PARAM,
TYPE_ID,
TAB_ID: Number(params.index) + 1,
TAB_NAME: params.title,
PRD_LIST: PRD_LIST.toString(),
TOTAL: total})
// console.log(this.listYasParams)
}
},
getIndex({index,params}) {
this.setYasParam({index,...params});
this.active = Number(index);
this.$refs.scroll.scrollTo(0, -this.navTop, 300);
},
scrollEndHandler({y}) {
let scrollHeight = Math.abs(y)
this.getReportEle(scrollHeight)
this.reportYas(scrollHeight)
this.$refs.product && this.$refs.product.yasShowEvent(scrollHeight+this.navTop);
},
scrollHandler({ y }) {
... ... @@ -211,11 +257,13 @@ export default {
}
}
},
refreshProductList(index) {
async refreshProductList(index) {
let str = get(get(this.navList, `[${index}].url`, '').split('?'), '[1]', '');
this.searchParams = Object.assign({}, queryString.parse(str), this.listBaseParams);
this.fetchList(this.searchParams);
await this.fetchList(this.searchParams);
// 列表上报数据
this.setYasParam({index: 0, ...this.searchParams})
},
async onPullingUp() {
... ... @@ -280,7 +328,7 @@ export default {
this.productList = list;
}
}catch(e) {
console.log(e);
// console.log(e);
this.productList.list = [];
}
},
... ... @@ -291,10 +339,10 @@ export default {
});
},
getParams(params) {
this.reportParams = params;
console.log(this.reportParams);
}
// getParams(params) {
// this.reportParams = params;
// console.log(this.reportParams);
// }
},
components: {
Swiper,
... ... @@ -420,12 +468,16 @@ export default {
background: #f5f5f5;
height: 64px;
font-size: 28px;
color: #000;
color: #999;
flex: 1;
border-radius: 4px;
padding: 14px 14px 14px 10px;
}
input::-webkit-input-placeholder {
color: #000;
}
.middle {
margin: 0 10px;
}
... ...
... ... @@ -45,14 +45,14 @@ export default {
},
methods: {
changeHandler(cur) {
async changeHandler(cur) {
let index = this.$refs[`getindex${cur}`].innerText;
let url = this.list[index].url.split('?');
let params = queryString.parse(url[1]);
params.isReset = true;
this.$parent.$parent.$parent.fetchList && this.$parent.$parent.$parent.fetchList(params);
this.$emit('transfer', index);
this.$parent.$parent.$parent.fetchList && await this.$parent.$parent.$parent.fetchList(params);
this.$emit('transfer', {index,params});
}
}
};
... ...
... ... @@ -2,9 +2,7 @@
<div class="tab-item" :class="itemClass" @click="goPage(data)">
<i v-if="icon" class="tab-icon" :class=icon></i>
<div v-if="data.title" class="title" :class="titleClass">{{ data.title }}</div>
<slot
><div class="text">{{ data.num }}</div></slot
>
<slot><div class="text">{{ data.num }}</div></slot>
<i class="cubeic-arrow"></i>
</div>
</template>
... ...
... ... @@ -57,7 +57,8 @@ export default {
top: false
},
pullUpLoad: true,
pullDownRefresh: false
pullDownRefresh: false,
useTransition: false,
},
fixed: false,
selectedType: 2, // tab类型高亮
... ... @@ -182,7 +183,8 @@ export default {
list.list && list.list.length ? list.isEmpty = false : list.isEmpty = true;
this.productList = list;
this.productList =Object.freeze(list);
}
},
... ...
... ... @@ -673,4 +673,7 @@ export default {
border-top-left-radius: 15px !important;
border-top-right-radius: 15px !important;
}
.cube-input_active::after {
border-color: transparent!important; ;
}
</style>
... ...
... ... @@ -303,6 +303,7 @@ export default {
if (this.preView) {
this.preView.hide && this.preView.hide()
}
next();
}
};
</script>
... ...
... ... @@ -5,11 +5,12 @@
:center="false"
:mask="true"
:z-index="100"
:class="{'fake-viewport': position !== 'bottom'}"
v-show="isVisible"
@mask-click="maskClick">
<transition :name="transition" appear>
<transition :name="transition">
<div class="cube-action-sheet-panel"
:class="{'with-radius': hasBorderRadius, [`panel-${position}`]: true}"
:class="{'with-radius': hasBorderRadius, [`panel-${position}`]: true, full: full}"
:style="panelStyle"
v-show="isVisible"
ref="panel"
... ... @@ -45,6 +46,10 @@ export default {
type: Boolean,
default: false,
},
full: {
type: Boolean,
default: false,
}
},
data() {
return {
... ... @@ -93,7 +98,23 @@ export default {
</script>
<style lang="scss" scoped>
.cube-action-sheet-fade-enter, .cube-action-sheet-fade-leave-active {
.cube-popup.fake-viewport {
position: absolute;
/deep/ {
.cube-popup-content {
height: 100%;
}
.cube-popup-mask {
background-color: rgba(0, 0, 0, 0.5);
opacity: 1;
}
}
}
.cube-action-sheet-fade-enter,
.cube-action-sheet-fade-leave-active {
opacity: 0;
}
... ... @@ -110,6 +131,10 @@ export default {
border-top-left-radius: 20px 20px;
border-top-right-radius: 20px 20px;
&.full {
height: 100%;
}
&:after {
content: "";
width: 100%;
... ... @@ -120,6 +145,7 @@ export default {
z-index: -1;
background: #fff;
}
&.panel-right:after {
width: 100px;
height: 100%;
... ... @@ -147,9 +173,4 @@ export default {
.cube-action-sheet-right-leave-active {
transition: all 0.2s cubic-bezier(0.165, 0.84, 0.44, 1);
}
/deep/ .cube-popup-mask {
background-color: rgba(0, 0, 0, 0.5);
opacity: 1;
}
</style>
... ...
... ... @@ -4,9 +4,10 @@
@shown="onShown"
:panelStyle="{background: 'transparent', paddingLeft: '20%'}"
:hasBorderRadius="false"
:emulateMask="true">
:emulateMask="true"
:full="true">
<div class="buy-sheet">
<a class="header" href="https://activity.yoho.cn/feature/6761.html?share_id=9467&title=UFO求购功能说明">求购<i class="cubeic-question"></i></a>
<a class="header" href="https://activity.yoho.cn/feature/6765.html?nodownload=1">求购<i class="cubeic-question"></i></a>
<div class="title">
<div class="title-thumbnail">
<square-img :src="imageUrl" :width="300" :height="300"/>
... ... @@ -154,7 +155,7 @@ export default {
@import "../product-detail";
.buy-sheet {
height: 100vh;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
... ...
... ... @@ -30,6 +30,18 @@
import { Scroll } from 'cube-ui';
import { orderBy } from 'lodash';
const sizeMap = {
XXXS: -1, // ???
XXS: 1,
XS: 2,
S: 3,
M: 4,
L: 5,
XL: 6,
XXL: 7,
XXXL: 8,
};
export default {
name: 'size-list',
props: {
... ... @@ -84,7 +96,7 @@ export default {
* 根据 name 及 subName 从小到大排列
* @type {Array}
*/
const sorted = orderBy(list, [o => parseFloat(o.name), o => {
const sorted = orderBy(list, [o => (sizeMap[o.name] || parseFloat(o.name)), o => {
if (o.subName == null) {
return 0;
}
... ... @@ -195,6 +207,8 @@ export default {
ul {
padding-bottom: 1px;
overflow: auto;
border-top: 1px solid #ddd;
border-left: 1px solid #ddd;
}
}
... ... @@ -203,15 +217,18 @@ export default {
width: 25%; // 当前固定为4列
float: left;
border: 1px solid #ddd;
margin-right: -1px;
margin-bottom: -1px;
margin-left: -1px;
margin-top: -1px;
.size-info,
.size-price {
font-family: $num-font;
}
&.selected {
background: $primary-color;
.size-info,
.size-price {
font-family: "Alte DIN 1451 Mittelschrift";
color: #fff;
}
}
... ... @@ -219,7 +236,6 @@ export default {
&.disabled {
.size-info,
.size-price {
font-family: "Alte DIN 1451 Mittelschrift";
color: #ccc;
}
}
... ...
<template>
<action-sheet @hidden="onHidden" @shown="onShown" position="right" ref="popup">
<action-sheet @hidden="onHidden" @shown="onShown" position="right" ref="popup" :full="true">
<div class="buy-sheet">
<div class="header">
<div class="back-wrapper flex" @touchend="onBack">
... ... @@ -116,9 +116,14 @@ export default {
@import "../product-detail";
.buy-sheet {
height: 100vh;
display: flex;
flex-direction: column;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
.size-list {
flex: 1;
... ...
... ... @@ -66,7 +66,6 @@ export default {
font-size: 36px;
font-weight: bold;
color: #000;
font-weight: bold;
}
.row {
... ... @@ -110,6 +109,8 @@ export default {
font-family: $num-font;
font-weight: bold;
font-size: 32px;
line-height: 38px;
height: 38px;
color: #d0021b;
text-align: center;
... ...
... ... @@ -2,7 +2,7 @@
<div class="layout">
<LayoutHeader class="layout-header" :show-back="true" :title="'\u200E'"></LayoutHeader>
<div class="layout-context fixscroll">
<cube-scroll :data="imageList">
<cube-scroll :data="imageList" ref="pageScroll">
<div class="slide">
<cube-slide ref="slide" :data="imageList">
<cube-slide-item v-for="(item, index) in imageList" :key="index">
... ... @@ -11,25 +11,27 @@
</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>
<div class="dot-wrap">
<span class="dot" :class="{active: props.current === index}" v-for="(item, index) in props.dots">&bull;</span>
</div>
</template>
</cube-slide>
<div class="qiugou" v-if="isQiugouEnabled" @click="qiugou"></div>
</div>
<div class="info">
<div class="info-basic">
<div class="info-price">
<template v-if="productDetail.least_price == null">{{'\u200E'}}</template>
<template v-else><i>¥</i>{{productDetail.least_price}}</template>
</div>
<div class="info-name">{{productDetail.product_name}}</div>
<div class="info-name"><div>{{productDetail.product_name}}</div></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">
<transition-group name="info-list" tag="div" class="info-list">
<div class="info-list-item" transtion="fade" v-if="activity && activity.length !== 0" @click="showActivity" key="promotion">
<div class="info-list-name">促销</div>
<div class="info-list-value info-promote">
<span>{{activity[0].promotionTypeStr}}</span>
... ... @@ -40,7 +42,7 @@
<div class="info-list-name">{{desc.text}}</div>
<div class="info-list-value">{{desc.value}}</div>
</div>
</div>
</transition-group>
<top-list v-if="topList && topList.length !== 0" :list="topList" @itemClick="gotoProduct" @allClick="gotoBrand" />
<img class="ref-img" v-lazy="prdDetailTip"/>
</div>
... ... @@ -51,7 +53,6 @@
<product-list :list="recommend" priceKey="price"/>
</div>
</cube-scroll>
</div>
<div class="footer">
<div class="heart">
<div class="icon-fav" v-if="isFav" @click="_toggleFav(false)">
... ... @@ -79,6 +80,9 @@
<cube-button class="sell" @click="sell">出售</cube-button>
<cube-button class="buy active" @click="buy">购买</cube-button>
</div>
<buy-sheet v-if="showBidSheet" @hidden="onBidSheet" :productId="productId"/>
<size-request-sheet v-if="showSizeRequestSheet" @hidden="onSizeRequestHidden" :productId="productId"/>
</div>
<activity-list-sheet v-if="showActivitySheet" :list="activity" @hidden="onActivitySheetHidden"/>
<size-select-sheet v-if="showSizeSelectSheet"
:list="sizeList"
... ... @@ -88,8 +92,6 @@
@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>
... ... @@ -144,7 +146,7 @@ export default {
showActivitySheet: false,
showBuySheet: false,
showBidSheet: false,
showSizeSelectSheet: false,
showSizeRequestSheet: false,
... ... @@ -189,19 +191,45 @@ export default {
// if (this.isQiugouEnabled === null) {
this.$store.dispatch('getSysConfigQiugou');
// }
// 在action-sheet显示控制与对应关闭方法
this.actionSheetCloseMap = {
showActivitySheet: 'onActivitySheetHidden',
showBidSheet: 'onBidSheet',
showSizeSelectSheet: 'onSizeSelectSheetHidden',
showSizeRequestSheet: 'onSizeRequestHidden',
};
},
activated() {
this.$refs.pageScroll && this.$refs.pageScroll.scrollTo(0, 0, 0);
this.loadData(this.productId);
},
beforeRouteUpdate(to, from ,next) {
if (this.historyBackGuard() === false) {
return next(false);
}
next();
},
beforeRouteLeave(to, from, next) {
if (this.showSizeRequestSheet) {
this.onSizeRequestHidden();
if (this.historyBackGuard() === false) {
return next(false);
}
next();
},
methods: {
...mapActions(['fetchProductInfo', 'fetchBrandTop', 'fetchFav', 'toggleFav', 'updateTradeInfo', 'getSelectedTradeProduct', 'payment', 'resetSelectedSize']),
historyBackGuard() {
for (let key of Object.keys(this.actionSheetCloseMap)) {
if (this[key]) {
this[this.actionSheetCloseMap[key]]();
return false;
}
}
return true;
},
refresh() {
this.$refs.slide.refresh();
},
... ... @@ -210,11 +238,8 @@ export default {
return getImgUrl(src, width, height);
}
},
createLoading() {
return this.$createToast({
time: 0,
});
},
// 加载商品详情数据
loadData(productId = this.productId, loading) {
loading && loading.show();
... ... @@ -248,6 +273,8 @@ export default {
return this.$yoho.auth();
},
// toggle收藏
async _toggleFav(isFav) {
const userInfo = await this.auth();
... ... @@ -264,6 +291,8 @@ export default {
}).show();
});
},
// 进入商品详情
gotoProduct(product) {
this.$router.push({
name: this.$route.name,
... ... @@ -272,6 +301,8 @@ export default {
},
});
},
// 打开产品品牌或系列TOP
gotoBrand() {
this.$router.push({
name: 'BrandProductList',
... ... @@ -280,15 +311,23 @@ export default {
},
});
},
// 显示活动列表
showActivity() {
this.showActivitySheet = true;
},
// 关闭活动列表
onActivitySheetHidden() {
this.showActivitySheet = false;
},
// 选择尺寸
onSizeSelectSheetHidden() {
this.showSizeSelectSheet = false;
},
// 购买
buy() {
this.resetSelectedSize();
this.selectSizeConfig = {
... ... @@ -298,9 +337,8 @@ export default {
};
this.showSizeSelectSheet = true;
},
onBuyHidden() {
this.showBuySheet = false;
},
// 出售
async sell() {
// 出售需要实名认证
const userInfo = await this.auth(true);
... ... @@ -317,6 +355,8 @@ export default {
};
this.showSizeSelectSheet = true;
},
// 选择出售或购买
async onSelectTradeProduct(tradeProduct) {
if (this.selectSizeConfig.type === 'buy') {
//数据埋点
... ... @@ -359,13 +399,14 @@ export default {
href: 'javascript:;'
},
onConfirm: () => {
this.showSizeSelectSheet = false;
this.$router.push({
name: 'OrderList',
params: {
owner: this.selectSizeConfig.type,
},
});
this.showSizeSelectSheet = false;
},
}).show();
}
... ... @@ -374,21 +415,33 @@ export default {
}
}
this.showSizeSelectSheet = false;
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;
this.showBidSheet = true;
},
// 购买
onBidSheet() {
this.showBidSheet = false;
},
},
};
... ... @@ -424,18 +477,28 @@ export default {
margin: 0 auto;
}
/deep/ .cube-slide-dots {
margin-bottom: -2px;
}
.dot-wrap {
padding-bottom: 10px;
}
.dot {
display: inline-block;
width: 8px;
height: 8px;
margin: 0 10px 2px;
border-radius: 50%;
margin: 0 10px;
background: radial-gradient(8px 8px at 50% 50%, rgba(0, 0, 0, 0.15) 50%, transparent 50%) no-repeat;
&.active {
margin: 2px 10px 0;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #08304b;
/*margin: 2px 10px 0;*/
/*width: 12px;*/
/*height: 12px;*/
transform-origin: 50% 50%;
transform: scale(1.5);
background: radial-gradient(8px 8px at 50% 50%, rgba(0, 0, 0, 1) 50%, transparent 50%) no-repeat;
}
}
... ... @@ -457,7 +520,6 @@ export default {
}
.banner {
padding: 20px 0 0;
display: block;
img {
... ... @@ -482,7 +544,7 @@ export default {
span {
display: inline-block;
border: 1px solid #feabac;
color: #D0021B;
color: #d0021b;
line-height: 1.8;
margin-right: 15px;
font-size: 0.8em;
... ... @@ -494,14 +556,16 @@ export default {
}
}
.info {
padding: 46px 40px 10px;
.info-basic {
padding: 46px 40px 32px;
&-price {
.info-price {
color: #d0021b;
font-size: 48px;
font-family: $num-font;
font-weight: bold;
line-height: 56px;
height: 56px;
text-align: center;
letter-spacing: 0;
... ... @@ -512,14 +576,30 @@ export default {
}
}
&-name {
.info-name {
margin-top: 14px;
font-size: 28px;
color: #999;
text-align: center;
height: 64px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
div {
font-size: 28px;
line-height: 32px;
max-height: 64px;
white-space: normal;
word-break: break-all;
}
}
}
.info {
padding: 0 40px;
&-list {
line-height: 104px; // PM: 60 -> 52
white-space: nowrap;
... ... @@ -547,16 +627,21 @@ export default {
.ref-img {
margin-top: 40px;
width: 670px;
height: 100px;
}
}
.ref-img {
width: 100%;
display: block;
width: 750px;
height: 628px;
}
.recommend {
padding-top: 20px;
background: #f5f5f5;
background-color: #f2f2f2;
background-image: linear-gradient(180deg, #fefefe 0%, #f2f2f2 320px);
h2 {
font-size: 36px;
... ... @@ -567,6 +652,24 @@ export default {
}
}
.layout-context {
display: flex;
flex-direction: column;
}
.cube-scroll-wrapper {
height: auto;
flex: 1 0 0;
// 当前页面上面白色背景,底部recommend的f2f2f2, scroll的moment功能会使用cube-scroll-wrapper的顶部及底部做空内容填充
background: linear-gradient(to bottom, #fff 0, #f2f2f2 100%);
// 这里覆盖cube-scroll-wrapper渐变背景
/deep/ .cube-scroll-content {
background: #fff;
}
}
.footer {
display: flex;
text-align: center;
... ... @@ -597,4 +700,18 @@ export default {
flex: 0 0 6em;
}
}
.info-list-item {
transition: all 0.1s;
}
.info-list-enter,
.info-list-leave-to {
opacity: 0;
transform: translateX(-30%);
}
.info-list-leave-active {
position: absolute;
}
</style>
... ...
... ... @@ -50,6 +50,12 @@ export function defaultProduct() {
export function defaultState() {
return {
products: {},
/**
1: 当前商品对应品牌系统的推荐
2: 商品详情页面取前3
3: 品牌系统-商品列表页面显示前30个商品, 且不能显示当前详情页对应的商品(接口过滤)
*/
topLists: {},
fav: {},
... ...