mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
refactor: 优化代码结构与功能,修复细节问题
1. 新增首页精选分类配置类型定义与逻辑,支持自定义首页分类展示顺序 2. 移除冗余的qs导入与注释代码,简化请求参数处理 3. 修复瞬间详情页悬浮操作的渲染问题与收藏按钮样式 4. 优化文章卡片的头像处理与分类跳转逻辑 5. 重构数据加载组件,新增尺寸配置与样式优化 6. 为分类文章页添加排序切换功能,支持默认/置顶/最新/最旧排序 7. 优化首页分类模块的加载状态与数据获取逻辑,添加延迟模拟 8. 统一API文件的导入格式与代码风格
This commit is contained in:
+74
-80
@@ -1,11 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Halo 官方 API 接口定义
|
* Halo 官方 API 接口定义
|
||||||
*/
|
*/
|
||||||
import { http } from '@/http/alova'
|
import { http } from '@/http/alova';
|
||||||
import { RequestFrom } from '@/http/tools/enum'
|
import { RequestFrom } from '@/http/tools/enum';
|
||||||
import type { IResponse } from '@/http/types'
|
import type { IResponse } from '@/http/types';
|
||||||
import { getCache } from '@/utils/storage'
|
import { getCache } from '@/utils/storage';
|
||||||
import { getNologinEmail, getOpenid } from '@/utils/auth'
|
import { getNologinEmail, getOpenid } from '@/utils/auth';
|
||||||
import type {
|
import type {
|
||||||
IBlogStats,
|
IBlogStats,
|
||||||
ICategory,
|
ICategory,
|
||||||
@@ -31,11 +31,11 @@ import type {
|
|||||||
ISearchRes,
|
ISearchRes,
|
||||||
ITagListRes,
|
ITagListRes,
|
||||||
ITrackerCounterReq,
|
ITrackerCounterReq,
|
||||||
IUpvoteReq,
|
IUpvoteReq
|
||||||
} from './types/halo'
|
} from './types/halo';
|
||||||
|
|
||||||
/** 评论验证码 cookie key */
|
/** 评论验证码 cookie key */
|
||||||
const COMMENT_WIDGET_CAPTCHA_COOKIES = 'comment-widget-captcha'
|
const COMMENT_WIDGET_CAPTCHA_COOKIES = 'comment-widget-captcha';
|
||||||
|
|
||||||
/* ==================== 文章 ==================== */
|
/* ==================== 文章 ==================== */
|
||||||
|
|
||||||
@@ -45,8 +45,8 @@ const COMMENT_WIDGET_CAPTCHA_COOKIES = 'comment-widget-captcha'
|
|||||||
export function getPostList(params: IPostListReq) {
|
export function getPostList(params: IPostListReq) {
|
||||||
return http.Get<IResponse<IPostListRes>>('/apis/api.content.halo.run/v1alpha1/posts', {
|
return http.Get<IResponse<IPostListRes>>('/apis/api.content.halo.run/v1alpha1/posts', {
|
||||||
query: params,
|
query: params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,10 +56,10 @@ export function getPostByName(name: string) {
|
|||||||
return http.Get<IResponse<IPost>>(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {
|
return http.Get<IResponse<IPost>>(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Wechat-Session-Id': getOpenid(),
|
'Wechat-Session-Id': getOpenid(),
|
||||||
'nologin-email': getNologinEmail(),
|
'nologin-email': getNologinEmail()
|
||||||
},
|
},
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -67,22 +67,20 @@ export function getPostByName(name: string) {
|
|||||||
*/
|
*/
|
||||||
export function getPostListByKeyword(params: ISearchReq) {
|
export function getPostListByKeyword(params: ISearchReq) {
|
||||||
return http.Post<IResponse<ISearchRes>>('/apis/api.halo.run/v1alpha1/indices/-/search', params, {
|
return http.Post<IResponse<ISearchRes>>('/apis/api.halo.run/v1alpha1/indices/-/search', params, {
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 分类 / 标签 ==================== */
|
/* ==================== 分类 / 标签 ==================== */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分类列表
|
* 分类列表
|
||||||
* 注:旧代码用 qs.stringify 特殊序列化(arrayFormat:'repeat'、allowDots),
|
|
||||||
* alova 的 params 对数组默认即 repeat 形式(a=1&a=2),已等价;如遇嵌套对象场景再单独处理
|
|
||||||
*/
|
*/
|
||||||
export function getCategoryList(params: ICategoryListReq) {
|
export function getCategoryList(params: ICategoryListReq) {
|
||||||
return http.Get<IResponse<ICategoryListRes>>('/apis/api.content.halo.run/v1alpha1/categories', {
|
return http.Get<IResponse<ICategoryListRes>>('/apis/api.content.halo.run/v1alpha1/categories', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,8 +89,8 @@ export function getCategoryList(params: ICategoryListReq) {
|
|||||||
export function getCategoryPostList(name: string, params: IPostListReq) {
|
export function getCategoryPostList(name: string, params: IPostListReq) {
|
||||||
return http.Get<IResponse<IPostListRes>>(`/apis/api.content.halo.run/v1alpha1/categories/${name}/posts`, {
|
return http.Get<IResponse<IPostListRes>>(`/apis/api.content.halo.run/v1alpha1/categories/${name}/posts`, {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,8 +99,8 @@ export function getCategoryPostList(name: string, params: IPostListReq) {
|
|||||||
export function getTagList(params: ICategoryListReq) {
|
export function getTagList(params: ICategoryListReq) {
|
||||||
return http.Get<IResponse<ITagListRes>>('/apis/api.content.halo.run/v1alpha1/tags', {
|
return http.Get<IResponse<ITagListRes>>('/apis/api.content.halo.run/v1alpha1/tags', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,8 +109,8 @@ export function getTagList(params: ICategoryListReq) {
|
|||||||
export function getPostByTagName(tagName: string, params: IPostListReq) {
|
export function getPostByTagName(tagName: string, params: IPostListReq) {
|
||||||
return http.Get<IResponse<IPostListRes>>(`/apis/api.content.halo.run/v1alpha1/tags/${tagName}/posts`, {
|
return http.Get<IResponse<IPostListRes>>(`/apis/api.content.halo.run/v1alpha1/tags/${tagName}/posts`, {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 评论(含验证码 cookie 链路) ==================== */
|
/* ==================== 评论(含验证码 cookie 链路) ==================== */
|
||||||
@@ -124,8 +122,8 @@ export function getPostCommentList(params: ICommentListReq) {
|
|||||||
return http.Get<IResponse<ICommentListRes>>('/apis/api.halo.run/v1alpha1/comments', {
|
return http.Get<IResponse<ICommentListRes>>('/apis/api.halo.run/v1alpha1/comments', {
|
||||||
params,
|
params,
|
||||||
cacheFor: 0,
|
cacheFor: 0,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,63 +133,59 @@ export function getPostCommentReplyList(commentName: string, params: ICommentLis
|
|||||||
return http.Get<IResponse<ICommentListRes>>(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, {
|
return http.Get<IResponse<ICommentListRes>>(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, {
|
||||||
params,
|
params,
|
||||||
cacheFor: 0,
|
cacheFor: 0,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 新增评论(带验证码,captchaCode 转入请求头) */
|
/** 新增评论(带验证码,captchaCode 转入请求头) */
|
||||||
export interface IAddCommentReq {
|
export interface IAddCommentReq {
|
||||||
allowNotification: boolean
|
allowNotification: boolean;
|
||||||
raw: string
|
raw: string;
|
||||||
content?: string
|
content?: string;
|
||||||
owner?: Record<string, unknown>
|
owner?: Record<string, unknown>;
|
||||||
/** 评论目标引用(subjectRef: group/kind/name/version) */
|
/** 评论目标引用(subjectRef: group/kind/name/version) */
|
||||||
subjectRef?: {
|
subjectRef?: {
|
||||||
group: string
|
group: string;
|
||||||
kind: string
|
kind: string;
|
||||||
name: string
|
name: string;
|
||||||
version?: string
|
version?: string;
|
||||||
}
|
};
|
||||||
/** 验证码,提交时转入 X-Captcha-Code 头 */
|
/** 验证码,提交时转入 X-Captcha-Code 头 */
|
||||||
captchaCode?: string
|
captchaCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增评论(captchaCode 拆出转 X-Captcha-Code 头 + Cookie)
|
* 新增评论(captchaCode 拆出转 X-Captcha-Code 头 + Cookie)
|
||||||
*/
|
*/
|
||||||
export function addPostComment(data: IAddCommentReq) {
|
export function addPostComment(data: IAddCommentReq) {
|
||||||
const { captchaCode, ...rest } = data
|
const { captchaCode, ...rest } = data;
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json'
|
||||||
}
|
};
|
||||||
if (captchaCode)
|
if (captchaCode) headers['X-Captcha-Code'] = captchaCode;
|
||||||
headers['X-Captcha-Code'] = captchaCode
|
const cookie = getCache<string>(COMMENT_WIDGET_CAPTCHA_COOKIES);
|
||||||
const cookie = getCache<string>(COMMENT_WIDGET_CAPTCHA_COOKIES)
|
if (cookie) headers.Cookie = cookie;
|
||||||
if (cookie)
|
|
||||||
headers.Cookie = cookie
|
|
||||||
return http.Post<IResponse<IComment>>('/apis/api.halo.run/v1alpha1/comments', rest, {
|
return http.Post<IResponse<IComment>>('/apis/api.halo.run/v1alpha1/comments', rest, {
|
||||||
headers,
|
headers,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增评论回复(同上,验证码逻辑)
|
* 新增评论回复(同上,验证码逻辑)
|
||||||
*/
|
*/
|
||||||
export function addPostCommentReply(commentName: string, data: IAddCommentReq) {
|
export function addPostCommentReply(commentName: string, data: IAddCommentReq) {
|
||||||
const { captchaCode, ...rest } = data
|
const { captchaCode, ...rest } = data;
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json'
|
||||||
}
|
};
|
||||||
if (captchaCode)
|
if (captchaCode) headers['X-Captcha-Code'] = captchaCode;
|
||||||
headers['X-Captcha-Code'] = captchaCode
|
const cookie = getCache<string>(COMMENT_WIDGET_CAPTCHA_COOKIES);
|
||||||
const cookie = getCache<string>(COMMENT_WIDGET_CAPTCHA_COOKIES)
|
if (cookie) headers.Cookie = cookie;
|
||||||
if (cookie)
|
|
||||||
headers.Cookie = cookie
|
|
||||||
return http.Post<IResponse<IComment>>(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, rest, {
|
return http.Post<IResponse<IComment>>(`/apis/api.halo.run/v1alpha1/comments/${commentName}/reply`, rest, {
|
||||||
headers,
|
headers,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 瞬间 ==================== */
|
/* ==================== 瞬间 ==================== */
|
||||||
@@ -202,8 +196,8 @@ export function addPostCommentReply(commentName: string, data: IAddCommentReq) {
|
|||||||
export function getMomentList(params: IMomentListReq) {
|
export function getMomentList(params: IMomentListReq) {
|
||||||
return http.Get<IResponse<IMomentListRes>>('/apis/api.moment.halo.run/v1alpha1/moments', {
|
return http.Get<IResponse<IMomentListRes>>('/apis/api.moment.halo.run/v1alpha1/moments', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,8 +205,8 @@ export function getMomentList(params: IMomentListReq) {
|
|||||||
*/
|
*/
|
||||||
export function getMomentByName(name: string) {
|
export function getMomentByName(name: string) {
|
||||||
return http.Get<IResponse<IMoment>>(`/apis/api.moment.halo.run/v1alpha1/moments/${name}`, {
|
return http.Get<IResponse<IMoment>>(`/apis/api.moment.halo.run/v1alpha1/moments/${name}`, {
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 图库 ==================== */
|
/* ==================== 图库 ==================== */
|
||||||
@@ -223,8 +217,8 @@ export function getMomentByName(name: string) {
|
|||||||
export function getPhotoGroupList(params: IPhotoGroupListReq) {
|
export function getPhotoGroupList(params: IPhotoGroupListReq) {
|
||||||
return http.Get<IResponse<IPhotoGroupListRes>>('/apis/api.photo.halo.run/v1alpha1/photogroups', {
|
return http.Get<IResponse<IPhotoGroupListRes>>('/apis/api.photo.halo.run/v1alpha1/photogroups', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -233,8 +227,8 @@ export function getPhotoGroupList(params: IPhotoGroupListReq) {
|
|||||||
export function getPhotoListByGroupName(params: IPhotoListReq) {
|
export function getPhotoListByGroupName(params: IPhotoListReq) {
|
||||||
return http.Get<IResponse<IPhotoListRes>>('/apis/api.photo.halo.run/v1alpha1/photos', {
|
return http.Get<IResponse<IPhotoListRes>>('/apis/api.photo.halo.run/v1alpha1/photos', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 友链 ==================== */
|
/* ==================== 友链 ==================== */
|
||||||
@@ -245,8 +239,8 @@ export function getPhotoListByGroupName(params: IPhotoListReq) {
|
|||||||
export function getFriendLinkGroupList(params: ICategoryListReq) {
|
export function getFriendLinkGroupList(params: ICategoryListReq) {
|
||||||
return http.Get<IResponse<Array<ILinkGroup>>>('/apis/api.link.halo.run/v1alpha1/linkgroups', {
|
return http.Get<IResponse<Array<ILinkGroup>>>('/apis/api.link.halo.run/v1alpha1/linkgroups', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,8 +249,8 @@ export function getFriendLinkGroupList(params: ICategoryListReq) {
|
|||||||
export function getFriendLinkList(params: ICategoryListReq) {
|
export function getFriendLinkList(params: ICategoryListReq) {
|
||||||
return http.Get<IResponse<ILinkListRes>>('/apis/api.link.halo.run/v1alpha1/links', {
|
return http.Get<IResponse<ILinkListRes>>('/apis/api.link.halo.run/v1alpha1/links', {
|
||||||
params,
|
params,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ==================== 统计 / 埋点 / 插件 ==================== */
|
/* ==================== 统计 / 埋点 / 插件 ==================== */
|
||||||
@@ -266,8 +260,8 @@ export function getFriendLinkList(params: ICategoryListReq) {
|
|||||||
*/
|
*/
|
||||||
export function getBlogStatistics() {
|
export function getBlogStatistics() {
|
||||||
return http.Get<IResponse<IBlogStats>>('/apis/api.halo.run/v1alpha1/stats/-', {
|
return http.Get<IResponse<IBlogStats>>('/apis/api.halo.run/v1alpha1/stats/-', {
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,8 +269,8 @@ export function getBlogStatistics() {
|
|||||||
*/
|
*/
|
||||||
export function submitUpvote(data: IUpvoteReq) {
|
export function submitUpvote(data: IUpvoteReq) {
|
||||||
return http.Post<IResponse<unknown>>('/apis/api.halo.run/v1alpha1/trackers/upvote', data, {
|
return http.Post<IResponse<unknown>>('/apis/api.halo.run/v1alpha1/trackers/upvote', data, {
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -284,8 +278,8 @@ export function submitUpvote(data: IUpvoteReq) {
|
|||||||
*/
|
*/
|
||||||
export function postTrackersCounter(data: ITrackerCounterReq) {
|
export function postTrackersCounter(data: ITrackerCounterReq) {
|
||||||
return http.Post<IResponse<unknown>>('/apis/api.halo.run/v1alpha1/trackers/counter', data, {
|
return http.Post<IResponse<unknown>>('/apis/api.halo.run/v1alpha1/trackers/counter', data, {
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -294,9 +288,9 @@ export function postTrackersCounter(data: ITrackerCounterReq) {
|
|||||||
export function checkPluginAvailable(name: string) {
|
export function checkPluginAvailable(name: string) {
|
||||||
return http.Get<IResponse<boolean>>(`/apis/api.plugin.halo.run/v1alpha1/plugins/${name}/available`, {
|
return http.Get<IResponse<boolean>>(`/apis/api.plugin.halo.run/v1alpha1/plugins/${name}/available`, {
|
||||||
cacheFor: 0,
|
cacheFor: 0,
|
||||||
meta: { requestFrom: RequestFrom.Halo },
|
meta: { requestFrom: RequestFrom.Halo }
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 分类资源(供加密分类判断等场景) */
|
/** 分类资源(供加密分类判断等场景) */
|
||||||
export type { ICategory, ILink, IMoment, IPost }
|
export type { ICategory, ILink, IMoment, IPost };
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ export interface IPageConfig {
|
|||||||
/** 是否显示快捷导航(首页) */
|
/** 是否显示快捷导航(首页) */
|
||||||
useQuickNavigation?: boolean
|
useQuickNavigation?: boolean
|
||||||
bannerConfig?: IBannerConfig
|
bannerConfig?: IBannerConfig
|
||||||
|
/** 首页精选分类引用(插件端「通用配置 → 页面设置 → 首页」配置,固定最多 3 个,
|
||||||
|
* 数组顺序 = 展示顺序;未配置/为空时客户端回退默认取数) */
|
||||||
|
categories?: Array<{
|
||||||
|
name: string
|
||||||
|
displayName?: string
|
||||||
|
}>
|
||||||
}
|
}
|
||||||
categoryConfig?: { type?: string }
|
categoryConfig?: { type?: string }
|
||||||
momentConfig?: { useTagRandomColor?: boolean }
|
momentConfig?: { useTagRandomColor?: boolean }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { checkThumbnailUrl } from '@/utils/url'
|
import { checkAvatarUrl, checkThumbnailUrl } from '@/utils/url'
|
||||||
import { useSettingStore } from '@/store/setting'
|
import { useSettingStore } from '@/store/setting'
|
||||||
import { formatTime } from '@/utils/formatTime'
|
import { formatTime } from '@/utils/formatTime'
|
||||||
import type { ICategory, IPost } from '@/api/types/halo'
|
import type { ICategory, IPost } from '@/api/types/halo'
|
||||||
@@ -140,15 +140,6 @@
|
|||||||
animationType: 'slide-in-right',
|
animationType: 'slide-in-right',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToCategory(category : ICategory) {
|
|
||||||
if (props.auditMode) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
uni.navigateTo({
|
|
||||||
url: `/pages-blog/category-articles/category-articles?name=${category.metadata.name}&title=${category.spec.displayName}`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -171,7 +162,7 @@
|
|||||||
<view v-if="!isGrid" class="my-1 box-border flex flex-wrap gap-2" :class="cardLayout.tagCategory">
|
<view v-if="!isGrid" class="my-1 box-border flex flex-wrap gap-2" :class="cardLayout.tagCategory">
|
||||||
<template v-if="article.categories && article.categories.length !== 0">
|
<template v-if="article.categories && article.categories.length !== 0">
|
||||||
<text v-for="cate in article.categories" :key="cate.metadata.name"
|
<text v-for="cate in article.categories" :key="cate.metadata.name"
|
||||||
class="box-border uh-global-card-glass border uh-shadow-xs rounded-xl bg-secondary px-2 py-0.5 text-xs" @click.stop="handleToCategory(cate)">
|
class="box-border uh-global-card-glass border uh-shadow-xs rounded-xl bg-secondary px-2 py-0.5 text-xs">
|
||||||
{{ cate.spec.displayName }}
|
{{ cate.spec.displayName }}
|
||||||
</text>
|
</text>
|
||||||
</template>
|
</template>
|
||||||
@@ -184,7 +175,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="flex items-center text-xs text-gray-500" :class="cardLayout.footer">
|
<view class="flex items-center text-xs text-gray-500" :class="cardLayout.footer">
|
||||||
<view class="flex items-center" :class="cardLayout.authorGroup">
|
<view class="flex items-center" :class="cardLayout.authorGroup">
|
||||||
<image :src="article.owner.avatar" class="uh-global-card-glass h-5 w-5 rounded-full"
|
<image :src="checkAvatarUrl(article.owner?.avatar || '')" class="uh-global-card-glass h-5 w-5 rounded-full"
|
||||||
:class="cardLayout.avatar" mode="aspectFill" />
|
:class="cardLayout.avatar" mode="aspectFill" />
|
||||||
<template v-if="isSocialCard">
|
<template v-if="isSocialCard">
|
||||||
<view :class="cardLayout.infoCol">
|
<view :class="cardLayout.infoCol">
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { checkUrl } from '@/utils/url'
|
import { checkUrl } from '@/utils/url'
|
||||||
import { useAppConfigStore } from '@/store/appConfig'
|
|
||||||
import type { DataLoadingStatus } from '@/hooks/useDataLoading'
|
import type { DataLoadingStatus } from '@/hooks/useDataLoading'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
/** 加载状态(取值同 useDataLoading 返回的 status) */
|
|
||||||
loadingStatus ?: DataLoadingStatus
|
loadingStatus ?: DataLoadingStatus
|
||||||
/** 占位区最小高度 */
|
size ?: 'mini' | 'small' | 'large'
|
||||||
minHeight ?: string
|
minHeight ?: string
|
||||||
loadingText ?: string
|
loadingText ?: string
|
||||||
errorText ?: string
|
errorText ?: string
|
||||||
emptyText ?: string
|
emptyText ?: string
|
||||||
/** 各态副文案(留空则不显示副行) */
|
|
||||||
loadingSubText ?: string
|
loadingSubText ?: string
|
||||||
errorSubText ?: string
|
errorSubText ?: string
|
||||||
emptySubText ?: string
|
emptySubText ?: string
|
||||||
@@ -21,6 +18,7 @@
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<IProps>(), {
|
const props = withDefaults(defineProps<IProps>(), {
|
||||||
loadingStatus: 'loading',
|
loadingStatus: 'loading',
|
||||||
|
size: 'large',
|
||||||
minHeight: '80vh',
|
minHeight: '80vh',
|
||||||
loadingText: '稍等,正在加载中哦',
|
loadingText: '稍等,正在加载中哦',
|
||||||
errorText: '哎呀,加载失败了呢~',
|
errorText: '哎呀,加载失败了呢~',
|
||||||
@@ -33,14 +31,26 @@
|
|||||||
|
|
||||||
const emit = defineEmits<{ (e : 'refresh') : void }>()
|
const emit = defineEmits<{ (e : 'refresh') : void }>()
|
||||||
|
|
||||||
const appConfigStore = useAppConfigStore()
|
const SizeClasses = {
|
||||||
|
mini: {
|
||||||
const appInfo = computed(() => {
|
icon: '60rpx',
|
||||||
const _appInfo = (appConfigStore.configs?.appConfig?.appInfo as any)
|
stage: 'h-16 w-16',
|
||||||
return {
|
glow: 'h-12 w-12'
|
||||||
name: _appInfo.name ?? 'UniHalo',
|
},
|
||||||
logo: checkUrl(_appInfo?.logo ?? 'https://uni-halo.925i.cn/logo.png')
|
small: {
|
||||||
|
icon: '100rpx',
|
||||||
|
stage: 'h-22 w-22',
|
||||||
|
glow: 'h-18 w-18'
|
||||||
|
},
|
||||||
|
large: {
|
||||||
|
icon: '120rpx',
|
||||||
|
stage: 'h-32 w-32',
|
||||||
|
glow: 'h-28 w-28'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sizeClasses = computed(() => {
|
||||||
|
return SizeClasses[props.size] ?? SizeClasses.large
|
||||||
})
|
})
|
||||||
|
|
||||||
const isLoading = computed(() => props.loadingStatus === 'loading')
|
const isLoading = computed(() => props.loadingStatus === 'loading')
|
||||||
@@ -76,20 +86,17 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="relative w-full flex flex-col items-center justify-center gap-y-4 text-sm"
|
<view class="relative w-full flex flex-col items-center justify-center gap-y-3 text-sm"
|
||||||
:style="{ minHeight: props.minHeight }">
|
:style="{ minHeight: props.minHeight }">
|
||||||
|
|
||||||
<!-- logo背景 -->
|
<view class="scene relative z-1 flex items-center justify-center"
|
||||||
<image v-if="false" :src="appInfo.logo" class="absolute left-1/2 top-1/2 -translate-1/2 z-0 opacity-10 w-46 h-46"></image>
|
:class="[statusScene.stageClass,sizeClasses.stage]">
|
||||||
|
<view class="glow absolute inset-0 m-auto rounded-full" :class="sizeClasses.glow" />
|
||||||
<view class="scene relative z-1 h-[250rpx] w-[250rpx] flex items-center justify-center"
|
|
||||||
:class="statusScene.stageClass">
|
|
||||||
<view class="glow absolute inset-0 m-auto h-[220rpx] w-[220rpx] rounded-full" />
|
|
||||||
<view class="deco-dot dot-a absolute rounded-full" />
|
<view class="deco-dot dot-a absolute rounded-full" />
|
||||||
<view class="deco-dot dot-b absolute rounded-full" />
|
<view class="deco-dot dot-b absolute rounded-full" />
|
||||||
<view class="bubble relative h-[150rpx] w-[150rpx] flex items-center justify-center rounded-full">
|
<view class="bubble">
|
||||||
<text class="bubble-icon">
|
<text class="bubble-icon">
|
||||||
<wd-icon class-prefix="uhemoji-icon" :name="statusScene.icon" size="120rpx" />
|
<wd-icon class-prefix="uhemoji-icon" :name="statusScene.icon" :size="sizeClasses.icon" />
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -103,11 +110,12 @@
|
|||||||
:style="{ animationDelay: `${(n - 1) * 0.15}s` }" />
|
:style="{ animationDelay: `${(n - 1) * 0.15}s` }" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<text v-if="statusScene.subText" class="mt-3 text-xs text-gray-500">
|
<text v-if="statusScene.subText" class="mt-2 text-xs text-gray-500">
|
||||||
{{ statusScene.subText }}
|
{{ statusScene.subText }}
|
||||||
</text>
|
</text>
|
||||||
<view v-if="props.useRefreshButton" class="mt-5">
|
<view v-if="props.useRefreshButton" class="mt-4">
|
||||||
<uh-button custom-class="!rounded-lg uh-global-card-glass uh-shadow-xs border rounded-lg" @click="emit('refresh')">
|
<uh-button custom-class="!rounded-lg uh-global-card-glass uh-shadow-xs border"
|
||||||
|
@click="emit('refresh')">
|
||||||
刷新试试
|
刷新试试
|
||||||
</uh-button>
|
</uh-button>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { checkThumbnailUrl } from '@/utils/url'
|
import { checkThumbnailUrl } from '@/utils/url'
|
||||||
import { useAppConfigStore } from '@/store/appConfig'
|
import { useAppConfigStore } from '@/store/appConfig'
|
||||||
import type { ICategory } from '@/api/types/halo'
|
import type { ICategory } from '@/api/types/halo'
|
||||||
|
import { sleep } from '@/utils/common'
|
||||||
|
|
||||||
const appConfigStore = useAppConfigStore()
|
const appConfigStore = useAppConfigStore()
|
||||||
|
|
||||||
@@ -14,23 +15,34 @@
|
|||||||
const loading = ref<'loading' | 'success' | 'error'>('loading')
|
const loading = ref<'loading' | 'success' | 'error'>('loading')
|
||||||
const categoryList = ref<ICategory[]>([])
|
const categoryList = ref<ICategory[]>([])
|
||||||
|
|
||||||
const calcIsShowCategory = computed(() => {
|
const isEnableCategoryModule = computed(() => {
|
||||||
if (calcAuditModeEnabled.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !!haloConfigs.value.pageConfig?.homeConfig?.useCategory
|
return !!haloConfigs.value.pageConfig?.homeConfig?.useCategory
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 精选分类 */
|
|
||||||
async function handleGetCategoryList() {
|
async function handleGetCategoryList() {
|
||||||
if (calcAuditModeEnabled.value || !calcIsShowCategory.value) {
|
|
||||||
loading.value = 'success'
|
|
||||||
return
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
loading.value = 'loading'
|
loading.value = 'loading'
|
||||||
|
const configured = haloConfigs.value.pageConfig?.homeConfig?.categories
|
||||||
|
let categoryListRaw : ICategory[] = []
|
||||||
|
if (configured && configured.length) {
|
||||||
|
// 配置模式:按 name 用 in 查询(Halo fieldSelector 数组为 AND 语义,多 name 需 in 语法)
|
||||||
|
const names = configured.map(c => c.name)
|
||||||
|
const res = await getCategoryList({
|
||||||
|
fieldSelector: [`metadata.name in (${names.join(',')})`],
|
||||||
|
size: 3,
|
||||||
|
})
|
||||||
|
// 按配置顺序排列;配置的 name 查不到(分类已删除)则跳过
|
||||||
|
const byName = new Map(res.data.items.map(item => [item.metadata.name, item]))
|
||||||
|
categoryListRaw = names
|
||||||
|
.map(name => byName.get(name))
|
||||||
|
.filter((item) : item is ICategory => !!item)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 默认模式(老部署无配置):保持原有取数与排序
|
||||||
const res = await getCategoryList({ fieldSelector: ['spec.hideFromList=false'], size: 3 })
|
const res = await getCategoryList({ fieldSelector: ['spec.hideFromList=false'], size: 3 })
|
||||||
categoryList.value = res.data.items
|
categoryListRaw = res.data.items
|
||||||
|
}
|
||||||
|
categoryList.value = categoryListRaw
|
||||||
.map(item => {
|
.map(item => {
|
||||||
item.spec.cover = checkThumbnailUrl(item.spec.cover)
|
item.spec.cover = checkThumbnailUrl(item.spec.cover)
|
||||||
return {
|
return {
|
||||||
@@ -38,7 +50,14 @@
|
|||||||
postCount: item.postCount ?? 0
|
postCount: item.postCount ?? 0
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sort((a, b) => (b.postCount || 0) - (a.postCount || 0))
|
.sort((a, b) => {
|
||||||
|
if (configured && configured.length) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (b.postCount || 0) - (a.postCount || 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
await sleep(600)
|
||||||
loading.value = 'success'
|
loading.value = 'success'
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -54,8 +73,6 @@
|
|||||||
|
|
||||||
|
|
||||||
function handleToCategoryBy(category : ICategory) {
|
function handleToCategoryBy(category : ICategory) {
|
||||||
if (calcAuditModeEnabled.value)
|
|
||||||
return
|
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: `/pages-blog/category-articles/category-articles?name=${category.metadata.name}&title=${category.spec.displayName}`,
|
url: `/pages-blog/category-articles/category-articles?name=${category.metadata.name}&title=${category.spec.displayName}`,
|
||||||
})
|
})
|
||||||
@@ -67,28 +84,30 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view v-if="calcIsShowCategory" class="mb-6">
|
<view v-if="isEnableCategoryModule" class="mb-6">
|
||||||
<uh-section-title class="mb-4 px-3 box-border">
|
<uh-section-title class="mb-4 px-3 box-border">
|
||||||
精选分类
|
精选分类
|
||||||
<template #right>
|
<template #right>
|
||||||
<view class="flex items-center justify-center rounded-md bg-white p-1.5 text-gray-400" @click="handleToCategoryPage">
|
<view class="box-border flex items-center justify-center rounded-md bg-white p-1.5 text-gray-400"
|
||||||
<wd-icon name="arrow-right" size="12px" />
|
@click="handleToCategoryPage">
|
||||||
|
<wd-icon name="arrow-right" size="24rpx" />
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</uh-section-title>
|
</uh-section-title>
|
||||||
|
|
||||||
<view class="w-full grid grid-cols-2 grid-rows-auto h-42 box-border px-3 gap-2">
|
<view v-if="loading!=='success'" class="box-border px-3">
|
||||||
<view v-if="categoryList.length === 0"
|
<view class="uh-global-card-glass shadow-none rounded-xl">
|
||||||
class="cate-empty text-grey w-full flex items-center justify-center">
|
<uh-data-loading :loading-status="loading" min-height="28vh" size="small" :use-refresh-button="true"
|
||||||
还没有任何分类~
|
@refresh="handleGetCategoryList()" />
|
||||||
</view>
|
</view>
|
||||||
<block v-else>
|
</view>
|
||||||
|
|
||||||
|
<view v-else class="w-full grid grid-cols-2 grid-rows-auto h-42 box-border px-3 gap-2">
|
||||||
<view v-for="(category,index) in categoryList" :key="category.metadata.name"
|
<view v-for="(category,index) in categoryList" :key="category.metadata.name"
|
||||||
class="uh-global-card-glass relative w-full h-full overflow-hidden rounded-xl text-center text-white"
|
class="uh-global-card-glass relative w-full h-full overflow-hidden rounded-xl text-center text-white"
|
||||||
:class="{'grid-row-span-2':index===0 }" @click="handleToCategoryBy(category)">
|
:class="{'grid-row-span-2':index===0 }" @click="handleToCategoryBy(category)">
|
||||||
<image :src="category.spec.cover" class="w-full h-full" mode="aspectFill" lazy-load />
|
<image :src="category.spec.cover" class="w-full h-full" mode="aspectFill" lazy-load />
|
||||||
<view
|
<view class="absolute bottom-0 left-0 h-[140rpx] w-full bg-gradient-to-b from-black/0 to-black/30" />
|
||||||
class="absolute bottom-0 left-0 h-[140rpx] w-full bg-gradient-to-b from-black/0 to-black/30" />
|
|
||||||
<view class="absolute left-2 bottom-2 flex z-2 flex-col text-left">
|
<view class="absolute left-2 bottom-2 flex z-2 flex-col text-left">
|
||||||
<text class="text-sm font-bold">
|
<text class="text-sm font-bold">
|
||||||
{{ category.spec.displayName }}
|
{{ category.spec.displayName }}
|
||||||
@@ -96,7 +115,6 @@
|
|||||||
<text class="mt-1 text-xs text-gray-200">共 {{ category.postCount ?? 0 }} 篇</text>
|
<text class="mt-1 text-xs text-gray-200">共 {{ category.postCount ?? 0 }} 篇</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -2,6 +2,7 @@ import type { CustomRequestOptions } from '@/http/types';
|
|||||||
import { useTokenStore } from '@/store';
|
import { useTokenStore } from '@/store';
|
||||||
import { getEnvBaseUrl } from '@/utils';
|
import { getEnvBaseUrl } from '@/utils';
|
||||||
import { stringifyQuery } from './tools/queryString';
|
import { stringifyQuery } from './tools/queryString';
|
||||||
|
// import qs from 'qs'
|
||||||
|
|
||||||
// 请求基准地址
|
// 请求基准地址
|
||||||
const baseUrl = getEnvBaseUrl();
|
const baseUrl = getEnvBaseUrl();
|
||||||
@@ -16,7 +17,17 @@ const httpInterceptor = {
|
|||||||
|
|
||||||
// 非 alova 请求,正常执行
|
// 非 alova 请求,正常执行
|
||||||
// 接口请求支持通过 query 参数配置 queryString
|
// 接口请求支持通过 query 参数配置 queryString
|
||||||
|
|
||||||
|
|
||||||
if (options.query) {
|
if (options.query) {
|
||||||
|
// const queryStr = qs.stringify(options.query, {
|
||||||
|
// allowDots: true,
|
||||||
|
// encodeValuesOnly: true,
|
||||||
|
// skipNulls: true,
|
||||||
|
// encode: true,
|
||||||
|
// arrayFormat: 'repeat'
|
||||||
|
// })
|
||||||
|
|
||||||
const queryStr = stringifyQuery(options.query)
|
const queryStr = stringifyQuery(options.query)
|
||||||
if (options.url.includes('?')) {
|
if (options.url.includes('?')) {
|
||||||
options.url += `&${queryStr}`;
|
options.url += `&${queryStr}`;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { ref } from 'vue'
|
|||||||
import { onLoad, onPullDownRefresh, onReachBottom, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
import { onLoad, onPullDownRefresh, onReachBottom, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
|
||||||
import { getCategoryPostList } from '@/api/halo'
|
import { getCategoryPostList } from '@/api/halo'
|
||||||
import { DataLoadingStatusEnum, useDataLoadingStatus } from '@/hooks/useDataLoadingStatus'
|
import { DataLoadingStatusEnum, useDataLoadingStatus } from '@/hooks/useDataLoadingStatus'
|
||||||
import type { IPost } from '@/api/types/halo'
|
import type { IPost, IPostListReq } from '@/api/types/halo'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
style: {
|
style: {
|
||||||
@@ -23,6 +23,24 @@ const dataList = ref<IPost[]>([])
|
|||||||
const isLoadMore = ref(false)
|
const isLoadMore = ref(false)
|
||||||
const loadMoreText = ref('')
|
const loadMoreText = ref('')
|
||||||
|
|
||||||
|
/* ---------------- 排序切换(sort 参数由接口透传,见 IPostListReq.sort) ---------------- */
|
||||||
|
const sortOptions: { key: string; label: string; sort?: string[] }[] = [
|
||||||
|
{ key: 'default', label: '默认' },
|
||||||
|
{ key: 'pinned', label: '按置顶', sort: ['spec.pinned,desc'] },
|
||||||
|
{ key: 'latest', label: '按最新', sort: ['metadata.creationTimestamp,desc'] },
|
||||||
|
{ key: 'oldest', label: '按最旧', sort: ['metadata.creationTimestamp,asc'] },
|
||||||
|
]
|
||||||
|
const activeSort = ref('default')
|
||||||
|
|
||||||
|
function handleSortChange(key: string) {
|
||||||
|
if (activeSort.value === key)
|
||||||
|
return
|
||||||
|
activeSort.value = key
|
||||||
|
isLoadMore.value = false
|
||||||
|
queryParams.value.page = 0
|
||||||
|
handleGetData()
|
||||||
|
}
|
||||||
|
|
||||||
async function handleGetData() {
|
async function handleGetData() {
|
||||||
if (!isLoadMore.value) {
|
if (!isLoadMore.value) {
|
||||||
updateLoadingStatus(DataLoadingStatusEnum.Loading)
|
updateLoadingStatus(DataLoadingStatusEnum.Loading)
|
||||||
@@ -30,7 +48,15 @@ async function handleGetData() {
|
|||||||
loadMoreText.value = '加载中...'
|
loadMoreText.value = '加载中...'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await getCategoryPostList(name.value, { ...queryParams.value })
|
const reqParams: Record<string, unknown> = { ...queryParams.value }
|
||||||
|
const currentSort = sortOptions.find(opt => opt.key === activeSort.value)?.sort
|
||||||
|
if (currentSort) {
|
||||||
|
reqParams.sort = currentSort
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete reqParams.sort
|
||||||
|
}
|
||||||
|
const res = await getCategoryPostList(name.value, reqParams as IPostListReq)
|
||||||
navbarTitle.value = `${pageTitle.value} (共${res.data.total}篇)`
|
navbarTitle.value = `${pageTitle.value} (共${res.data.total}篇)`
|
||||||
hasNext.value = res.data.hasNext
|
hasNext.value = res.data.hasNext
|
||||||
dataList.value = isLoadMore.value
|
dataList.value = isLoadMore.value
|
||||||
@@ -62,16 +88,6 @@ function handleToArticleDetail(article: IPost) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToTopPage(duration = 500) {
|
|
||||||
uni.pageScrollTo({
|
|
||||||
scrollTop: 0,
|
|
||||||
duration,
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('回顶失败', err)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad((options) => {
|
||||||
name.value = options?.name || ''
|
name.value = options?.name || ''
|
||||||
pageTitle.value = options?.title || '分类详情'
|
pageTitle.value = options?.title || '分类详情'
|
||||||
@@ -112,6 +128,19 @@ onShareTimeline(() => ({
|
|||||||
<!-- 自定义导航 -->
|
<!-- 自定义导航 -->
|
||||||
<uh-navbar :default-title="navbarTitle" title-color="text-gray-900" />
|
<uh-navbar :default-title="navbarTitle" title-color="text-gray-900" />
|
||||||
|
|
||||||
|
<!-- 排序切换:默认 / 按置顶 / 按最新 / 按最旧 -->
|
||||||
|
<view class="box-border flex items-center gap-2 px-3 py-2">
|
||||||
|
<view
|
||||||
|
v-for="opt in sortOptions"
|
||||||
|
:key="opt.key"
|
||||||
|
class="rounded-full px-3 py-1 text-xs"
|
||||||
|
:class="activeSort === opt.key ? 'bg-secondary font-bold' : 'uh-global-card-glass shadow-none border text-gray-500'"
|
||||||
|
@click="handleSortChange(opt.key)"
|
||||||
|
>
|
||||||
|
{{ opt.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 加载/错误/空占位(状态机) -->
|
<!-- 加载/错误/空占位(状态机) -->
|
||||||
<view v-if="loadingStatus !== 'success'">
|
<view v-if="loadingStatus !== 'success'">
|
||||||
<uh-data-loading
|
<uh-data-loading
|
||||||
@@ -122,6 +151,7 @@ onShareTimeline(() => ({
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<block v-else>
|
<block v-else>
|
||||||
|
<view class="box-border flex flex-col gap-y-3 p-3">
|
||||||
<uh-article-card
|
<uh-article-card
|
||||||
v-for="(article, index) in dataList"
|
v-for="(article, index) in dataList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -131,9 +161,6 @@ onShareTimeline(() => ({
|
|||||||
<view class="load-text py-5 text-center text-[24rpx] text-[#999]">
|
<view class="load-text py-5 text-center text-[24rpx] text-[#999]">
|
||||||
{{ loadMoreText }}
|
{{ loadMoreText }}
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="to-top-btn fixed bottom-[100rpx] right-6 z-6 h-[72rpx] w-[72rpx] flex items-center justify-center rounded-full bg-white shadow-sm" @click="handleToTopPage()">
|
|
||||||
<wd-icon name="arrow-up" size="20px" color="#03a9f4" />
|
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -362,7 +362,7 @@
|
|||||||
kind="Moment" :disallow-comment="!moment.spec.allowComment" @on-comment="handleOnComment" />
|
kind="Moment" :disallow-comment="!moment.spec.allowComment" @on-comment="handleOnComment" />
|
||||||
</view>
|
</view>
|
||||||
<!-- 悬浮操作(与文章详情一致:点赞/评论/收藏) -->
|
<!-- 悬浮操作(与文章详情一致:点赞/评论/收藏) -->
|
||||||
<view class="fixed bottom-8 left-1/2 z-10 flex items-center justify-center pb-safe -translate-x-1/2">
|
<view v-if="moment" class="fixed bottom-8 left-1/2 z-10 flex items-center justify-center pb-safe -translate-x-1/2">
|
||||||
<view
|
<view
|
||||||
class="uh-global-card-glass box-border flex items-center justify-center gap-2 border rounded-full p-1 text-primary">
|
class="uh-global-card-glass box-border flex items-center justify-center gap-2 border rounded-full p-1 text-primary">
|
||||||
<!-- 点赞 -->
|
<!-- 点赞 -->
|
||||||
@@ -384,8 +384,8 @@
|
|||||||
class="uh-global-card-glass box-border h-[72rpx] flex flex-1 items-center justify-center gap-x-1 border rounded-full px-4 shadow-none"
|
class="uh-global-card-glass box-border h-[72rpx] flex flex-1 items-center justify-center gap-x-1 border rounded-full px-4 shadow-none"
|
||||||
@click="handleToggleMomentFavorite">
|
@click="handleToggleMomentFavorite">
|
||||||
<wd-icon class-prefix="uhemoji-icon" name="-smile-" size="36rpx" />
|
<wd-icon class-prefix="uhemoji-icon" name="-smile-" size="36rpx" />
|
||||||
<text class="shrink-0 text-sm text-gray-900 font-semibold"
|
<text class="shrink-0 text-sm font-semibold"
|
||||||
:style="momentFavorited ? { color: '#ffb300' } : ''">{{ momentFavorited ? '已收藏' : '收藏' }}</text>
|
:class="momentFavorited ? 'text-primary' : 'text-gray-900'">{{ momentFavorited ? '已收藏' : '收藏' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
Reference in New Issue
Block a user