1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-09-13 00:50:40 +08:00

feat: 重构评论交互与请求错误处理,新增搜索页

- 请求层:非 200 响应统一抛 UniHaloError,Halo 来源响应归一化
- 评论弹窗:新增 allowNotification、验证码错误自动刷新与错误详情提示
- 评论回复结构调整为分页结果(replies.items)
- 首页:快捷导航改 5 列、最新列表跳转搜索页,移除旧文章列表页
- 图库:分类切换改用 wd-tabs/wd-tab 组件
- 新增搜索页、uh-data-loading 组件、useDataLoadingStatus 与 exception 工具

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>
This commit is contained in:
小莫唐尼
2026-09-02 00:51:43 +08:00
parent 067f3ed98a
commit 254d0b924b
19 changed files with 737 additions and 623 deletions
+1 -1
View File
@@ -28,7 +28,7 @@ const articleDetailPath = '/pages-blog/article-detail/article-detail'
// 本地开发快速跳转页面,发布请置为 false
const DEV_MODE = false
const DEV_TO_TYPE = 'page' as 'page' | 'tabbar'
const DEV_TO_PATH = '/pages-blog/data-visual/data-visual'
const DEV_TO_PATH = articleDetailPath + '?name=01a057b2-3200-74af-8afe-28a054092e82'
/* ---------------- 状态 ---------------- */
const appConfigStore = useAppConfigStore()
+13 -11
View File
@@ -51,7 +51,8 @@ async function handleGetCategory() {
}
try {
const res = await getPhotoGroupList({ page: 1, size: 0 })
category.value.list = (res.data.items || [])
console.log('分类数据',res.data)
category.value.list = (res.data || [])
.map(item => ({
name: item.metadata.name,
displayName: item.spec.displayName,
@@ -134,10 +135,11 @@ function handleGetDataByCategory(index: number) {
handleGetData(true)
}
function handleOnCategoryChange(e: { detail: { current: number } }) {
function handleOnCategoryChange(e:{index:number,name:number}) {
console.log('切换分类', e)
if (lock.value)
return
handleGetDataByCategory(e.detail.current)
handleGetDataByCategory(e.index)
}
/* ---------------- 图片预览 ---------------- */
@@ -207,17 +209,17 @@ onReachBottom(() => {
/>
<template v-else>
<!-- 顶部切换 -->
<view v-if="category.list.length > 0" class="category-tabs fixed inset-x-0 top-0 z-6 bg-white">
<wd-tabs
<wd-tabs
v-if="category.list.length > 0"
v-model="category.activeIndex"
:tabs="category.list.map(item => ({ title: item.displayName }))"
align="left"
sticky
:offset-top="0"
@change="handleOnCategoryChange"
/>
</view>
<!-- 占位区域 -->
<view v-if="category.list.length > 0" class="h-[90rpx] w-screen" />
>
<wd-tab v-for="cate in category.list" :key="cate.displayName" :title="cate.displayName"></wd-tab>
</wd-tabs>
<!-- 骨架屏 -->
<view v-if="loading === 'loading'" class="loading-wrap box-border p-3">
<wd-skeleton :row="4" :animated="true" />
+11 -71
View File
@@ -18,6 +18,7 @@ definePage({
navigationBarTitleText: '首页',
enablePullDownRefresh: true,
navigationStyle: 'custom',
backgroundColor:'#F8F8F8'
},
})
@@ -66,7 +67,7 @@ const bloggerInfo = computed(() => {
const calcAuditModeEnabled = computed(() => !!haloConfigs.value.auditConfig?.auditModeEnabled)
const calcIsShowQuickNavigationEnabled = computed(() => !!haloConfigs.value.pageConfig?.homeConfig?.useQuickNavigation)
const calcIsShowQuickNavigationEnabled = computed(() => haloConfigs.value.pageConfig?.homeConfig?.useQuickNavigation)
const calcIsShowCategory = computed(() => {
if (calcAuditModeEnabled.value)
@@ -241,7 +242,7 @@ async function handleGetArticleList() {
loadMoreText.value = t('common.loading')
try {
const res = await getPostList({ ...queryParams.value })
const res = await getPostList({ ...toRaw(queryParams.value) })
result.value.hasNext = res.data.hasNext
articleList.value = isLoadMore.value
? articleList.value.concat(res.data.items)
@@ -277,11 +278,7 @@ function handleToArticleDetail(article: IPost) {
function handleToCategoryPage() {
uni.switchTab({ url: '/pages/tabbar/category/category' })
}
function handleToArticlesPage() {
uni.navigateTo({ url: '/pages-blog/articles/articles' })
}
function handleToCategoryBy(category: ICategory) {
if (calcAuditModeEnabled.value)
return
@@ -291,7 +288,7 @@ function handleToCategoryBy(category: ICategory) {
}
function handleToSearch() {
uni.navigateTo({ url: '/pages-blog/articles/articles' })
uni.navigateTo({ url: '/pages-blog/search/search' })
}
function handleOnLogoToPage() {
@@ -374,7 +371,7 @@ handleQuery()
</script>
<template>
<view class="app-page min-h-screen w-screen flex flex-col">
<view class="min-h-screen w-screen flex flex-col">
<!-- 顶部栏 -->
<view class="header flex items-center gap-4 px-3 py-1.5">
<image class="logo h-[60rpx] w-[60rpx] rounded-3xl" :src="appInfo.logo" mode="scaleToFill" @click="handleOnLogoToPage" />
@@ -398,7 +395,7 @@ handleQuery()
<block v-else>
<!-- 轮播 Banner -->
<view v-if="bannerConfig?.enabled" class="bg-white pb-6">
<view v-if="bannerConfig?.enabled" class="bg-white mb-4">
<view v-if="bannerList.length !== 0" class="banner mx-3 mt-3 overflow-hidden rounded-xl">
<uh-swiper
:height="bannerConfig.height"
@@ -414,11 +411,11 @@ handleQuery()
</view>
<!-- 快捷导航 -->
<view v-if="calcIsShowQuickNavigationEnabled && navList.filter(x => x.show).length" class="nav-box mx-6 mb-6 mt-4 overflow-hidden rounded-xl bg-white p-3">
<view v-if="navList.filter(x => x.show).length" class="nav-box px-4 overflow-hidden rounded-xl bg-white p-3">
<view class="page-item-title font-bold">
快捷导航
</view>
<view class="nav-list grid grid-cols-4 mt-6 gap-6">
<view class="nav-list grid grid-cols-5 mt-6 gap-6">
<template v-for="item in navList.filter(x => x.show)" :key="item.key">
<view class="nav-item flex flex-col items-center gap-3" @click="handleClickNav(item)">
<view class="nav-item-icon h-[88rpx] w-[88rpx] flex items-center justify-center rounded-3xl" :style="{ backgroundColor: item.bgColor }">
@@ -463,7 +460,7 @@ handleQuery()
<view class="page-item-title font-bold">
最新列表
</view>
<view class="show-more flex items-center justify-center rounded-xl bg-white" @click="handleToArticlesPage">
<view class="show-more flex items-center justify-center rounded-xl bg-white" @click="handleToSearch">
<wd-icon name="arrow-right" size="12px" color="#909399" />
</view>
</view>
@@ -500,61 +497,4 @@ handleQuery()
/>
</view>
</template>
<style scoped lang="scss">
.app-page {
display: flex;
flex-direction: column;
}
.header {
.logo {
flex-shrink: 0;
}
.search-input {
.search-text {
margin-left: 12rpx;
}
}
}
.page-item-title {
position: relative;
padding-left: 24rpx;
font-size: 32rpx;
color: #303133;
&::before {
content: '';
position: absolute;
left: 0;
top: 8rpx;
width: 8rpx;
height: 30rpx;
background-color: rgb(33 150 243);
border-radius: 6rpx;
}
}
.show-more {
width: 42rpx;
height: 42rpx;
box-shadow: 0 0 24rpx rgb(0 0 0 / 3%);
}
.to-top-btn {
position: fixed;
right: 24rpx;
bottom: 120rpx;
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 50%;
box-shadow: 0 4rpx 16rpx rgb(0 0 0 / 10%);
z-index: 6;
}
</style>
+3 -21
View File
@@ -5,7 +5,6 @@
*/
import { computed, ref } from 'vue'
import { onLoad, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import dayjs from 'dayjs'
import { getMomentList } from '@/api/halo'
import { useAppConfigStore } from '@/store/appConfig'
import { checkAvatarUrl, checkThumbnailUrl } from '@/utils/url'
@@ -95,7 +94,6 @@ async function handleGetData() {
try {
const res = await getMomentList({ ...queryParams.value })
console.log('获取瞬间数据成功', res)
loading.value = 'success'
loadMoreText.value = res.data.hasNext ? t('common.loadMore') : t('common.noMore')
hasNext.value = res.data.hasNext
@@ -250,7 +248,7 @@ onReachBottom(() => {
</script>
<template>
<view class="app-page box-border min-h-screen w-screen flex flex-col py-6">
<view class=" box-border min-h-screen w-screen flex flex-col py-6">
<uh-plugin-unavailable
v-if="!uniHaloPluginAvailable"
:plugin-id="uniHaloPluginId"
@@ -262,14 +260,14 @@ onReachBottom(() => {
<wd-skeleton :row="3" :animated="true" />
</view>
<view v-else class="app-page-content">
<view v-else class="flex flex-col gap-y-2 p-4">
<view v-if="dataList.length === 0" class="min-h-[70vh] w-full flex items-center justify-center content-empty">
<wd-empty :description="t('common.empty')" />
</view>
<block v-else>
<!-- 瞬间卡片 -->
<view v-for="moment in dataList" :key="moment.metadata.name" class="moment-card mx-6 mb-6 flex flex-col overflow-hidden rounded-xl bg-white shadow-sm">
<view v-for="moment in dataList" :key="moment.metadata.name" class="flex flex-col overflow-hidden rounded-xl bg-white shadow-sm">
<view class="head flex items-center p-3 pb-0">
<image class="avatar h-[66rpx] w-[66rpx] shrink-0 rounded-full" :src="moment.spec.owner?.avatar || bloggerInfo.avatar" mode="aspectFill" />
<view class="nickname ml-3">
@@ -361,19 +359,3 @@ onReachBottom(() => {
</template>
</view>
</template>
<style scoped lang="scss">
.app-page {
/* 布局全部由 UnoCSS 原子类实现 */
}
.moment-card {
.head {
.nickname {
.nickname-text {
/* 无额外样式 */
}
}
}
}
</style>