Authored by 陈峰

merge

<template>
<div class="recycle-scroll-reveal" v-bind="$attrs" v-on="$listeners">
<div class="recycle-scroll-reveal-main">
<div class="recycle-scroll-reveal-main" ref="scroll">
<div ref="eternal" class="eternal-top">
<slot name="eternalTop"></slot>
</div>
... ... @@ -46,8 +46,8 @@ export default {
colsHeight: [],
items: [],
itemWidth: 0,
currentItems: [],
noMore: false
noMore: false,
startIndexs: [0, 0]
};
for (let i = 0; i < this.cols; i++) {
... ... @@ -136,7 +136,6 @@ export default {
this.$refs.scroll.resize(index);
},
init() {
this.currentItems = [];
this.colsHeight = [];
this.itemWidth = Math.floor(this.$el.offsetWidth / this.cols);
... ... @@ -192,36 +191,40 @@ export default {
}, i <= lastIndex ? (startCol + i) % this.cols : -1);
}
for (let i = 0; i < this.cols; i++) {
let loop = true;
let col = this[this.colPrefix + i];
let k = col.length - 1;
this.$nextTick(() => {
for (let i = 0; i < this.cols; i++) {
let loop = true;
let col = this[this.colPrefix + i];
let k = col.length - 1;
while (loop) {
let cur = col[k];
while (loop) {
let cur = col[k];
if (!cur || cur.height) {
loop = false;
continue;
}
if (!cur || cur.height) {
loop = false;
continue;
}
let dom = this.$refs[`items${cur.index}`];
let dom = this.$refs[`items${cur.index}`];
try {
if (dom && dom[0]) {
cur.height = dom[0].offsetHeight;
cur.top = dom[0].offsetTop;
}
} catch (error) {
const message = `cur_${typeof cur}, dom_ ${typeof dom}, column_${i}, index_ ${index}, length_ ${this.items.length}, ${error ? error.message : ''}`;
try {
if (dom && dom[0]) {
cur.height = dom[0].offsetHeight;
cur.top = dom[0].offsetTop;
throw new Error(message);
}
} catch (error) {
const message = `cur_${typeof cur}, dom_ ${typeof dom}, column_${i}, index_ ${index}, length_ ${this.items.length}, ${error ? error.message : ''}`;
throw new Error(message);
this.$set(this[this.colPrefix + i], k, cur);
k--;
}
this.$set(this[this.colPrefix + i], k, cur);
k--;
}
}
});
return true;
},
loadItem(item, colIndex) {
return new Promise(r => {
... ... @@ -272,37 +275,30 @@ export default {
let arr = [];
for (let i = 0; i < this.cols; i++) {
arr.push(this.updateColumnCurrentItems (i, top));
arr.push(this.updateColumnCurrentItems(i, top));
}
this.currentItems = arr;
},
updateColumnCurrentItems (index, top) {
updateColumnCurrentItems(index, top) {
let col = this[this.colPrefix + index];
let startIndex = Math.max(0, Math.floor(col.length * top / this.$refs['col'+index][0].offsetHeight) - this.size);
let endIndex = col.length;
let startIndex = this.startIndexs[index];
let hasTopItem = false;
while (startIndex < endIndex) {
if (col[startIndex].top > top) {
endIndex = startIndex - 1;
for (let i = 0; i < col.length; i++) {
if ((i < startIndex - this.size || i > startIndex + this.size) && col[i].height) {
this.$set(col[i], 'placeholder', true);
} else {
this.$set(col[i], 'placeholder', false);
}
startIndex++;
}
for (let i = Math.max(0, Math.floor(endIndex - this.size * 2.5)); i < Math.min(this.size * 5, col.length); i++) {
let placeholder = Math.abs(i - endIndex) > this.size;
if (col[i].placeholder !== placeholder) {
this.$set(col[i], 'placeholder', placeholder);
if (!hasTopItem && col[i].top > top) {
startIndex = Math.max(0, i - 1);
hasTopItem = true;
}
}
return col[endIndex];
this.startIndexs[index] = startIndex;
},
_updateList() {
const scrollTop = this.$el.scrollTop;
const heights = this.$el.offsetHeight;
const heights = this.$refs.scroll.offsetHeight;
// trigger load
if (scrollTop + this.$el.offsetHeight > heights - this.offset) {
... ...
... ... @@ -93,10 +93,21 @@ export default {
this.title = `@${first.authorName} 在有货逛上发了一篇笔记,快点开看看!`;
if (!this.$yoho.isApp) {
let shareImage = '';
let shareIntro = '';
get(first, 'blockList', []).forEach(block => {
if (block.templateKey === 'image') {
!shareImage && (shareImage = (block.contentData || '').split('?')[0]);
} else if (block.templateKey === 'text') {
!shareIntro && (shareIntro = block.contentData);
}
});
Share.setShareInfo({
title: this.title,
imgUrl: first.shareImage,
desc: first.intro
imgUrl: shareImage,
desc: shareIntro
});
}
}
... ...
... ... @@ -17,7 +17,8 @@
v-if="!thumb || inx === 0"
:lazy="true"
:data="item"
:thumb-size="firstBlockSize">
:thumb-size="firstBlockSize"
:share="share">
</ArticleItemSlideImage>
</SlideItem>
</Slide>
... ...
... ... @@ -2,8 +2,8 @@
<div class="article-item">
<slot>
<div class="article-item-main">
<div class="layer-image" :style="`height: ${width * coverImage.scale}px`" @click="toArticlePage">
<ImageFormat :mode="1" :src="coverImage.contentData" :width="imgWidth" :height="Math.floor(coverImage.scale * imgWidth)"></ImageFormat>
<div class="layer-image" :style="`height: ${Math.floor(width * coverImage.scale)}px`" @click="toArticlePage">
<ImageFormat :mode="1" :src="coverImage.contentData" :width="imgWidth" :height="Math.floor(coverImage.scale * imgWidth)" :style="imageStyle"></ImageFormat>
</div>
<div v-if="intro" class="description" @click="toArticlePage">{{intro}}</div>
<div class="attribution">
... ... @@ -21,7 +21,7 @@
</template>
<script>
import {get, first} from 'lodash';
import {get, round} from 'lodash';
import YAS from 'utils/yas-constants';
import {createNamespacedHelpers} from 'vuex';
... ... @@ -31,7 +31,7 @@ export default {
name: 'ArticleItem',
data() {
return {
imgWidth: 370
imgWidth: 500
}
},
props: {
... ... @@ -61,6 +61,15 @@ export default {
return img;
},
imageStyle() {
let width = 370;
let height = Math.floor(this.coverImage.scale * width);
return {
width: `${round(width / 40, 2)}rem`,
height: `${round(height / 40, 2)}rem`,
};
},
intro() {
return get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData', '')
},
... ... @@ -149,14 +158,6 @@ export default {
overflow: hidden;
}
.layer-image {
img {
width: 100%;
height: 100%;
display: block;
}
}
.description {
margin: 14px 20px;
font-size: 24px;
... ... @@ -173,6 +174,8 @@ export default {
}
.attribution {
overflow: hidden;
height: 40px;
margin: 20px;
display: flex;
justify-content: space-between;
... ...
... ... @@ -84,8 +84,13 @@ export default {
},
mounted() {
if (!this.yoho.context.userHeadIco) {
this.fetchUserProfile();
this.$sdk.getUser().then(user => {
if (user && user.uid) {
this.fetchUserProfile();
}
});
}
this.scrollEvent = throttle(this.onDounceScroll.bind(this), 100);
this.reportShow = this.startReportShow();
},
... ...
... ... @@ -82,7 +82,7 @@ export default {
this.$yoho.share({
title: this.data.topicName,
imgUrl: this.data.topicImageUrl,
link: `${location.origin}/grass/topic/share/${this.data.topicId}/${this.data.topicName}`,
link: `${location.origin}/grass/topic/share/${this.data.topicId}`,
desc: '我在有货的社区发现一个热门话题。' + this.data.topicDesc,
hideType: ['7', '8', '9']
});
... ...
... ... @@ -31,6 +31,14 @@ export default [{
keepAlive: true
}
}, {
path: '/topic/share/:topicId',
alias: '/topic/share/:topicId',
name: 'topic.share',
component: () => import(/* webpackChunkName: "article" */ './topic'),
meta: {
keepAlive: true
}
}, {
path: '/topic/:topicId/:topicName',
alias: '/topic/:topicId/:topicName',
name: 'topic',
... ... @@ -40,14 +48,6 @@ export default [{
statusBarColor: 'white'
}
}, {
path: '/topic/share/:topicId/:topicName',
alias: '/topic/share/:topicId/:topicName',
name: 'topic.share',
component: () => import(/* webpackChunkName: "article" */ './topic'),
meta: {
keepAlive: true
}
}, {
path: '/article/:articleId/comment',
alias: '/article/:articleId/comment',
name: 'article.comment',
... ...
... ... @@ -3,7 +3,7 @@
<Layout class="article">
<TopicHeader ref="header" :title="topicTitle" :step="headerAnimateStep" :data="topicInfo" :share="share" @on-follow="onFollowTopic"></TopicHeader>
<RecycleScrollReveal v-if="topicInfo.viewModel == 2" :size="20" :thumbs="thumbs" ref="scroll" @scroll="onScroll" :offset="2000" :on-fetch="onFetch" :manual-init="true"
<RecycleScrollReveal v-if="topicInfo.viewModel == 2" :size="5" :thumbs="thumbs" ref="scroll" @scroll="onScroll" :offset="800" :on-fetch="onFetch" :manual-init="true"
@on-inited="onInited">
<template v-slot:eternalTop>
<TopicBanner ref="topicHead" :data="topicInfo" :share="share" @on-follow="onFollowTopic"></TopicBanner>
... ... @@ -122,8 +122,13 @@ export default {
}
if (!this.yoho.context.userHeadIco) {
this.fetchUserProfile();
this.$sdk.getUser().then(user => {
if (user && user.uid) {
this.fetchUserProfile();
}
});
}
this.scrollEvent = throttle(this.onDounceScroll.bind(this), throttleTime);
this.reportShow = this.startReportShow();
this.colWidthForTwo = Math.floor(this.$el.offsetWidth / 2);
... ... @@ -231,7 +236,7 @@ export default {
let step = Math.round((scrollTop - 10) / (this._topicHeaderHeight - this._headerHeight) * 100);
if (this.topicInfo.topicImageUrl) {
if (step && this.topicInfo.topicImageUrl) {
this.headerAnimateStep = Math.max(Math.min(step, 100), 0);
}
... ... @@ -265,10 +270,12 @@ export default {
this.headerAnimateStep = 100;
}
if (this.$refs.scroll) {
this.$refs.scroll.$el.scrollTop = 0;
this.$refs.scroll.init();
}
this.$nextTick(() => {
if (this.$refs.scroll) {
this.$refs.scroll.$el.scrollTop = 0;
this.$refs.scroll.init();
}
});
});
},
async onFetch() {
... ...
... ... @@ -77,7 +77,7 @@ function init(qs) {
if (/MicroMessenger/i.test(navigator.userAgent)) {
loadScript('//res.wx.qq.com/open/js/jweixin-1.3.2.js', () => {
jsonp(location.protocol + '//m.yohobuy.com/activity/wechat/share', {
url: location.href
url: encodeURIComponent(location.href.split('#')[0])
}).then(res => {
if (window.wx) {
window.wx.config({
... ... @@ -113,9 +113,10 @@ function init(qs) {
export default {
init,
setShareInfo(data) {
Object.assign(shareData, data);
shareData.link = shareData.link || location.href;
shareData.title = data.title;
shareData.desc = data.desc || '逛';
shareData.link = data.link || location.href;
shareData.imgUrl = data.imgUrl || shareData.imgUrl;
if (window.wx) {
setWxShareData();
... ...
... ... @@ -105,7 +105,7 @@ export default function(mergeState = {}) {
}
},
actions: {
async fetchUserProfile({commit}) {
async fetchUserProfile({commit, state}) {
const result = await this.$api.get('/api/grass/userProfile');
if (result && result.code === 200) {
... ...
module.exports = [
{
route: /grass\/article\/\d+$/,
cacheKey: '$url',
query: {
columnType: Number
},
cacheTime: 900,
cache: true,
protocol: true
},
{
route: /grass\/article\/\d+\/user/,
cacheKey: '$url',
cache: false
},
{
route: /grass\/article\/share\/\d+/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/article\/detail\/\d+/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/topic\/\d+\/(.*)/,
cacheKey: '$url$yoho-protocol',
cacheTime: 900,
cache: true
},
{
route: /grass\/author\/\d+\/\d+/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/author\/mine/,
cacheKey: '$url',
cacheTime: 900,
cache: true,
protocol: true
},
{
route: /grass\/author\/follow\/\d+\/\d+/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/author\/fans\/\d+\/\d+/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/mine\/follow/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
{
route: /grass\/mine\/fans/,
cacheKey: '$url',
cacheTime: 900,
cache: true
},
... ...
... ... @@ -53,31 +53,6 @@ const asyncLoadScripts = (renderScripts) => {
return scripts;
};
const versionToNumber = (str, len = 3) => {
let verNum = 0;
const ver = str.split('.').map(i => Number(i));
for (let i = 0; i < len; i++) {
verNum += (ver[i] || 0) * Math.pow(100, len - i - 1);
}
return verNum;
}
const getImmersedStatus = (req) => {
let status = false;
if (req.yoho.isYohoApp && req.yoho.isiOS) {
let appVersion = req.cookies.app_version || req.query.app_version;
if (appVersion && versionToNumber(appVersion) >= versionToNumber('6.9.2')) {
status = true;
}
}
return status;
}
const getContext = (req) => {
return {
url: req.url,
... ... @@ -107,13 +82,20 @@ const handlerError = (err = {}, req, res, next) => {
return next(err);
};
const getCacheKey = (req, cacheKey = '') => {
const getCacheKey = (req, route) => {
const urlObj = url.parse(req.url);
const isIos = req.yoho.isiOS;
let ck = urlObj.pathname;
if (route.query) {
const qks = Object.keys(route.query);
ck += `?${qks.map(qk => `${qk}=${req.query && req.query[qk] || ''}`).join('&')}`;
}
ck += `|${isIos ? 'ios' : 'android'}`;
return md5(cacheKey
.replace('$url', urlObj.pathname)
.replace('$params', urlObj.query || '') + (isIos ? 'ios' : 'android'));
return md5(ck);
};
const render = (route) => {
... ... @@ -125,7 +107,7 @@ const render = (route) => {
if (isDegrade) {
return res.send(degradeHtml);
}
const ck = route.cacheKey ? getCacheKey(req, route.cacheKey) : void 0;
const ck = getCacheKey(req, route);
if (config.useCache && route.cache && ck) {
try {
... ... @@ -161,13 +143,15 @@ const render = (route) => {
let resources = context.renderResourceHints();
const states = context.renderState();
let asyncScripts;
let zk = {};
let zk = {
asyncJs: _.get(req.app.locals.wap, 'webapp.ios-async-js', true)
};
if (process.env.NODE_ENV === 'production') {
zk.webperf = _.get(req.app.locals.wap, 'open.webperf', false);
zk.asyncJs = _.get(req.app.locals.wap, 'webapp.ios-async-js', true);
}
scripts = scripts.replace(/defer/g, 'defer crossorigin="anonymous"');
if (req.yoho.isiOS && zk.asyncJs) {
asyncScripts = asyncLoadScripts(scripts);
}
... ... @@ -180,8 +164,7 @@ const render = (route) => {
resources,
states,
zk,
routeHash: routeEncode.getRouter(req),
needImmersed: getImmersedStatus(req)
routeHash: routeEncode.getRouter(req)
});
if (config.useCache && route.cache && ck) {
... ... @@ -198,7 +181,7 @@ const devRender = (route) => {
return async(req, res, next) => {
try {
res.setHeader('X-YOHO-Version', pkg.version);
const ck = route.cacheKey ? getCacheKey(req, route.cacheKey) : void 0;
const ck = getCacheKey(req, route);
const isDegrade = _.get(req.app.locals.wap, `webapp.${config.appName}-degrade`, false);
... ... @@ -242,14 +225,14 @@ const devRender = (route) => {
}
let {styles, scripts, resources, states, html} = msg;
scripts = scripts.replace(/defer/g, 'defer crossorigin="anonymous"');
const result = template({
html,
styles,
scripts,
resources,
states,
routeHash: routeEncode.getRouter(req),
needImmersed: getImmersedStatus(req)
routeHash: routeEncode.getRouter(req)
});
if (config.useCache && route.cache && ck) {
... ...
... ... @@ -23,13 +23,11 @@
<script type="text/javascript">
(function(d,c){var e=d.documentElement,a="orientationchange" in window?"orientationchange":"resize",b=function(){var f=e.clientWidth;if(!f){return}if(f>=750){e.style.fontSize="40px"}else{e.style.fontSize=40*(f/750)+"px"}};if(!d.addEventListener){return}b();c.addEventListener(a,b,false);d.addEventListener("DOMContentLoaded",b,false)})(document,window);
</script>
{{#if needImmersed}}
</head>
<body>
<script type="text/javascript">
(function(d){var e=d.documentElement,n=(e.clientHeight/e.clientWidth)>2.1?'-high':'';d.getElementsByTagName("body")[0].className="immerse-body" + n})(document);
(function (d) {function vtn(e){var t,a=0,n=e.split(".").map(e=>Number(e));for(t=0;t<3;t++)a+=(n[t]||0)*Math.pow(100,2-t);return a}function gav(e,t){var a=e.match(new RegExp("(^|)app_version=([^"+t+"]*)("+t+"|$)"));return a&&a.length?a[2]:""}var appv=gav(d.cookie,";")||gav(location.href,"&");if(/YohoBuy/i.test(navigator.userAgent||"")&&/\(i[^;]+;( U;)? CPU.+Mac OS X/i.test(navigator.userAgent||"")&&appv&&vtn(appv)>=vtn("6.9.2")){var e=d.documentElement,n=e.clientHeight/e.clientWidth>2.1?"-high":"";d.getElementsByTagName("body")[0].className="immerse-body"+n}})(document);
</script>
{{/if}}
{{{html}}}
<div id="degrade-app"></div>
<div id="main-wrap">
... ...