1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-09-12 16:40:40 +08:00
Files
uni-halo/src/pages/tabbar/home/home.vue
T
小莫唐尼 eb19276a2d refactor: 清理配置文件注释并优化组件样式与逻辑
1. 移除appSettings.ts和appConfig.ts中的旧注释文档
2. 优化首页分类、文章页的箭头按钮样式与尺寸
3. 隐藏home页的多余"更多"按钮并统一右侧按钮样式
4. 重构快捷导航组件的逻辑,简化默认列表渲染
5. 优化文章卡片的布局样式、结构和代码组织
2026-09-10 00:44:27 +08:00

260 lines
8.3 KiB
Vue

<script lang="ts" setup>
import { computed, ref } from 'vue'
import { onPullDownRefresh, onReachBottom, onShow } from '@dcloudio/uni-app'
import { getPostList } from '@/api/halo'
import { useAppConfigStore } from '@/store/appConfig'
import { useSettingStore } from '@/store/setting'
import { checkAvatarUrl, checkImageUrl } from '@/utils/url'
import { t } from '@/locale'
import { useMaintenanceIntercept } from '@/hooks/useMaintenanceIntercept'
import { DataLoadingStatusEnum, useDataLoadingStatus } from '@/hooks/useDataLoadingStatus'
import type { IPost } from '@/api/types/halo'
definePage({
style: {
navigationBarTitleText: '首页',
enablePullDownRefresh: true,
navigationStyle: 'custom',
},
})
const appConfigStore = useAppConfigStore()
const settingStore = useSettingStore()
/** 维护拦截(插件可用性 + 维护模式,任一命中跳维护页;与入口页共用 hooks) */
const { interceptOrContinue } = useMaintenanceIntercept()
/** 是否已被拦截(配置已带维护键时同步置位,避免首载闪跳) */
const intercepted = ref(!!appConfigStore.configs.maintenance)
const haloConfigs = computed(() => appConfigStore.configs)
/* ---------------- 状态 ---------------- */
const { loadingStatus, updateLoadingStatus } = useDataLoadingStatus()
const isLoadMore = ref(false)
const loadMoreText = ref(t('common.loading'))
const articleList = ref<IPost[]>([])
const result = ref<{ hasNext : boolean }>({ hasNext: false })
const queryParams = ref({
size: 10,
page: 1,
sort: ['spec.pinned,desc', 'spec.publishTime,desc'],
})
/* ---------------- 最新推荐模式(默认/置顶/最新/最旧) ---------------- */
const recommendTabs = [
{ label: '默认', value: 'default' },
{ label: '置顶', value: 'pinned' },
{ label: '最新', value: 'latest' },
{ label: '最旧', value: 'oldest' },
]
const recommendMode = ref<'default' | 'pinned' | 'latest' | 'oldest'>('default')
/** 各模式对应排序参数(默认 = 置顶优先 + 发布时间倒序) */
const recommendSortMap : Record<string, string[]> = {
default: ['spec.pinned,desc', 'spec.publishTime,desc'],
pinned: ['spec.pinned,desc'],
latest: ['spec.publishTime,desc'],
oldest: ['spec.publishTime,asc'],
}
/** 切换推荐模式:重置分页并重新查询 */
function handleRecommendModeChange(mode : 'default' | 'pinned' | 'latest' | 'oldest') {
if (recommendMode.value === mode)
return
recommendMode.value = mode
isLoadMore.value = false
articleList.value = []
queryParams.value.page = 1
queryParams.value.sort = recommendSortMap[mode]
handleGetArticleList()
}
/* ---------------- 计算属性 ---------------- */
const appInfo = computed(() => {
const appInfoData = haloConfigs.value.appConfig?.appInfo as { name ?: string, logo ?: string } | undefined
return {
name: appInfoData?.name || 'uni-halo',
logo: checkImageUrl(appInfoData?.logo),
}
})
const bloggerInfo = computed(() => {
const blogger = haloConfigs.value.authorConfig?.blogger as { nickname ?: string, avatar ?: string } | undefined
return {
nickname: blogger?.nickname || '',
avatar: checkAvatarUrl(blogger?.avatar),
}
})
const calcAuditModeEnabled = computed(() => appConfigStore.auditModeEnabled)
const globalAppSettings = computed(() => settingStore.settings)
/** 首页列表布局(偏好设置驱动:single=单列 / double=双列) */
const homeListLayout = computed(() => settingStore.settings.homeListLayout)
/* ---------------- 数据加载 ---------------- */
async function handleQuery() {
handleGetArticleList()
}
/** 文章列表 */
async function handleGetArticleList() {
if (calcAuditModeEnabled.value) {
// 审核模式:真实文章按 audit-data posts 过滤(数组顺序即展示顺序)
const auditPostNames = appConfigStore.auditData.spec?.posts || []
try {
const res = await getPostList({ page: 1, size: 0, sort: ['spec.publishTime,desc'] })
const filtered = res.data.items.filter(item => auditPostNames.includes(item.metadata.name))
articleList.value = filtered.map((item) => {
item.owner.avatar = checkAvatarUrl(item.owner.avatar)
return item
})
updateLoadingStatus(articleList.value.length === 0 ? DataLoadingStatusEnum.Empty : DataLoadingStatusEnum.Success)
loadMoreText.value = t('common.noMore')
}
catch (err) {
console.error('获取审核文章失败', err)
updateLoadingStatus(DataLoadingStatusEnum.Error)
loadMoreText.value = t('common.loadFailed')
}
finally {
uni.hideLoading()
uni.stopPullDownRefresh()
}
return
}
if (!isLoadMore.value) {
updateLoadingStatus(DataLoadingStatusEnum.Loading)
}
loadMoreText.value = t('common.loading')
try {
const res = await getPostList({ ...toRaw(queryParams.value) })
result.value.hasNext = res.data.hasNext
articleList.value = (isLoadMore.value
? articleList.value.concat(res.data.items)
: res.data.items).map((item) => {
item.owner.avatar = checkAvatarUrl(item.owner.avatar)
return item
})
updateLoadingStatus(articleList.value.length === 0 ? DataLoadingStatusEnum.Empty : DataLoadingStatusEnum.Success)
loadMoreText.value = res.data.hasNext ? t('common.loadMore') : t('common.noMore')
}
catch (err) {
updateLoadingStatus(DataLoadingStatusEnum.Error)
loadMoreText.value = t('common.loadFailed')
console.error('获取文章失败', err)
}
finally {
uni.hideLoading()
uni.stopPullDownRefresh()
}
}
/* ---------------- 跳转 ---------------- */
function handleToArticles() {
uni.navigateTo({ url: '/pages-blog/articles/articles' })
}
function handleOnLogoToPage() {
uni.switchTab({ url: '/pages/tabbar/about/about' })
}
function init() {
if (!intercepted.value) {
handleQuery()
}
}
init()
/* ---------------- 生命周期 ---------------- */
// 维护检查
onShow(async () => {
intercepted.value = await interceptOrContinue()
console.log('拦截状态', intercepted.value)
})
onPullDownRefresh(() => {
isLoadMore.value = false
queryParams.value.page = 1
handleQuery()
})
onReachBottom(() => {
if (calcAuditModeEnabled.value) {
uni.showToast({ icon: 'none', title: t('common.noMoreData') })
return
}
if (result.value.hasNext) {
queryParams.value.page += 1
isLoadMore.value = true
handleGetArticleList()
}
else {
uni.showToast({ icon: 'none', title: t('common.noMoreData') })
}
})
</script>
<template>
<view class="min-h-screen w-screen flex flex-col bg-page">
<!-- 轮播 -->
<uh-home-banner />
<!-- 公告 -->
<uh-home-notify />
<!-- 快捷导航 -->
<uh-home-quick-nav />
<!-- 精选分类 -->
<uh-home-category />
<!-- 最新文章 -->
<uh-section-title class="mb-4 box-border px-3">
最新推荐
<template #right>
<view class="flex items-center gap-2">
<!-- 推荐模式分段器:默认 / 置顶 / 最新 -->
<view class="uh-global-card-glass uh-shadow-xs flex items-center border rounded-lg p-0.5">
<view v-for="tab in recommendTabs" :key="tab.value" class="rounded-md px-2 py-0.5 text-xs"
:class="recommendMode === tab.value ? 'bg-secondary text-gray-900' : 'text-gray-500'"
@click="handleRecommendModeChange(tab.value as 'default' | 'pinned' | 'latest' | 'oldest')">
{{ tab.label }}
</view>
<view v-if="false" class="rounded-md px-2 py-0.5 text-xs text-gray-500" @click="handleToArticles()">
更多
</view>
</view>
<view
class="uh-global-card-glass uh-shadow-xs border flex items-center justify-center rounded-md p-1 text-gray-400"
@click="handleToArticles()">
<wd-icon name="arrow-right" size="28rpx" />
</view>
</view>
</template>
</uh-section-title>
<uh-data-loading v-if="loadingStatus !== DataLoadingStatusEnum.Success" :loading-status="loadingStatus"
min-height="36vh" @refresh="handleQuery" />
<block v-else>
<view class="box-border p-3 pt-0"
:class="homeListLayout === 'double' ? 'grid grid-cols-2 gap-3' : 'flex flex-col gap-y-3'">
<uh-article-card v-for="(article, index) in articleList" :key="index" from="home" :article="article"
:variant="homeListLayout === 'double' ? 'grid' : 'list'" :audit-mode="calcAuditModeEnabled" />
</view>
<view class="mt-3 box-border pb-5 text-center text-xs text-gray-400">
{{ loadMoreText }}
</view>
</block>
</view>
<uh-notify-dialog />
</template>