feat(fe): 评论改成小红书那套交互——点评论直接唤起输入

上一版做成了居中弹窗,参考图给的是另一种范式:输入栏常驻底部,点任意
一条评论就把它激活并填上「回复 @xxx」。按参考图重做,居中那套一并删掉,
不留死代码。

结构从「一个滚动容器装所有东西」改成三段式:
  固定头(共 N 条评论)+ 滚动列表 + 常驻输入栏
输入栏必须在滚动区外——放进去的话列表一滚它就跟着走,点完评论还得再
把输入框找回来,正是这次要解决的问题。

交互:
- 点评论或回复的任意位置 → 输入框获得焦点,placeholder 变「回复 @xxx」,
  上方出现可取消的橙色提示条。不用再单独点一个「回复」按钮
- 删除从长按改成显式的「删除」文字,只在自己的评论上出现——长按是
  隐藏交互,没人会发现
- 回复带首字头像并缩进一层,「甲 回复 乙」的指向关系显示在昵称行

键盘处理用 adjust-position=false + bindfocus 拿键盘高度自己顶:弹层是
fixed 定位,交给系统调整会把整块推出屏幕。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-29 16:39:36 +08:00
parent 9fc61e878e
commit fe420ba47c
3 changed files with 150 additions and 115 deletions
@@ -117,9 +117,11 @@ Component({
proInfo: null,
saving: false,
posterSaving: false,
centered: false,
commentImages: [],
replyTo: {},
cmFocus: false,
kbHeight: 0,
total: 0,
},
observers: {
show: function (show) {
@@ -139,8 +141,7 @@ Component({
methods: {
setType(type, fresh) {
const pet = store.getPet();
// 评论以输入为主,贴底弹层会被键盘顶掉大半屏,改成居中
const patch = { innerType: type, pet, centered: type === 'comments' };
const patch = { innerType: type, pet };
if (fresh) {
patch.segSel = {};
patch.optSel = {};
@@ -167,6 +168,8 @@ Component({
patch.commentText = '';
patch.commentImages = [];
patch.replyTo = {};
patch.cmFocus = false;
patch.kbHeight = 0;
patch.riskData = null;
patch.saving = false;
}
@@ -413,13 +416,18 @@ Component({
api.listComments(this.data.postId)
.then((page) =>
this.setData({
total: page.total || 0,
comments: (page.list || []).map((c) => ({
...c,
timeText: fmtAgo(c.created_at),
// 没有头像字段,用昵称首字做个色块,比一律显示同一个 emoji 强
initial: (c.author_name || '?').slice(0, 1),
imgs: Array.isArray(c.images) ? c.images : [],
replies: (c.replies || []).map((r) => ({ ...r, timeText: fmtAgo(r.created_at) })),
replies: (c.replies || []).map((r) => ({
...r,
timeText: fmtAgo(r.created_at),
initial: (r.author_name || '?').slice(0, 1),
})),
})),
}),
)
@@ -965,9 +973,20 @@ Component({
});
},
// 回复某条评论:输入区顶部显示「回复 xxx」,发送时带上 parent_id
// 小红书那套:点任意一条评论/回复,直接把输入框激活并填上「回复 @xxx」,
// 不用再去底部找输入框、也不用单独点一个「回复」按钮
onReplyTo(e) {
const { id, name } = e.currentTarget.dataset;
this.setData({ replyTo: { id, name } });
if (!id) return;
this.setData({ replyTo: { id, name }, cmFocus: true });
},
onCommentFocus(e) {
// adjust-position=false,键盘高度自己接管:弹层是 fixed 定位,
// 交给系统顶会把整块推出屏幕
this.setData({ kbHeight: (e.detail && e.detail.height) || 0 });
},
onCommentBlur() {
this.setData({ kbHeight: 0, cmFocus: false });
},
onCancelReply() {
this.setData({ replyTo: {} });
@@ -979,7 +998,11 @@ Component({
.listReplies(id)
.then((list) =>
this.setData({
[`comments[${index}].replies`]: (list || []).map((r) => ({ ...r, timeText: fmtAgo(r.created_at) })),
[`comments[${index}].replies`]: (list || []).map((r) => ({
...r,
timeText: fmtAgo(r.created_at),
initial: (r.author_name || '?').slice(0, 1),
})),
}),
)
.catch((err) => wx.showToast({ title: (err && err.message) || '加载失败', icon: 'none' }));