文章详情
This commit is contained in:
@@ -4,6 +4,7 @@ export default defineAppConfig({
|
||||
'pages/tasks/index',
|
||||
'pages/messages/index',
|
||||
'pages/profile/index',
|
||||
'pages/article/index',
|
||||
],
|
||||
window: {
|
||||
backgroundTextStyle: 'light',
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { articles } from './articles'
|
||||
|
||||
/** 文章详情额外字段 */
|
||||
interface ArticleDetail {
|
||||
id: string
|
||||
content: string
|
||||
images: string[]
|
||||
}
|
||||
|
||||
/** 用一段长文本来模拟文章正文 */
|
||||
const FULL_CONTENT = `近日,学校召开了2024年秋季学期教学工作会议。会议总结了上学期教学工作取得的成绩,并对新学期教学工作进行了全面部署。
|
||||
|
||||
教务处处长在会议上指出,上学期全校教师积极探索教学改革,推行线上线下混合式教学模式,取得了显著成效。学生在各类学科竞赛中斩获国家级奖项12项、省级奖项35项,创历史新高。
|
||||
|
||||
会议特别强调了以下几点:
|
||||
|
||||
第一,持续推进课程思政建设。各学院要深入挖掘专业课程中蕴含的思政元素,将价值引领融入知识传授全过程,培养德才兼备的高素质人才。
|
||||
|
||||
第二,深化产教融合。学校将进一步加强与企业的合作,建设一批高水平的校外实践教学基地,为学生提供更多实习实训机会。目前已与30余家知名企业达成合作协议。
|
||||
|
||||
第三,优化课程体系。根据社会需求和学科发展趋势,及时调整课程设置,增加人工智能、大数据等前沿领域的课程内容,提升学生的核心竞争力。
|
||||
|
||||
第四,强化教学质量监控。完善教学督导制度,建立学生评教、同行评议、专家评价相结合的教学质量评价体系,确保教学质量持续提升。
|
||||
|
||||
会上,多位优秀教师代表分享了教学经验。来自计算机学院的张老师介绍了翻转课堂的实践经验,引起与会教师的广泛共鸣。
|
||||
|
||||
会议最后,校领导对新学期教学工作提出了殷切期望,希望全体教师以更加饱满的热情投入到教学工作中,为培养新时代优秀人才贡献力量。`
|
||||
|
||||
export function getArticleDetail(id: string) {
|
||||
const article = articles.find((a) => a.id === id)
|
||||
if (!article) return null
|
||||
return {
|
||||
...article,
|
||||
content: article.excerpt + '\n\n' + FULL_CONTENT,
|
||||
images: article.images,
|
||||
} as ArticleDetail & typeof article
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { Comment } from '@/types/comment'
|
||||
|
||||
/** 模拟评论数据 */
|
||||
export const comments: Comment[] = [
|
||||
{
|
||||
id: 'c1',
|
||||
author: { name: '张同学', avatar: 'https://picsum.photos/seed/c1/100/100' },
|
||||
content: '写得太好了!对新学期的工作重点有了更清晰的了解,期待学校的教学改革能带来更好的学习体验。',
|
||||
time: '2小时前',
|
||||
likes: 28,
|
||||
liked: true,
|
||||
},
|
||||
{
|
||||
id: 'c2',
|
||||
author: { name: '李老师', avatar: 'https://picsum.photos/seed/c2/100/100' },
|
||||
content: '作为一线教师,深切感受到学校对教学工作的重视。翻转课堂确实能激发学生的学习主动性,推荐更多老师尝试。',
|
||||
time: '3小时前',
|
||||
likes: 15,
|
||||
},
|
||||
{
|
||||
id: 'c3',
|
||||
author: { name: '王同学', avatar: 'https://picsum.photos/seed/c3/100/100' },
|
||||
content: '产教融合这个方向真的很赞,希望能多一些实习机会,让我们在校期间就能接触真实项目。',
|
||||
time: '5小时前',
|
||||
likes: 42,
|
||||
replies: [
|
||||
{
|
||||
id: 'c3r1',
|
||||
author: { name: '教务处回复', avatar: 'https://picsum.photos/seed/admin/100/100' },
|
||||
content: '感谢同学的反馈!学校正在积极拓展合作企业,预计下学期将新增20个实习基地,请关注后续通知。',
|
||||
time: '4小时前',
|
||||
likes: 18,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'c4',
|
||||
author: { name: '赵同学', avatar: 'https://picsum.photos/seed/c4/100/100' },
|
||||
content: '希望课程设置能多加入一些AI相关的内容,现在就业市场对这方面的需求很大。',
|
||||
time: '6小时前',
|
||||
likes: 35,
|
||||
},
|
||||
{
|
||||
id: 'c5',
|
||||
author: { name: '陈同学', avatar: 'https://picsum.photos/seed/c5/100/100' },
|
||||
content: '选修课的种类能不能再多一些?特别是跨学科的课程,感觉很有意思。',
|
||||
time: '8小时前',
|
||||
likes: 12,
|
||||
},
|
||||
{
|
||||
id: 'c6',
|
||||
author: { name: '刘同学', avatar: 'https://picsum.photos/seed/c6/100/100' },
|
||||
content: '教学质量监控很重要,希望能真实反映课堂情况,不要流于形式。',
|
||||
time: '昨天',
|
||||
likes: 20,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '文章详情',
|
||||
navigationStyle: 'custom',
|
||||
})
|
||||
@@ -0,0 +1,316 @@
|
||||
/* ========================================
|
||||
文章详情页
|
||||
======================================== */
|
||||
|
||||
.article-page {
|
||||
min-height: 100vh;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ========== 导航栏 ========== */
|
||||
.article-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
|
||||
.nav-content {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
position: relative;
|
||||
|
||||
.nav-back {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.nav-placeholder {
|
||||
width: 50px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 滚动区域 ========== */
|
||||
.article-scroll {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ========== 文章内容 ========== */
|
||||
.article-content {
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
display: block;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26px;
|
||||
color: #969799;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #f2f3f5;
|
||||
|
||||
.meta-source {
|
||||
color: #1989fa;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.meta-time {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.meta-read {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.article-body {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.article-paragraph {
|
||||
display: block;
|
||||
font-size: 32px;
|
||||
line-height: 1.85;
|
||||
color: #323233;
|
||||
margin-bottom: 20px;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.article-images {
|
||||
margin-bottom: 24px;
|
||||
|
||||
.article-img {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 点赞/评论/分享栏 ========== */
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding: 28px 32px;
|
||||
margin: 0 32px;
|
||||
background: #f7f8fa;
|
||||
border-radius: 16px;
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.action-text {
|
||||
font-size: 28px;
|
||||
color: #646566;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 评论区域 ========== */
|
||||
.comment-section {
|
||||
margin-top: 32px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.comment-title {
|
||||
font-size: 34px;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.comment-count {
|
||||
font-size: 28px;
|
||||
color: #969799;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 评论列表 */
|
||||
.comment-list {
|
||||
.comment-item {
|
||||
display: flex;
|
||||
padding: 24px 0;
|
||||
border-bottom: 1px solid #f2f3f5;
|
||||
|
||||
.comment-avatar {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-body {
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.comment-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.comment-name {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 24px;
|
||||
color: #c8c9cc;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-text {
|
||||
display: block;
|
||||
font-size: 30px;
|
||||
color: #323233;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.comment-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.comment-like {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 4px 0;
|
||||
|
||||
.like-count {
|
||||
font-size: 24px;
|
||||
color: #969799;
|
||||
|
||||
&.liked {
|
||||
color: #ee0a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 子回复 */
|
||||
.reply-list {
|
||||
margin-top: 16px;
|
||||
background: #f7f8fa;
|
||||
border-radius: 12px;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
.reply-item {
|
||||
display: flex;
|
||||
padding: 12px 0;
|
||||
|
||||
& + .reply-item {
|
||||
border-top: 1px solid #ebedf0;
|
||||
}
|
||||
|
||||
.reply-avatar {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.no-comments {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
font-size: 28px;
|
||||
color: #c8c9cc;
|
||||
}
|
||||
|
||||
/* ========== 底部评论输入框 ========== */
|
||||
.comment-input-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #f2f3f5;
|
||||
gap: 16px;
|
||||
|
||||
.input-wrap {
|
||||
flex: 1;
|
||||
background: #f7f8fa;
|
||||
border-radius: 40px;
|
||||
padding: 12px 24px;
|
||||
|
||||
.comment-input {
|
||||
font-size: 28px;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
flex-shrink: 0;
|
||||
padding: 12px 28px;
|
||||
background: #1989fa;
|
||||
border-radius: 40px;
|
||||
|
||||
.send-text {
|
||||
font-size: 28px;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部安全区占位 */
|
||||
.comment-bottom-safe {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/* 文章不存在 */
|
||||
.empty-article {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
font-size: 30px;
|
||||
color: #969799;
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
import { useState, useMemo, useCallback } from 'react'
|
||||
import Taro, { useRouter } from '@tarojs/taro'
|
||||
import { View, Text, Image, ScrollView, Input } from '@tarojs/components'
|
||||
import { Icon } from '@antmjs/vantui'
|
||||
import { getArticleDetail } from '@/mock/articleDetail'
|
||||
import { comments } from '@/mock/comments'
|
||||
import type { Comment } from '@/types/comment'
|
||||
import './index.less'
|
||||
|
||||
/** 状态栏和导航栏高度 */
|
||||
function getStatusBarH(): number {
|
||||
try { return Taro.getSystemInfoSync().statusBarHeight || 20 }
|
||||
catch { return 20 }
|
||||
}
|
||||
|
||||
export default function ArticleDetail() {
|
||||
// useRouter 在部分 Taro 版本首次渲染 params 为空,用 getCurrentInstance 兜底
|
||||
const router = useRouter()
|
||||
const id = router.params.id || Taro.getCurrentInstance().router?.params?.id
|
||||
const article = useMemo(() => (id ? getArticleDetail(id) : null), [id])
|
||||
const [commentText, setCommentText] = useState('')
|
||||
const [commentList, setCommentList] = useState<Comment[]>(comments)
|
||||
|
||||
const statusBarH = useMemo(getStatusBarH, [])
|
||||
|
||||
/** 返回 */
|
||||
const handleBack = useCallback(() => {
|
||||
Taro.navigateBack()
|
||||
}, [])
|
||||
|
||||
/** 点赞评论 */
|
||||
const handleLikeComment = useCallback((commentId: string) => {
|
||||
setCommentList((prev) =>
|
||||
prev.map((c) => {
|
||||
if (c.id === commentId) {
|
||||
return { ...c, liked: !c.liked, likes: c.liked ? c.likes - 1 : c.likes + 1 }
|
||||
}
|
||||
if (c.replies) {
|
||||
return {
|
||||
...c,
|
||||
replies: c.replies.map((r) =>
|
||||
r.id === commentId ? { ...r, liked: !r.liked, likes: r.liked ? r.likes - 1 : r.likes + 1 } : r,
|
||||
),
|
||||
}
|
||||
}
|
||||
return c
|
||||
}),
|
||||
)
|
||||
}, [])
|
||||
|
||||
/** 发送评论 */
|
||||
const handleSendComment = useCallback(() => {
|
||||
if (!commentText.trim()) return
|
||||
const newComment: Comment = {
|
||||
id: `c${Date.now()}`,
|
||||
author: { name: '我', avatar: 'https://picsum.photos/seed/me/100/100' },
|
||||
content: commentText.trim(),
|
||||
time: '刚刚',
|
||||
likes: 0,
|
||||
}
|
||||
setCommentList((prev) => [newComment, ...prev])
|
||||
setCommentText('')
|
||||
Taro.showToast({ title: '评论成功', icon: 'success' })
|
||||
}, [commentText])
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<View className='article-page'>
|
||||
<View className='empty-article'>文章不存在</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
// 正文段落
|
||||
const paragraphs = article.content.split('\n\n').filter(Boolean)
|
||||
|
||||
return (
|
||||
<View className='article-page'>
|
||||
{/* ========== 自定义导航栏 ========== */}
|
||||
<View className='article-nav' style={{ paddingTop: `${statusBarH}px` }}>
|
||||
<View className='nav-content'>
|
||||
<View className='nav-back' onClick={handleBack}>
|
||||
<Icon name='arrow-left' size={22} color='#323233' />
|
||||
</View>
|
||||
<Text className='nav-title'>文章详情</Text>
|
||||
<View className='nav-placeholder' />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 占位 */}
|
||||
<View style={{ height: `${statusBarH + 44}px` }} />
|
||||
|
||||
<ScrollView className='article-scroll' scrollY enhanced showScrollbar={false}>
|
||||
{/* ========== 文章内容 ========== */}
|
||||
<View className='article-content'>
|
||||
{/* 标题 */}
|
||||
<Text className='article-title'>{article.title}</Text>
|
||||
|
||||
{/* Meta 信息 */}
|
||||
<View className='article-meta'>
|
||||
<Text className='meta-source'>{article.source}</Text>
|
||||
<Text className='meta-time'>{article.time}</Text>
|
||||
<Text className='meta-read'>{article.readCount} 阅读</Text>
|
||||
</View>
|
||||
|
||||
{/* 正文 */}
|
||||
<View className='article-body'>
|
||||
{paragraphs.map((p, idx) => (
|
||||
<Text key={idx} className='article-paragraph'>{p}</Text>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* 图片 */}
|
||||
{article.images && article.images.length > 0 && (
|
||||
<View className='article-images'>
|
||||
{article.images.map((img, idx) => (
|
||||
<Image
|
||||
key={idx}
|
||||
className='article-img'
|
||||
src={img}
|
||||
mode='widthFix'
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* ========== 点赞/评论/分享栏 ========== */}
|
||||
<View className='action-bar'>
|
||||
<View className='action-item'>
|
||||
<Icon name='like-o' size={20} color='#646566' />
|
||||
<Text className='action-text'>点赞</Text>
|
||||
</View>
|
||||
<View className='action-item'>
|
||||
<Icon name='comment-o' size={20} color='#646566' />
|
||||
<Text className='action-text'>{commentList.length}</Text>
|
||||
</View>
|
||||
<View className='action-item'>
|
||||
<Icon name='share-o' size={20} color='#646566' />
|
||||
<Text className='action-text'>分享</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* ========== 评论区域 ========== */}
|
||||
<View className='comment-section'>
|
||||
<View className='comment-header'>
|
||||
<Text className='comment-title'>全部评论</Text>
|
||||
<Text className='comment-count'>({commentList.length})</Text>
|
||||
</View>
|
||||
|
||||
{commentList.length > 0 ? (
|
||||
<View className='comment-list'>
|
||||
{commentList.map((comment) => (
|
||||
<View key={comment.id} className='comment-item'>
|
||||
<Image
|
||||
className='comment-avatar'
|
||||
src={comment.author.avatar}
|
||||
mode='aspectFill'
|
||||
/>
|
||||
<View className='comment-body'>
|
||||
<View className='comment-top'>
|
||||
<Text className='comment-name'>{comment.author.name}</Text>
|
||||
<Text className='comment-time'>{comment.time}</Text>
|
||||
</View>
|
||||
<Text className='comment-text'>{comment.content}</Text>
|
||||
<View className='comment-actions'>
|
||||
<View
|
||||
className='comment-like'
|
||||
onClick={() => handleLikeComment(comment.id)}
|
||||
>
|
||||
<Icon
|
||||
name={comment.liked ? 'like' : 'like-o'}
|
||||
size={16}
|
||||
color={comment.liked ? '#ee0a24' : '#969799'}
|
||||
/>
|
||||
<Text className={`like-count ${comment.liked ? 'liked' : ''}`}>
|
||||
{comment.likes || '赞'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 子回复 */}
|
||||
{comment.replies && comment.replies.length > 0 && (
|
||||
<View className='reply-list'>
|
||||
{comment.replies.map((reply) => (
|
||||
<View key={reply.id} className='reply-item'>
|
||||
<Image
|
||||
className='reply-avatar'
|
||||
src={reply.author.avatar}
|
||||
mode='aspectFill'
|
||||
/>
|
||||
<View className='comment-body'>
|
||||
<View className='comment-top'>
|
||||
<Text className='comment-name'>{reply.author.name}</Text>
|
||||
<Text className='comment-time'>{reply.time}</Text>
|
||||
</View>
|
||||
<Text className='comment-text'>{reply.content}</Text>
|
||||
<View className='comment-actions'>
|
||||
<View
|
||||
className='comment-like'
|
||||
onClick={() => handleLikeComment(reply.id)}
|
||||
>
|
||||
<Icon
|
||||
name={reply.liked ? 'like' : 'like-o'}
|
||||
size={16}
|
||||
color={reply.liked ? '#ee0a24' : '#969799'}
|
||||
/>
|
||||
<Text className={`like-count ${reply.liked ? 'liked' : ''}`}>
|
||||
{reply.likes || '赞'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : (
|
||||
<View className='no-comments'>暂无评论,快来抢沙发吧~</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 底部占位(防止被输入框遮挡) */}
|
||||
<View className='comment-bottom-safe' />
|
||||
</ScrollView>
|
||||
|
||||
{/* ========== 底部评论输入框(固定) ========== */}
|
||||
<View className='comment-input-bar'>
|
||||
<View className='input-wrap'>
|
||||
<Input
|
||||
className='comment-input'
|
||||
value={commentText}
|
||||
placeholder='写下你的评论...'
|
||||
placeholderStyle='color: #c8c9cc;'
|
||||
onInput={(e) => setCommentText(e.detail.value)}
|
||||
confirmType='send'
|
||||
onConfirm={handleSendComment}
|
||||
/>
|
||||
</View>
|
||||
<View className='send-btn' onClick={handleSendComment}>
|
||||
<Text className='send-text'>发送</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/** 评论 */
|
||||
export interface Comment {
|
||||
id: string
|
||||
author: {
|
||||
name: string
|
||||
avatar: string
|
||||
}
|
||||
content: string
|
||||
time: string
|
||||
likes: number
|
||||
liked?: boolean
|
||||
/** 子回复 */
|
||||
replies?: Comment[]
|
||||
}
|
||||
Reference in New Issue
Block a user