Authored by TaoHuang

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

... ... @@ -5,6 +5,9 @@
</template>
<script>
import { mapGetters } from 'vuex';
import queryString from 'querystring'
export default {
name: 'LayoutLink',
props: {
... ... @@ -14,6 +17,9 @@ export default {
},
reportEvent: String,
},
computed: {
...mapGetters(['getLogin'])
},
watch: {
report: function(newVal) {
},
... ... @@ -24,6 +30,13 @@ export default {
if (!this.href) {
return;
}
// 资源位跳转 领券等需判断是否登录
// 判断条件为查询参数 isNeedLogin
const qsParams = queryString.parse(this.href.split('?')[1]);
if(!this.getLogin && +qsParams.isNeedLogin === 1) {
this.$yoho.auth();
return;
}
this.$xianyu.goXianyuNewPage({
url: this.href
... ...
... ... @@ -320,7 +320,7 @@ export default {
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/008/coupon/1/4.png',
token: 'b13ebf9a-e5bc-4dec-8904-975008daaff5'
token: 'b13ebf9a-e5bc-4dec-8904-975007daaff5'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/008/coupon/1/5.png',
... ... @@ -443,7 +443,7 @@ export default {
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/008/coupon/2/3.png',
token: '4ce7d50c-fa8e-47e8-81cc-4700854d5cbc'
token: '4ce7d50c-fa8e-47e8-81cc-4700754d5cbc'
},
{
image: '//ad.yoho.cn/html5/2019/10/activity/008/coupon/2/4.png',
... ...
... ... @@ -2,9 +2,10 @@
<layout-app title="商品列表" class="brand-product-list">
<LayoutScroll
class="scroll-view"
ref="pageScroll"
@scroll-end="scrollEndHandler">
<div class="list-wrapper" v-if="brandProductList != null">
<product-list ref="productList" :list="brandProductList" priceKey="price" :yas-params="recommendYasParams"/>
<product-list :list="brandProductList" priceKey="price" :yas-params="recommendYasParams"/>
</div>
</LayoutScroll>
</layout-app>
... ... @@ -17,10 +18,12 @@ import ProductList from '../list/components/productList';
const STORE_PATH = 'product';
const { mapState } = createNamespacedHelpers(STORE_PATH);
import trackingMixins from './tracking-mixins';
export default {
name: 'BrandProductList',
props: ['productId'],
mixins: [trackingMixins],
components: {
ProductList,
ScrollView,
... ... @@ -60,17 +63,52 @@ export default {
return this.topLists[this.$route.params.productId];
},
},
watch: {
brandProductList(list) {
if (list && list.length > 0) {
this.listDataDirty = true;
this.scrollEndHandler();
}
}
},
asyncData({store, router}) {
return store.dispatch(`${STORE_PATH}/fetchBrandTop`, {productId: router.params.productId});
},
activated() {
this.$refs.productList && this.$refs.productList.yasShowEvent(0);
this.scrollEndHandler();
},
methods: {
scrollEndHandler({y}) {
const scrollTop = Math.abs(y);
collectTrackingInfo(viewInfo, force = false) {
if (!this.yasTargets) {
this.yasTargets = {};
}
if (this.brandProductList && this.brandProductList.length > 0 && (!this.yasTargets.productItem0 || force)) {
let productElList = document.querySelectorAll('.product-list-item');
if (productElList && productElList.length > 0) {
this.brandProductList.forEach((item, idx) => {
if (productElList[idx] && item) {
const id = `productItem${idx}`;
this.$refs.productList && this.$refs.productList.yasShowEvent(scrollTop);
/**
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
this.yasTargets[id] = {
el: {
offsetTop: productElList[idx].offsetTop,
offsetHeight: productElList[idx].offsetHeight,
},
yasParams: {...this.recommendYasParams, I_INDEX: idx + 1, PRD_SKN: item.id},
};
}
});
}
}
},
},
};
... ...
... ... @@ -30,8 +30,8 @@
</div>
<div class="info-name"><div>{{productDetail.product_name}}</div></div>
</div>
<a class="banner" v-if="resource.src" @click.prevent="gotoNewPage">
<img-size ref="resourceImg" :src="sizeImg(resource.src)"/>
<a class="banner" ref="resourceImg" v-if="resource.src" @click.prevent="gotoNewPage">
<img-size :src="sizeImg(resource.src)"/>
</a>
<div class="info">
<transition-group name="info-list" tag="div" class="info-list">
... ... @@ -96,12 +96,13 @@ import BuySheet from './components/buy-sheet';
import TopList from './components/top-list';
import SquareImg from './components/square-img';
import stateShortCutsMixins from './mixins';
import trackingMixins from './tracking-mixins';
const { mapActions, mapState } = createNamespacedHelpers('product');
export default {
name: 'ProductDetail',
mixins: [stateShortCutsMixins],
mixins: [stateShortCutsMixins, trackingMixins],
components: {
SizeSelectSheet,
ActivityListSheet,
... ... @@ -173,11 +174,11 @@ export default {
}, {
text: '发售时间',
value: this.productDetail.sale_time
},
},
// {
// text: '发售价格',
// value: this.productDetail.offer_price
// },
// },
{
text: '货号',
value: this.productDetail.product_code
... ... @@ -201,6 +202,24 @@ export default {
showSizeSelectSheet: 'onSizeSelectSheetHide',
showSizeRequestSheet: 'onSizeRequestHide',
};
// create watcher for list data
this.watchList = [];
['resource.url', 'topList', 'recommend'].forEach(key => {
this.watchList.push(this.$watch(key, val => {
if (val) {
this.listDataDirty = true;
}
}));
});
},
beforeDestroy() {
if (this.watchList) {
this.watchList.forEach(item => {
item && item();
});
this.watchList = null;
}
},
asyncData({store, router}) {
const productId = parseInt(router.params.productId, 10);
... ... @@ -233,17 +252,15 @@ export default {
}
if (this.resource && this.resource.url) {
this.yasResourceVisible();
this.yasResourceAvailable();
} else {
this._resourceImgWatcher = this.$watch(() => {
return this.resource && this.resource.url;
}, (url) => {
if (url) {
this._resourceImgWatcher && this._resourceImgWatcher();
this.yasResourceVisible();
this.yasResourceAvailable();
}
}, {
immediate: true,
});
}
... ... @@ -318,96 +335,80 @@ export default {
refresh() {
this.$refs.slide && this.$refs.slide.refresh && this.$refs.slide.refresh();
},
scrollEndHandler({y}) {
const scrollTop = Math.abs(y);
const pageScrollHeight = this.$refs.pageScroll.$el.offsetHeight;
// 相关商品
if (!this._topListYas && this.$refs.topList) {
if (this._topListTop === undefined) {
this._topListTop = this.$refs.topList.$el.offsetTop;
}
if (scrollTop < this._topListTop && (scrollTop + pageScrollHeight) > this._topListTop) {
const DATA = this.topList.slice(0, 3).map((value, i) => {
return {...this.recommendYasParams, I_INDEX: i + 1, PRD_SKN: value.id, POS_ID: 1};
});
/**
*
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
this.$store.dispatch('reportYas', {
params: {
param: {DATA},
appop: 'XY_UFO_SHOW_EVENT'
}
});
this._topListYas = true;
}
collectTrackingInfo(viewInfo, force) {
if (!this.yasTargets) {
this.yasTargets = {};
}
// 推荐商品
if (this._productItemHeight === undefined) {
let item = document.querySelector('.product-list-item');
if (item) {
this._productItemHeight = item.offsetHeight;
this._productListOffsetTop = item.offsetTop;
}
// banner
if (this.$refs.resourceImg && this.resource.url && (!this.yasTargets.banner || force)) {
/**
* 商品详情页中的资源位曝光
* XY_UFO_SHOW_EVENT
* 1.P_NAME:当前页面名称,XY_UFOProductDetail;
* 2.P_PARAM:当前页面资源位code;
* 3.PRD_ID:商品ID;
* 4.ACTION_URL:资源位跳转URL;
*/
this.yasTargets.banner = {
el: {
offsetTop: this.$refs.resourceImg.offsetTop,
offsetHeight: this.$refs.resourceImg.offsetHeight,
},
yasParams: {
P_NAME: 'XY_UFOProductDetail',
P_PARAM: this.resourceContentCode,
PRD_ID: this.productId,
ACTION_URL: this.resource.url,
}
};
}
if (this.$refs.recommendList && this._productItemHeight) {
const productListVisibleHeight = scrollTop + pageScrollHeight - this._productListOffsetTop;
if (productListVisibleHeight > 0) {
const maxRow = Math.floor(productListVisibleHeight / this._productItemHeight);
const listScrollTop = Math.max(0, scrollTop - this._productListOffsetTop);
const row = Math.floor(listScrollTop / this._productItemHeight);
const list = this.recommend.slice(row * 2, (row + maxRow) * 2).filter(item => !item.yas);
if (list.length > 0) {
const DATA = list.map(item => {
item.yas = true;
return {...this.recommendYasParams, I_INDEX: ++this.recommendYasIndex, PRD_SKN: item.id};
});
// 相关商品
if (this.$refs.topList && this.topList && (!this.yasTargets.topList0 || force)) {
const elInfo = {
offsetTop: this.$refs.topList.$el.offsetTop,
offsetHeight: this.$refs.topList.$el.offsetHeight,
};
/**
* 1.P_NAME:页面名称,XY_UFOProductDetail;
* 2.P_PARAM:页面参数;
* 3.I_INDEX:曝光顺序;
* 4.PRD_SKN:商品id
* 5.POS_ID: 1:相关商品,2: 推荐推荐,3: 相关商品列表页面
*/
this.topList.slice(0, 3).forEach((value, i) => {
this.yasTargets[`topList${i}`] = {
el: elInfo,
yasParams: {...this.recommendYasParams, I_INDEX: i + 1, PRD_SKN: value.id, POS_ID: 1},
};
});
}
this.$store.dispatch('reportYas', {
params: {
param: {DATA},
appop: 'XY_UFO_SHOW_EVENT'
}
});
}
// 推荐商品
if (this.$refs.recommendList && this.recommend && this.recommend.length > 0 && (!this.yasTargets.productItem0 || force)) {
let productElList = document.querySelectorAll('.product-list-item');
if (productElList && productElList.length > 0) {
this.recommend.forEach((item, i) => {
if (productElList[i] && item) {
const id = `productItem${i}`;
this.yasTargets[id] = {
el: {
offsetTop: productElList[i].offsetTop,
offsetHeight: productElList[i].offsetHeight,
},
yasParams: {...this.recommendYasParams, I_INDEX: i + 1, PRD_SKN: item.id},
};
}
});
}
}
},
yasResourceVisible() {
/**
* 商品详情页中的资源位曝光
* XY_UFO_SHOW_EVENT
* 1.P_NAME:当前页面名称,XY_UFOProductDetail;
* 2.P_PARAM:当前页面资源位code;
* 3.PRD_ID:商品ID;
* 4.ACTION_URL:资源位跳转URL;
*/
this.$store.dispatch('reportYas', {
params: {
appop: 'XY_UFO_SHOW_EVENT',
param: {
P_NAME: 'XY_UFOProductDetail',
P_PARAM: this.resourceContentCode,
PRD_ID: this.productId,
ACTION_URL: this.resource.url,
},
}
});
yasResourceAvailable() {
this.scrollEndHandler();
if (this._resourceImgWatcher) {
this._resourceImgWatcher();
this._resourceImgWatcher = null;
... ... @@ -421,8 +422,8 @@ export default {
// 加载商品详情数据
loadData(productId = this.productId) {
this.fetchBrandTop({productId});
this.fetchFav({productId});
this.fetchBrandTop({ productId });
this.fetchFav({ productId });
},
/**
... ...
export default {
deactivated() {
this.preVisibleEls = null;
},
methods: {
isVisible(offsetInfo, viewInfo, ratio = 0.5) {
return !((offsetInfo.offsetTop + offsetInfo.offsetHeight * (1 - ratio)) < viewInfo.top ||
(offsetInfo.offsetTop + offsetInfo.offsetHeight * ratio) > viewInfo.bottom);
},
scrollEndHandler({y = this.$refs.pageScroll && this.$refs.pageScroll.$el.offsetTop} = {}) {
const scrollTop = Math.abs(y);
let pageScrollHeight = this.$refs.pageScroll.$el.offsetHeight;
if (pageScrollHeight === 0) {
const app = document.querySelector('#app');
const header = document.querySelector('.layout-header');
const footer = document.querySelector('.footer');
pageScrollHeight = app && app.offsetHeight || window.innerHeight;
if (header) {
pageScrollHeight -= header.offsetHeight;
}
if (footer) {
pageScrollHeight -= footer.offsetHeight;
}
}
const viewInfo = {
top: scrollTop,
bottom: scrollTop + pageScrollHeight,
};
this.collectTrackingInfo(viewInfo, this.listDataDirty);
const visibleEls = Object.keys(this.yasTargets).filter(key => {
return this.isVisible(this.yasTargets[key].el, viewInfo);
});
let visibleChangedEls;
if (!this.preVisibleEls) {
visibleChangedEls = visibleEls;
} else {
visibleChangedEls = visibleEls.filter(key => {
return !this.preVisibleEls.includes(key);
});
}
this.preVisibleEls = visibleEls;
if (visibleChangedEls.length > 0) {
const DATA = visibleChangedEls.map(key => this.yasTargets[key].yasParams);
this.$store.dispatch('reportYas', {
params: {
param: {DATA},
appop: 'XY_UFO_SHOW_EVENT'
}
});
}
this.listDataDirty = false;
},
},
};
... ...
... ... @@ -3,15 +3,18 @@ module.exports = [
route: /xianyu\/index\/channel/,
cacheTime: 60,
cache: true,
pageName: 'channel'
},
{
route: /xianyu\/index/,
cacheTime: 60,
cache: true,
pageName: 'channel'
},
{
route: /xianyu\/index\/category/,
cacheTime: 30,
cache: true,
pageName: 'category'
},
];
... ...
... ... @@ -8,6 +8,7 @@ const pkg = require('./package.json');
const devtools = require('./doraemon/middleware/devtools');
const _ = require('lodash');
const uuid = require('uuid');
const favicon = require('serve-favicon');
// 全局注册library
yohoLib.global(config);
... ... @@ -38,6 +39,7 @@ exports.createApp = async(app) => {
}
app.set('etag', false);
app.use(favicon(path.join(__dirname, './favicon.ico')));
app.use('/xianyu/node/status.html', (req, res) => {
res.status(200).end();
... ...
... ... @@ -8,14 +8,14 @@ exports.serverError = (err, req, res, next) => { // eslint-disable-line
logger.error(`error at path: ${req.url}`);
logger.error(`${req.url},${typeof err === 'object' ? JSON.stringify(err) : err}`);
res.status(err.code || 500);
if (req.xhr) {
return res.json({
code: 500,
message: '服务器错误!'
code: err.code || 500,
message: err.message || '服务器错误!'
});
}
res.status(err.code || 500);
return res.send('服务器开小差了~');
};
... ...
... ... @@ -140,6 +140,7 @@ const render = (route) => {
app: config.appName,
ip: req.yoho.clientIp,
path: req.url,
pageName: _.get(route, 'pageName', 'unknown'),
uid: req.user.uid,
udid: req.cookies.udid || '',
clientType: req.yoho.clientType || '',
... ...
No preview for this file type
{
"name": "xianyu-ufo-app-web",
"version": "0.0.1-beta-40",
"version": "0.0.2-beta-1",
"private": true,
"description": "Xianyu Project With Express",
"repository": {
... ...