fix: 修复文章归档功能

This commit is contained in:
小莫唐尼 2024-10-17 17:19:39 +08:00
parent a98254e6cc
commit e28ea6ca08
2 changed files with 1039 additions and 897 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,379 +1,519 @@
<template> <template>
<view class="app-page"> <view class="app-page">
<view class="e-fixed"> <view class="e-fixed">
<tm-tabs color="light-blue" v-model="tab.activeIndex" :list="tab.list" align="center" @change="fnOnTabChange"></tm-tabs> <tm-tabs color="light-blue" v-model="tab.activeIndex" :list="tab.list" align="center"
</view> @change="fnOnTabChange"></tm-tabs>
<!-- 占位区域 --> </view>
<view style="width: 100vw;height: 90rpx;"></view> <!-- 占位区域 -->
<view style="width: 100vw;height: 90rpx;"></view>
<!-- 骨架屏加载区域 --> <!-- 骨架屏加载区域 -->
<view v-if="loading != 'success'" class="loading-wrap"> <view v-if="loading != 'success'" class="loading-wrap">
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
</view> </view>
<!-- 加载完成区域 --> <!-- 加载完成区域 -->
<block v-else> <block v-else>
<view v-if="dataList.length == 0" class="list-empty flex flex-center"> <view v-if="dataList.length == 0" class="list-empty flex flex-center">
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无归档的文章"></tm-empty> <tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无归档的文章"></tm-empty>
</view> </view>
<view v-else class="e-timeline tm-timeline mt-24"> <view v-else class="e-timeline tm-timeline mt-24">
<block v-for="(item, index) in dataList" :key="index"> <block v-for="(item, index) in dataList" :key="index">
<view class="tm-timeline-item tm-timeline-item--leftDir"> <view class="tm-timeline-item tm-timeline-item--leftDir">
<view style="width: 160rpx;"> <view style="width: 160rpx;">
<view :style="{ width: '24rpx', height: '24rpx' }" :class="[black_tmeme ? 'bk' : '']" <view :style="{ width: '24rpx', height: '24rpx' }" :class="[black_tmeme ? 'bk' : '']"
class="flex-center rounded tm-timeline-jidian border-white-a-2 grey-lighten-2 light-blue shadow-primary-4"></view> class="flex-center rounded tm-timeline-jidian border-white-a-2 grey-lighten-2 light-blue shadow-primary-4">
<view :style="{ marginTop: '-24rpx' }" </view>
:class="[index !== dataList.length - 1 ? 'tm-timeline-item-boder' : '', black_tmeme ? 'bk' : '']" <view :style="{ marginTop: '-24rpx' }"
class="grey-lighten-2"></view> :class="[index !== dataList.length - 1 ? 'tm-timeline-item-boder' : '', black_tmeme ? 'bk' : '']"
</view> class="grey-lighten-2"></view>
<view class="tm-timeline-item-content relative"> </view>
<view class="tm-timeline-item-left"> <view class="tm-timeline-item-content relative">
<view class="time text-weight-b mb-24"> <view class="tm-timeline-item-left">
{{ item.year }} <view class="flex time text-weight-b mb-24">
<block v-if="tab.activeIndex == 0">{{ item.month }}</block> <text>{{ item.year }}</text>
<text class="text-size-s text-grey-darken-1 ml-24">共发布 {{ item.posts.length }} 篇文章</text> <text v-if="tab.activeIndex == 0">{{ item.month }}</text>
</view> <view class="text-size-s text-grey-darken-1 ml-12">
<block v-if="item.posts.length != 0"> {{ item.posts.length }} 篇文章
<block v-for="(post, postIndex) in item.posts" :key="post.id"> </view>
<tm-translate animation-name="fadeUp" :wait="calcAniWait(postIndex)"> </view>
<view class="flex post shadow-3 pa-24 mb-24" :class="[globalAppSettings.layout.cardType]" <block v-if="item.posts.length != 0">
@click="fnToArticleDetail(post)"> <block v-for="(post, postIndex) in item.posts" :key="post.metadata.name">
<image class="post-thumbnail" :src="$utils.checkThumbnailUrl(post.thumbnail)" <tm-translate animation-name="fadeUp" :wait="calcAniWait(postIndex)">
mode="aspectFill"></image> <view class="flex post shadow-3 pa-24 mb-24"
<view class="post-info pl-20"> :class="[globalAppSettings.layout.cardType]"
<view class="post-info_title text-overflow">{{ post.title }}</view> @click="fnToArticleDetail(post)">
<view class="post-info_summary text-overflow-2 mt-12 text-size-s text-grey-darken-1"> <image class="post-thumbnail"
{{ post.summary }} :src="$utils.checkThumbnailUrl(post.spec.cover)" mode="aspectFill">
</view> </image>
<view class="post-info_time mt-12 text-size-s text-grey-darken-1"> <view class="post-info pl-20">
<text class="iconfont icon-clock text-size-s mr-6"></text> <view class="post-info_title text-overflow">{{ post.spec.title }}
<text class="time-label">发布时间</text> </view>
{{ {d: post.createTime, f: 'yyyy年MM月dd日 星期w'} | formatTime }} <view
</view> class="post-info_summary text-overflow-2 mt-12 text-size-s text-grey-darken-1">
</view> {{ post.status.excerpt }}
</view> </view>
</tm-translate> <view class="post-info_time mt-12 text-size-s text-grey-darken-1">
</block> <text class="iconfont icon-clock text-size-s mr-6"></text>
</block> <text class="time-label">发布时间</text>
<view v-else class="post-empty text-size-m text-grey-darken-1">该日期下暂无归档文章</view> {{ {d: post.spec.publishTime, f: 'yyyy年MM月dd日 星期w'} | formatTime }}
</view> </view>
</view> </view>
</view> </view>
</block> </tm-translate>
</view> </block>
<!-- 返回顶部 --> </block>
<tm-flotbutton @click="fnToTopPage" size="m" color="bg-gradient-light-blue-accent" icon="icon-angle-up"></tm-flotbutton> <view v-else class="post-empty text-size-m text-grey-darken-1">该日期下暂无归档文章</view>
</block> </view>
</view> </view>
</view>
</block>
</view>
<view class="load-text mt-12">{{ loadMoreText }}</view>
<!-- 返回顶部 -->
<tm-flotbutton @click="fnToTopPage" size="m" color="bg-gradient-light-blue-accent"
icon="icon-angle-up"></tm-flotbutton>
</block>
</view>
</template> </template>
<script> <script>
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue'; import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue'; import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue'; import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue'; import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue'; import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import qs from 'qs'
export default { export default {
components: { components: {
tmSkeleton, tmSkeleton,
tmTranslate, tmTranslate,
tmFlotbutton, tmFlotbutton,
tmTabs, tmTabs,
tmEmpty tmEmpty
}, },
data() { data() {
return { return {
loading: 'loading', loading: 'loading',
tab: { tab: {
activeIndex: 0, activeIndex: 0,
list: ['按月份查看', '按年份查看'] list: ['按月份查看', '按年份查看']
}, },
queryParams: { queryParams: {
size: 10, size: 10,
page: 0 page: 1
}, },
result: null, result: {},
dataList: [], cacheDataList: [], //
api: 'getMonthArchives' dataList: [], //
}; isLoadMore: false,
}, loadMoreText: "加载中..."
computed: { };
black_tmeme: function () { },
return this.$tm.vx.state().tmVuetify.black; computed: {
}, black_tmeme: function() {
color_tmeme: function () { return this.$tm.vx.state().tmVuetify.black;
return this.$tm.vx.state().tmVuetify.color; },
} color_tmeme: function() {
}, return this.$tm.vx.state().tmVuetify.color;
created() { }
this.fnGetData(); },
}, created() {
onPullDownRefresh() { this.fnGetData();
this.fnGetData(); },
this.queryParams.page = 0; onPullDownRefresh() {
}, this.isLoadMore = false;
this.queryParams.page = 1;
this.fnGetData();
},
onReachBottom(e) {
if (this.result.hasNext) {
this.queryParams.page += 1;
this.isLoadMore = true;
this.fnGetData();
} else {
uni.showToast({
icon: 'none',
title: '没有更多数据了'
});
}
},
methods: {
fnOnTabChange(index) {
this.fnResetSetAniWaitIndex();
this.queryParams.page = 0;
this.dataList = this.handleGetShowDataList(this.handleGetPosts(this.cacheDataList))
},
fnGetData() {
if (this.isLoadMore) {
uni.showLoading({
title: "加载中..."
})
} else {
this.loading = 'loading';
this.loadMoreText = "加载中...";
}
methods: { const paramsStr = qs.stringify(this.queryParams, {
fnOnTabChange(index) { allowDots: true,
this.fnResetSetAniWaitIndex(); encodeValuesOnly: true,
const _api = ['getMonthArchives', 'getYearArchives']; skipNulls: true,
this.api = _api[index]; encode: true,
this.queryParams.page = 0; arrayFormat: 'repeat'
this.fnGetData(); })
}, uni.request({
fnGetData() { url: this.$baseApiUrl + '/apis/api.content.halo.run/v1alpha1/posts?' + paramsStr,
this.loading = 'loading'; method: 'GET',
// uni.showLoading({ success: (res) => {
// mask: true, const data = res.data;
// title: '...' this.result = data;
// }); const posts = this.handleGetPosts(data.items)
this.$httpApi[this.api](this.queryParams) const showDataList = this.handleGetShowDataList(posts)
.then(res => { if (this.isLoadMore) {
this.result = res.data; this.cacheDataList = this.cacheDataList.concat(data.items);
this.dataList = res.data; this.handleMergeDataList2(showDataList)
setTimeout(() => { } else {
this.loading = 'success'; this.dataList = []
}, 500); this.cacheDataList = data.items;
}) this.dataList = showDataList
.catch(err => { }
console.error(err);
this.loading = 'error'; this.loading = 'success';
}) this.loadMoreText = data.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
.finally(() => { uni.hideLoading();
setTimeout(() => { uni.stopPullDownRefresh();
uni.hideLoading(); },
uni.stopPullDownRefresh(); fail: (err) => {
}, 500); this.loading = 'error';
}); this.loadMoreText = '加载失败,请下拉刷新!';
}, uni.$tm.toast(err.message || '数据加载失败!');
// uni.hideLoading();
fnToArticleDetail(article) { uni.stopPullDownRefresh();
uni.navigateTo({ }
url: '/pagesA/article-detail/article-detail?articleId=' + article.id, })
animationType: 'slide-in-right' },
}); //
} handleGetPosts(dataList) {
} const posts = {}
}; const postLabelYearKey = "content.halo.run/archive-year"
const postLabelMonthKey = "content.halo.run/archive-month"
dataList.forEach(item => {
let postItemKey = ""
if (this.tab.activeIndex == 0) {
postItemKey =
`${item.metadata.labels[postLabelYearKey]}-${item.metadata.labels[postLabelMonthKey]}`
} else {
postItemKey = `${item.metadata.labels[postLabelYearKey]}`
}
if (posts[postItemKey]) {
posts[postItemKey].push(item)
} else {
posts[postItemKey] = [item]
}
})
return posts;
},
//
handleGetShowDataList(posts) {
const dataListResult = []
Object.keys(posts).forEach((key) => {
const postData = {
sort: 0,
key: key,
year: key,
month: "",
posts: posts[key]
}
if (this.tab.activeIndex == 0) {
const splitDate = key.split("-")
postData.year = splitDate[0]
postData.month = splitDate[1]
postData.sort = Number(key.replace("-", ""))
} else {
postData.sort = Number(key)
}
dataListResult.push(postData)
})
if (this.tab.activeIndex == 1) {
dataListResult.sort((a, b) => {
return Number(b.year) - Number(a.year)
})
}
return dataListResult;
},
handleMergeDataList(list1, list2) {
// list1key
let merged = list1.reduce((acc, item) => {
acc[item.key] = {
...item
};
return acc;
}, {});
// list2posts
list2.forEach(item => {
if (merged[item.key]) {
// keyposts
merged[item.key].posts = [...merged[item.key].posts, ...item.posts];
} else {
// key
merged[item.key] = {
...item
};
}
});
//
return Object.values(merged);
},
handleMergeDataList2(list) {
const tempList = [...list]
this.dataList.forEach((item, index) => {
const find = this.dataList.find(x => x.key == item.key)
if (find) {
item.posts.forEach(post => {
if (!find.posts.some(x => x.metadata.name == post.metadata.name)) {
find.posts.push(post)
}
})
tempList.splice(index, 1)
}
})
tempList.forEach(post => {
this.dataList.push(post)
})
console.log("this.dataList", this.dataList)
},
fnToArticleDetail(article) {
uni.navigateTo({
url: '/pagesA/article-detail/article-detail?name=' + article.metadata.name,
animationType: 'slide-in-right'
});
}
}
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.app-page { .app-page {
width: 100vw; width: 100vw;
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: #fafafd; background-color: #fafafd;
} }
.loading-wrap { .loading-wrap {
padding: 24rpx; padding: 24rpx;
} }
.list-empty { .list-empty {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
} }
.statistics { .statistics {
background-color: #ffffff; background-color: #ffffff;
} }
.e-timeline { .e-timeline {
::v-deep { ::v-deep {
.tm-timeline-item > view:first-child { .tm-timeline-item>view:first-child {
width: 110rpx !important; width: 110rpx !important;
} }
.tm-timeline-item-left { .tm-timeline-item-left {
max-width: 580rpx !important; max-width: 580rpx !important;
width: 100% !important; width: 100% !important;
} }
} }
} }
.tm-timeline { .tm-timeline {
.tm-timeline-item { .tm-timeline-item {
.tm-timeline-item-left, .tm-timeline-item-left,
.tm-timeline-item-right { .tm-timeline-item-right {
width: 200rpx; width: 200rpx;
flex-shrink: 0; flex-shrink: 0;
} }
.tm-timeline-item-content { .tm-timeline-item-content {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
align-content: flex-start; align-content: flex-start;
} }
.tm-timeline-jidian { .tm-timeline-jidian {
margin: auto; margin: auto;
} }
&.tm-timeline-item--leftDir { &.tm-timeline-item--leftDir {
display: flex; display: flex;
flex-flow: row; flex-flow: row;
&.endright { &.endright {
justify-content: flex-end; justify-content: flex-end;
} }
.tm-timeline-item-left, .tm-timeline-item-left,
.tm-timeline-item-right { .tm-timeline-item-right {
width: auto; width: auto;
max-width: 400rpx; max-width: 400rpx;
} }
.tm-timeline-item-boder { .tm-timeline-item-boder {
height: 100%; height: 100%;
width: 1px; width: 1px;
margin: auto; margin: auto;
} }
.tm-timeline-jidian { .tm-timeline-jidian {
position: relative; position: relative;
margin: auto; margin: auto;
z-index: 2; z-index: 2;
} }
.tm-timeline-item-content { .tm-timeline-item-content {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
align-items: flex-start; align-items: flex-start;
align-content: flex-start; align-content: flex-start;
} }
} }
} }
} }
.post { .post {
width: 560rpx; width: 560rpx;
border-radius: 12rpx; border-radius: 12rpx;
background-color: #fff; background-color: #fff;
&.lr_image_text { &.lr_image_text {}
}
&.lr_text_image { &.lr_text_image {
.post-thumbnail { .post-thumbnail {
order: 2; order: 2;
} }
.post-info { .post-info {
order: 1; order: 1;
padding-left: 0; padding-left: 0;
padding-right: 24rpx; padding-right: 24rpx;
} }
} }
&.tb_image_text { &.tb_image_text {
flex-direction: column; flex-direction: column;
.post-thumbnail { .post-thumbnail {
width: 100%; width: 100%;
height: 220rpx; height: 220rpx;
} }
.post-info { .post-info {
width: 100%; width: 100%;
padding-left: 0; padding-left: 0;
&_title { &_title {
margin-top: 12rpx; margin-top: 12rpx;
} }
&_time { &_time {
.iconfont { .iconfont {
display: none; display: none;
} }
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
} }
} }
&.tb_text_image { &.tb_text_image {
flex-direction: column; flex-direction: column;
.post-thumbnail { .post-thumbnail {
order: 2; order: 2;
width: 100%; width: 100%;
height: 220rpx; height: 220rpx;
margin-top: 12rpx; margin-top: 12rpx;
} }
.post-info { .post-info {
order: 1; order: 1;
width: 100%; width: 100%;
padding-left: 0; padding-left: 0;
&_time { &_time {
.iconfont { .iconfont {
display: none; display: none;
} }
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
} }
} }
&.only_text { &.only_text {
.post-info { .post-info {
padding: 6rpx; padding: 6rpx;
&_time { &_time {
margin-top: 20rpx; margin-top: 20rpx;
.iconfont { .iconfont {
display: none; display: none;
} }
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
} }
.post-thumbnail { .post-thumbnail {
display: none; display: none;
} }
} }
} }
.post-thumbnail { .post-thumbnail {
border-radius: 6rpx; border-radius: 6rpx;
width: 200rpx; width: 200rpx;
height: 170rpx; height: 170rpx;
} }
.post-info { .post-info {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
&_title { &_title {
color: #303133; color: #303133;
font-size: 28rpx; font-size: 28rpx;
font-weight: bold; font-weight: bold;
} }
&_summary { &_summary {
display: -webkit-box; display: -webkit-box;
line-height: 1.6; line-height: 1.6;
} }
&_time { &_time {
.time-label { .time-label {
display: none; display: none;
} }
} }
} }
.time {
align-items: center;
}
</style> </style>