From 75737034d95c154de8d71d923e5d1c6502bd3079 Mon Sep 17 00:00:00 2001 From: Sakurasan <26715255+Sakurasan@users.noreply.github.com> Date: Sat, 26 Sep 2026 22:34:36 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E8=AE=BA=EF=BC=9A=E5=8E=BB=E6=B0=94?= =?UTF-8?q?=E6=B3=A1=E6=94=B9=E7=BA=B8=E9=9D=A2=E6=8E=92=E7=89=88=20+=20?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D=20@=E6=8F=90=E5=8F=8A=20+=20=E5=8F=8C?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E6=89=8B=E6=9C=BA=2012px=20=E8=B4=B4?= =?UTF-8?q?=E8=BE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 视觉:评论区从「带边框的聊天气泡盒」改成与去卡片后文章页一致的纸面排版 - .cm-bubble 类名保留(DOM 不动)盒子拆掉:正文直接落在纸面上,层级靠 头像 / 名字行 / 留白撑起,条与条之间不画线不画盒;删除态靠变灰变斜认 - 输入区去卡片盒,只留 textarea 底下一条「书写线」,聚焦变强调色 (vivid 再加同色光晕,与 .search 聚焦同一语言);名字 / 计数 / 发送 排在线下方,像在纸上落款 - 操作钮 ✎ ✕ ↩ 圆图标 → 一行文字钮(回复/编辑/删除):桌面 hover 浮现、 位置预留不跳版;触屏常显并加大可点面积;删除悬停给红;补 focus-visible 环 (文字钮无边框,键盘可达性自己画) - 回复串:空白缩进改左侧发丝线(与左栏时间线同一语言);26px 小头像不再 戴登录方式小标(戴不下);登录引导卡去框,一行引导 + 一排登录圆钮 - 暗色代码块修复(.cm-md pre 与 .prose pre 同一 bug):背景 color-mix(92% ink, accent) 在暗色下混出浅底浅字,暗端覆盖为 color-mix(86% paper, accent) + 细边框 @提及 - 回复另一条回复时正文前带强调色 @对方名字,扁平列表里才看得出在跟谁 说话;回主楼不标。名字由 parent_id 在已加载回复列表里解析,父回复 未加载就不标 —— 后端阶段二契约不变,纯前端即可工作 - 浮动贴正文首行行首(与首字下沉同一技巧),字号行高与 .cm-md 成对同步 (基础 / 手机断点 / vivid 三处,改字号要一起改);正文以代码块、列表 开头时退成独立一行,避免被块级背景盖住 - 编辑态去掉「正在改定此评:」,交给 placeholder;mock 加 reply-to-reply 用例(Mason 回 ONE) 手机 12px 贴边(两套主题正文与评论共用同一条左缘) - vivid:.article-layout 手机断点 1fr → minmax(0,1fr)。裸 1fr 的自动最小 尺寸是 min-content,正文里不可断行的长 token 把整列顶到 592px、整页 横向溢出(实测 375 视口 bodyScrollWidth 612);页边距 20 → 12; .prose 加 overflow-wrap:break-word - classic:.shell 手机 20 → 12(首页信息流同步收边),PostView .wrap ≤640 侧边距归零 —— 那 4px 正是正文比评论多出的一截 - 评论手机断点(styles.css 基础段,两套 UI 共用):头像 36→28、回复 缩进收紧、排序 / 登录 / 操作钮 44px 触控目标、输入 16px 防 iOS 聚焦缩放;.cm-md 加 overflow-wrap:break-word(用户内容长链接必须断行) 杂项 - vite dev 加 --host:手机连局域网可直接评审 - 实测(浏览器逐状态截图过目):vivid/classic × 亮/暗 × 桌面/390, 默认 / 未登录 / 空 / 审核中 / 已删除 / 多级回复 / 回复框 / 编辑框 共 13 张;375 视口 bodyScrollWidth = 375 无溢出;pnpm build 通过 --- frontend/package.json | 2 +- .../src/components/comments/CommentItem.vue | 40 +++- frontend/src/mocks/fixtures.js | 10 +- frontend/src/styles.css | 213 +++++++++++------- frontend/src/ui/yohaku/article.css | 13 +- frontend/src/ui/yohaku/comments.css | 85 +++---- frontend/src/ui/yohaku/mobile.css | 8 +- frontend/src/views/PostView.vue | 8 + 8 files changed, 238 insertions(+), 141 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 60062d6..b3ec1dd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,7 +5,7 @@ "type": "module", "packageManager": "pnpm@10.34.4", "scripts": { - "dev": "vite", + "dev": "vite --host", "build": "vite build", "preview": "vite preview" }, diff --git a/frontend/src/components/comments/CommentItem.vue b/frontend/src/components/comments/CommentItem.vue index a7baabf..27607a1 100644 --- a/frontend/src/components/comments/CommentItem.vue +++ b/frontend/src/components/comments/CommentItem.vue @@ -8,11 +8,16 @@ import { relativeDate } from '../../utils' // 单条评论。回复在同一个组件里自引用渲染(depth 0 → 1), // 这样「回复框 / 编辑框」的模板只写一遍,两层不会各写一份再漂移。 // 只渲染一层:depth >= 1 时不再显示它自己的 replies 与「加载更多」。 +// +// 回复之间的相互回复在扁平列表里靠 @提及 认亲:depth 0 渲染每条回复时 +// 解析出它回的是谁(parent_id 指向主楼 → 不标;指向同串里另一条回复 → +// 把那条的作者名传下去)。父回复没被加载进来时解析不到,就不标。 const props = defineProps({ c: { type: Object, required: true }, api: { type: Object, required: true }, depth: { type: Number, default: 0 }, - maxLen: { type: Number, default: 500 } + maxLen: { type: Number, default: 500 }, + replyTo: { type: String, default: '' } }) const s = props.api.s @@ -77,6 +82,19 @@ async function onRemove() { const moreReplies = computed(() => props.depth === 0 ? Math.max(0, (props.c.reply_count || 0) - ((props.c.replies || []).length)) : 0 ) + +// 这条回复要 @ 谁:parent 指向主楼(props.c)不用标;指向串里另一条 +// 已加载的回复才标。正文以块级元素开头(代码块/列表)时 @ 没法浮在 +// 首行行首,模板里会退成独立一行。 +const atInline = computed( + () => !!props.replyTo && /^]/i.test((props.c.body_html || '').trim()) +) + +function replyNameOf(r) { + if (!r.parent_id || r.parent_id === props.c.id) return '' + const t = (props.c.replies || []).find((x) => x.id === r.parent_id) + return t ? (t.author && t.author.name) || '' : '' +}