评论附件拖放区放大:小胶囊 → 整块虚线大框 (#81)

用户 2026-11-05:「评论里面拖文件上传附件,那个地方太小了。」

原来:`padding:4px 10px` 的小胶囊(约 26px 高)+ 一行小字提示,
拖动落点只有那一条窄带 —— 触屏不好瞄,桌面也容易拖偏。

改成与「任务附件」同一套 `.upbox`(那个是站点级上传组件的标准外观):
  · 高度 89px(触屏建议下限 44px,绰绰有余)
  · 宽度占满整行(桌面 840px / 手机 330px)
  · 虚线大框 + 加号图标 + 说明文字
  · 副标题写清「点『发表』时随这条评论一起发出」——
    与上面那块「拖上去立刻上传」语义不同,不写清楚用户会以为已经传了
  · 比「任务附件」矮一点(96 → 72 min-height,实测 89px):
    评论框已经在下面,96px 会把讨论区推太长;89px 仍然很好点

顺带改的:
  · 已选文件从"逗号拼一行"改成"一行一个,带 📎",长名截断
  · 渲染用 createElement + textContent,**不拼 innerHTML**
    (文件名是用户可控的,拼 innerHTML 就是 XSS)
  · 列表为空时 hidden,不挂空灰条
  · 加键盘支持:`.upbox` 带 role=button + tabindex,Enter/Space 能打开选择框

实测(Playwright,两种视口):
  桌面 900px:840 × 89px,边框 1px dashed,触屏可点性 ✅
  手机 390px:330 × 89px,触屏可点性 ✅
  文件列表:2 项,高 56px,正常显示
This commit is contained in:
2026-11-05 18:57:56 +08:00
parent 8e48d0b1b2
commit 33940fa730
3 changed files with 91 additions and 48 deletions
+43 -16
View File
@@ -576,17 +576,35 @@
try { input.files = dt.files; } catch (e) { /* 个别内核只读:下面提示照旧 */ }
/* 更新提示文字(用户要能确认"确实加上了哪几个") */
var nameEl = zone.querySelector('[data-cmt-file-name]');
if (nameEl) {
var names = [];
if (input.files) {
for (var k = 0; k < input.files.length; k++) { names.push(input.files[k].name); }
}
nameEl.textContent = names.length
? names.join('、')
: '可点击选择,也可把文件拖进来';
/* 更新已选文件列表(用户要能确认"确实加上了哪几个")。
#81:从"一行逗号拼接"改成"一行一个" —— 拖 5 个文件时
逗号拼起来读不清,也看不出到底几个。 */
cmtRenderNames(zone, input);
}
/**
* 把 `input.files` 渲染成一行一个的列表(#81)。
*
* ⚠️ 用 `textContent` 逐个 append,**不要** `innerHTML +=` 拼字符串 ——
* 文件名是用户可控的(`<img onerror=...>.jpg` 这种),拼进 innerHTML
* 就是 XSS。用 `document.createElement('span')` + `textContent` 才安全。
* ⚠️ 列表为空时 `hidden`,非空才显示 —— 否则评论区永远挂着一块空灰条。
*/
function cmtRenderNames(zone, input) {
var box = zone.querySelector('[data-cmt-file-name]');
if (!box) { return; }
while (box.firstChild) { box.removeChild(box.firstChild); }
var n = input && input.files ? input.files.length : 0;
if (!n) {
box.hidden = true;
return;
}
for (var i = 0; i < n; i++) {
var sp = document.createElement('span');
sp.textContent = input.files[i].name;
box.appendChild(sp);
}
box.hidden = false;
}
/* dragover:必须 preventDefault,否则浏览器把文件当"打开"处理(页面跳走) */
@@ -618,17 +636,26 @@
cmtAddFiles(zone, files);
});
/* 点选也要更新提示(原生 onchange) */
/* #81:新的上传框带 `role="button" tabindex="0"`(`.upbox` 的既有约定),
键盘 Enter/Space 也要能打开文件选择 —— 只做鼠标的话键盘用户点不动。 */
document.addEventListener('keydown', function (e) {
var t = e.target;
if (!t || !t.closest) { return; }
var zone = t.closest('[data-cmt-drop]');
if (!zone) { return; }
if (e.key !== 'Enter' && e.key !== ' ') { return; }
e.preventDefault();
var inp = zone.querySelector('[data-cmt-file-input]');
if (inp) { inp.click(); }
});
/* 点选也要更新列表(原生 onchange)—— 与拖拽共用同一个渲染函数 */
document.addEventListener('change', function (e) {
var t = e.target;
if (!t || !t.hasAttribute || !t.hasAttribute('data-cmt-file-input')) { return; }
var zone = t.closest ? t.closest('[data-cmt-drop]') : null;
if (!zone) { return; }
var nameEl = zone.querySelector('[data-cmt-file-name]');
if (!nameEl) { return; }
var names = [];
for (var i = 0; i < (t.files ? t.files.length : 0); i++) { names.push(t.files[i].name); }
nameEl.textContent = names.length ? names.join('、') : '可点击选择,也可把文件拖进来';
cmtRenderNames(zone, t);
});
/* 拖到框上要给 dragover preventDefault,否则浏览器不认这是落点(还会当"打开文件") */