Authored by 陈峰

Merge branch 'feature/fix' into 'feature/grassV2'

fix updateHeight error



See merge request !9
... ... @@ -10,9 +10,7 @@
:name="`route-view-${yoho.direction}`">
<router-view v-if="!$route.meta.keepAlive"></router-view>
</transition>
<div class="fps" v-if="showFps">
{{fps}}
</div>
<div id="fps" class="fps" v-if="showFps"></div>
</div>
</template>
... ... @@ -52,7 +50,7 @@ export default {
}
if (now - this.time >= 1000) {
this.time = now;
this.fps = this.fpsTick;
document.getElementById('fps').innerText = this.fpsTick;
this.fpsTick = 0;
}
this.fpsTick++;
... ...
... ... @@ -18,6 +18,11 @@ export default {
},
share: Boolean
},
data() {
return {
posting: false
};
},
methods: {
...mapActions(['postComment']),
...articleMapActions(['fetchArticleUpdate']),
... ... @@ -53,6 +58,10 @@ export default {
}
},
async comment(content) {
if (this.posting) {
return;
}
this.posting = true;
const result = await this.postComment({
content: content,
destId: this.addType === 0 ? this.destId : void 0,
... ... @@ -61,6 +70,7 @@ export default {
columnType: this.columnType
});
this.posting = false;
if (result.code === 200) {
const waitData = this.addType === 0 ? this.fetchArticleUpdate({articleId: this.destId}) : Promise.resolve();
... ...
... ... @@ -18,6 +18,12 @@ export default {
resize(index) {
this.$refs.scroll.resize(index);
},
unlockHight(params) {
this.$refs.scroll.unlockHight(params);
},
delete(params) {
this.$refs.scroll.delete(params);
},
init() {
this.$refs.scroll.init();
}
... ...
<template>
<div class="cube-recycle-list">
<div class="cube-recycle-list-main">
<div class="cube-recycle-list-items" :style="{height: heights + 'px'}">
<div ref="items" class="cube-recycle-list-items">
<div
v-for="(item, index) in visibleItems"
:key="index"
v-for="item in visibleItems"
:key="item.index"
class="cube-recycle-list-item"
:class="thumbClass"
:ref="'loads'+index"
:style="getItemStyle(item, index)"
>
<div
v-if="infinite"
:class="{'cube-recycle-list-transition': infinite}"
:style="{opacity: +!item.loaded}"
>
<slot name="tombstone"></slot>
</div>
<div
v-if="!item.placeholder"
:class="{'cube-recycle-list-transition': infinite}"
:style="{opacity: item.loaded}"
>
<slot name="item" :data="{data: item.data, index}"></slot>
:ref="'loads'+item.index"
:style="getItemStyle(item)">
<div v-if="!item.placeholder || !item.height">
<slot name="item" :data="item"></slot>
</div>
</div>
</div>
<div
v-if="!infinite"
class="cube-recycle-list-loading">
<div class="cube-recycle-list-loading">
<slot name="spinner">
<div class="cube-recycle-list-loading-content" v-show="!noMore" :style="{visibility: loadings.length ? 'visible' : 'hidden'}">
<cube-loading class="spinner"></cube-loading>
... ... @@ -54,7 +40,6 @@ export default {
data() {
return {
items: [],
list: [],
heights: this.thumbs.length ? 1000 : 0,
startIndex: 0,
currentIndex: 0,
... ... @@ -91,14 +76,19 @@ export default {
computed: {
visibleItems() {
if (this.thumbsList.length) {
return this.thumbsList.map(item => {
return {
data: item
};
}).concat(this.items.slice(this.thumbsList.length, this.items.length));
return this.thumbItems.concat(this.items.slice(this.thumbsList.length, this.items.length));
}
return this.items;
},
thumbItems() {
return this.thumbsList.map((item, index) => {
return {
data: item,
isThumb: true,
index
};
});
},
tombHeight() {
return this.infinite ? this.$refs.tomb && this.$refs.tomb.offsetHeight : 0;
},
... ... @@ -114,21 +104,6 @@ export default {
};
}
},
watch: {
list(newV) {
if (newV.length) {
this.loadings.pop();
if (!this.loadings.length) {
this.loadItems();
}
}
},
items(newV) {
if (newV.length > this.list.length) {
this.getItems();
}
}
},
mounted() {
this.scrollEvent = throttle(this._onScroll.bind(this), 100);
let supportsPassive = false;
... ... @@ -153,71 +128,68 @@ export default {
init() {
this.load(true);
},
getItemStyle(item, index) {
getItemStyle(item) {
const style = {};
if (!this.isThumb) {
style.transform = `translate3d(0, ${item.top}px, 0)`;
}
if (item.placeholder) {
if (item.height) {
if (!item.unlockHight) {
style.height = `${item.height}px`;
}
} else if (item.willchange) {
style.transition = 'height 300ms cubic-bezier(0.165, 0.84, 0.44, 1)';
style['will-change'] = 'height';
style.height = `${item.height}px`;
} else {
style['z-index'] = this.visibleItems.length - index;
}
if (!item.height && !this.isThumb) {
style.opacity = 0;
} else if (!item.isThumb) {
style.position = 'absolute';
style.top = `${-1000}px`;
style.visibility = 'hidden';
}
return style;
},
load(reload) {
if (this.infinite) {
// increase capacity of items to display tombstone
this.items.length += this.size;
this.loadItems();
} else if ((!this.loadings.length && !this.noMore) || reload) {
if ((!this.loadings.length && !this.noMore) || reload) {
this.getItems(reload);
}
},
getItems(reload) {
if (reload) {
this.noMore = false;
this.list = [];
this.items = [];
}
this.loadings.push('pending');
this.onFetch().then((res) => {
/* istanbul ignore if */
if (!res) {
this.noMore = true;
this.loadings.pop();
} else {
this.list = this.list.concat(res);
this.loadItems(res);
}
});
},
async loadItems() {
let end = this.infinite ? this.items.length : this.list.length;
let item;
async loadItems(list) {
const lastItem = this.items[this.items.length - 1];
const start = lastItem ? lastItem.index + 1 : 0;
for (let i = this.items.length; i < end; i++) {
item = this.items[i];
/* istanbul ignore if */
if (item && item.loaded) {
continue;
}
await this.loadItem(i);
for (let i = 0; i < list.length; i++) {
await this.loadItem(i + start, list[i]);
}
if (this.loadings.length) {
this.loadings.pop();
}
if (this.thumbsList.length) {
this.thumbsList = [];
}
},
loadItem(i) {
loadItem(i, item) {
return new Promise(r => {
this.setItem(i, this.list[i]);
const insertIndex = this.setItem(i, item);
this.$nextTick(() => {
this.updateItemHeight(i);
r();
setTimeout(() => {
this.updateItemHeight(insertIndex);
r();
}, 100);
});
});
},
... ... @@ -226,14 +198,18 @@ export default {
this.thumbsList[index] = data;
this.thumbsList = this.thumbsList.map(item => item);
}
this.$set(this.items, index, {
const insertIndex = this.items.length;
this.$set(this.items, insertIndex, {
data: data || {},
height: 0,
top: -1000,
isTombstone: !data,
loaded: data ? 1 : 0,
placeholder: false
placeholder: false,
unlockHight: false,
willchange: false,
index
});
return insertIndex;
},
updateItemHeight(index, resize) {
if (index === 0 && !resize) {
... ... @@ -243,7 +219,7 @@ export default {
// update item height
let increHeight = 0;
let cur = this.items[index];
let dom = this.$refs[`loads${index}`];
let dom = this.$refs[`loads${cur.index}`];
try {
if (dom && dom[0]) {
... ... @@ -266,18 +242,8 @@ export default {
return increHeight;
},
updateItemTop(startIndex, increHeight) {
// loop all items to update item top and list height
for (let i = startIndex; i < this.items.length; i++) {
let pre = this.items[i - 1];
this.items[i].top = pre ? pre.top + pre.height : 0;
}
this.heights += increHeight;
},
updateIndex() {
// update visible items start index
let top = this.$el.scrollTop;
updateIndex(scrollTop) {
let top = scrollTop;
let hasTopItem = false;
for (let i = 0; i < this.items.length; i++) {
... ... @@ -292,26 +258,65 @@ export default {
}
}
if (hasTopItem) {
this.currentIndex = this.startIndex;
this.currentIndex = this.items[this.startIndex].index;
} else {
this.currentIndex = this.items.length - 1;
this.currentIndex = this.items[this.items.length - 1].index;
}
},
_onScroll() {
const scrollTop = this.$el.scrollTop;
const heights = this.$refs.items.offsetHeight;
// trigger load
if (scrollTop + this.$el.offsetHeight > this.heights - this.offset) {
if (scrollTop + this.$el.offsetHeight > heights - this.offset) {
this.load();
}
this.updateIndex();
this.updateIndex(scrollTop);
this.$emit('scroll', {scrollTop, startIndex: this.currentIndex, item: this.items[this.currentIndex]});
},
resize(index) {
const increHeight = this.updateItemHeight(index, true);
findItemIndex(itemIndex) {
return this.items.findIndex(item => item.index === itemIndex);
},
resize(itemIndex) {
const findItemIndex = this.findItemIndex(itemIndex);
if (findItemIndex >= 0) {
this.updateItemHeight(findItemIndex, true);
}
},
delete(itemIndex) {
const findItemIndex = this.findItemIndex(itemIndex);
if (findItemIndex >= 0) {
this.items[findItemIndex].willchange = true;
let dom = this.$refs[`loads${itemIndex}`];
dom && dom[0].addEventListener('transitionend', () => {
this.items.splice(findItemIndex, 1);
this._onScroll();
}, {
once: true
});
this.$nextTick(() => {
this.items[findItemIndex].height = 0;
});
}
},
unlockHight({index, promise}) {
const findItemIndex = this.findItemIndex(index);
this.updateItemTop(index, increHeight);
if (findItemIndex >= 0) {
const cur = this.items[findItemIndex];
if (cur) {
cur.unlockHight = true;
promise.then(() => {
this.updateItemHeight(findItemIndex, true);
cur.unlockHight = false;
});
}
}
}
},
components: {
... ... @@ -336,7 +341,6 @@ export default {
visibility: hidden
.cube-recycle-list-item
width: 100%
position: absolute
box-sizing: border-box
&.thumb
position relative
... ...
... ... @@ -6,13 +6,15 @@
<ImageFormat :lazy="lazy" class="product-image" :src="product.productImage" :width="136" :height="180"></ImageFormat>
<div class="product-info">
<p class="product-name">{{product.productName}}</p>
<p class="price">¥{{product.salesPrice}}</p>
<div class="price">
¥{{product.salesPrice}}
<AuthComponent
class="btn-fav hover-opacity"
@click="onFav"
:class="favClass">{{favText}}</AuthComponent>
</div>
</div>
</div>
<AuthComponent
class="btn-fav hover-opacity"
@click="onFav"
:class="favClass">{{favText}}</AuthComponent>
</div>
</template>
... ... @@ -128,7 +130,6 @@ export default {
<style lang="scss" scoped>
.product-item {
position: relative;
margin-top: 20px;
margin-right: 20px;
margin-left: 30px;
... ... @@ -182,9 +183,8 @@ export default {
}
.btn-fav {
position: absolute;
bottom: 20px;
right: -20px;
float: right;
margin-right: -40px;
width: 120px;
height: 50px;
padding: 0;
... ...
... ... @@ -2,6 +2,7 @@
<Article
ref="article"
type="article"
:no-header="true"
:thumbs="articleThumbList"
share
:on-fetch="onFetch">
... ...
... ... @@ -117,7 +117,7 @@ export default {
methods: {
...mapMutations(['CHANGE_ARTICLE_LIST_INTRO_HEIGHT', 'CHANGE_ARTICLE_LIST_INTRO']),
init() {
if (this.data.introHeight === 0) {
if (this.data.introHeight === 0 && this.isEllipsis) {
this.CHANGE_ARTICLE_LIST_INTRO_HEIGHT({
articleId: this.data.articleId,
introHeight: get(this.$refs, 'introPool.scrollHeight', 0) + 20,
... ... @@ -189,7 +189,6 @@ export default {
margin-top: 20px;
.intro {
touch-action: none;
font-size: 28px;
color: #4a4a4a;
letter-spacing: 0.06PX;
... ...
... ... @@ -10,7 +10,7 @@
</template>
<script>
import {get, first, debounce} from 'lodash';
import {get, first} from 'lodash';
import ArticleItemHeader from './article-item-header';
import ArticleItemSlide from './article-item-slide';
import ArticleItemIntro from './article-item-intro';
... ... @@ -31,9 +31,6 @@ export default {
type: String,
thumb: Boolean
},
mounted() {
this.expandEvent = debounce(this.onExpandEvent.bind(this), 200);
},
computed: {
articleState() {
return this.articleStates[this.data.articleId] || this.data;
... ... @@ -88,34 +85,21 @@ export default {
},
methods: {
onExpand() {
this.expandEvent();
},
onExpandEvent() {
const $phItem = document.getElementById(`ph${this.index}`);
$phItem.innerHTML = '';
$phItem.status = 0;
this.$nextTick(() => {
this.onResize();
});
this.changeResolve && this.changeResolve();
},
onResize() {
this.$emit('on-resize', this.index);
this.$emit('on-unlock-height', {
index: this.index,
promise: Promise.resolve()
});
},
onExpanding() {
const $phItem = document.getElementById(`ph${this.index}`);
if ($phItem.status === 1) {
return;
}
const $nextItem = document.getElementById(`item${this.index + 1}`);
if ($nextItem) {
$phItem.innerHTML = $nextItem.outerHTML;
}
$phItem.style.zIndex = 999;
$phItem.status = 1;
this.$emit('on-unlock-height', {
index: this.index,
promise: new Promise(r => {
this.changeResolve = r;
})
});
},
onFollow(follow) {
this.$emit('on-follow', follow);
... ... @@ -127,7 +111,7 @@ export default {
this.$emit('on-show-comment', {articleId: this.data.articleId, index: this.index});
},
onShowMore() {
this.$emit('on-show-more', this.data);
this.$emit('on-show-more', {article: this.data, index: this.index});
}
},
components: {ArticleItemHeader, ArticleItemSlide, ArticleItemIntro, ArticleItemComment}
... ...
<template>
<div>
<Layout class="article">
<LayoutHeader theme="white" slot='header' :title="title">
<LayoutHeader v-if="!noHeader" theme="white" slot='header' :title="title">
<template v-if="showHeader">
<div class="avatar-wrapper" @click="toUserPage">
<WidgetAvatar class="widget-avatar" :src="currentAuthor.authorHeadIco" :width="70" :height="70"></WidgetAvatar>
... ... @@ -12,21 +12,19 @@
<WidgetFollow :share="share" class="widget-follow" :author-uid="currentAuthor.authorUid" :follow="currentAuthor.hasAttention === 'Y'" @on-follow="follow => onFollow(currentAuthor, follow)"></WidgetFollow>
</template>
</LayoutHeader>
<LayoutRecycleList :size="10" :thumbs="thumbs" :remove="deleteArticle" ref="scroll" @scroll="onScroll" :offset="2000" :on-fetch="onFetch">
<template class="article-item" #item="{ data: {data, index} }">
<LayoutRecycleList :size="10" :thumbs="thumbs" ref="scroll" @scroll="onScroll" :offset="2000" :on-fetch="onFetch">
<template class="article-item" #item="{ data }">
<ArticleItem
:id="`item${index}`"
:type="type"
:index="index"
:data="data"
:data-id="data.articleId"
:index="data.index"
:data="data.data"
:share="share"
@on-follow="follow => onFollow(data, follow)"
@on-follow="follow => onFollow(data.data, follow)"
@on-resize="onResize"
@on-unlock-height="onUnlockHeight"
@on-show-guang="onShowGuang"
@on-show-comment="onShowComment"
@on-show-more="onShowMore"></ArticleItem>
<div :id="`ph${index}`"></div>
</template>
</LayoutRecycleList>
</Layout>
... ... @@ -61,6 +59,7 @@ export default {
share: Boolean,
type: String,
onFetch: Function,
noHeader: Boolean,
thumbs: {
type: Array,
default() {
... ... @@ -91,7 +90,6 @@ export default {
isShare: false,
authorType: 1
},
deleteArticle: {}
};
},
activated() {
... ... @@ -128,17 +126,17 @@ export default {
this.$refs.comment.init();
});
},
async onShowMore(article) {
async onShowMore({article, index}) {
if (this.yoho && this.yoho.context.isLogin && !this._uid) {
let user = await this.$sdk.getUser();
this._uid = user.uid;
}
this.$refs.moreAction.show(article, this._uid);
this.$refs.moreAction.show(article, this._uid, index);
},
onDelete(articleId) {
this.deleteArticle = {articleId};
onDelete(index) {
this.$refs.scroll.delete(index);
},
onPageReady({success}) {
if (success && this.showCommentAction) {
... ... @@ -150,6 +148,9 @@ export default {
this.showCommentActioning = false;
}
},
onUnlockHeight(params) {
this.$refs.scroll.unlockHight(params);
},
onClose() {
this.$refs.commentAction.hide();
},
... ...
... ... @@ -45,7 +45,8 @@ export default {
methods: {
...articleStore.mapActions(['reportArticle', 'deleteArticle']),
...userStore.mapActions(['followUser']),
show(params, uid) {
show(params, uid, index) {
this.index = index;
params = params || {};
this.list[0].hide = params.hasAttention !== 'Y';
... ...