diff --git a/assets/css/app.css b/assets/css/app.css
index a884e0f..408371c 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -7000,33 +7000,42 @@ h6.md-h { font-size: 17px; color: var(--muted); }
/* #78:评论里的「添加附件」。
用原生 file input(视觉隐藏),点 label 触发 —— 与站内其它上传入口同一手法。 */
+/* 🔴 #81(用户 2026-11-05):「评论里面拖文件上传附件,那个地方太小了。」
+
+ 原来是一个 `padding:4px 10px` 的小胶囊(高约 26px)+ 一行小字,
+ 拖动落点太小:触屏上不好瞄,桌面上也容易拖偏。
+ ⇒ 改用与「任务附件」同一套 `.upbox`(96px 高的虚线大框)。
+ 这里只做评论特有的微调,不去覆盖 `.upbox` 本体 ——
+ 那一套是站点级组件,改它会影响所有上传入口。 */
.cmt-attach-row {
- display: flex; align-items: center; gap: 8px;
- margin-top: 6px;
+ display: flex; flex-direction: column; gap: 8px;
+ margin-top: 8px;
}
-.cmt-attach-pick {
- display: inline-flex; align-items: center; gap: 5px;
- font-size: 12.5px; color: var(--muted); cursor: pointer;
- padding: 4px 10px; border-radius: 999px;
- border: 1px dashed var(--line); background: transparent;
+/* 评论里的上传框比「任务附件」矮一点:评论框本身已经在下方,
+ 96px 会把整块讨论区推得太长;72px 在触屏上仍然好点。 */
+.cmt-upbox { min-height: 72px; padding: 10px 14px; gap: 3px; }
+.cmt-upbox .upbox-ico { font-size: 18px; }
+.cmt-upbox .upbox-msg { font-size: 13px; }
+.cmt-upbox-sub {
+ font-size: 11.5px; color: var(--faint); margin-top: 1px;
}
-.cmt-attach-pick:hover { color: var(--brand); border-color: var(--brand); }
-.cmt-attach-name {
- font-size: 12px; color: var(--faint);
- max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
+/* 拖拽经过时整框高亮(`.is-over` 由 task_panel.js 加在 `.cmt-attach-row` 上,
+ 而 `.upbox.is-over` 是站点级规则 —— 这里补一条子选择器把它传导下去) */
+.cmt-attach-row.is-over .cmt-upbox {
+ border-color: var(--brand); border-style: solid;
+ background: #FDF3EC; color: var(--brand);
}
-/* #79:评论附件区支持拖拽 —— 高亮用虚线框 + 浅主题底色,和上面那块
- 「任务附件」的 is-over 观感一致(同一个交互不该长两个样子)。 */
-.cmt-attach-row {
- padding: 6px 8px; border-radius: 8px;
- border: 1px dashed transparent;
- transition: background-color .12s, border-color .12s;
+/* 已选文件列表:一行一个,前面带个圆点;超长名截断 */
+.cmt-file-list {
+ display: flex; flex-direction: column; gap: 3px;
+ padding: 6px 10px; border-radius: var(--radius-s);
+ background: var(--surface-2, #F2F0EC);
+ font-size: 12px; color: var(--muted);
}
-.cmt-attach-row.is-over {
- border-color: var(--brand);
- background: color-mix(in srgb, var(--brand) 8%, transparent);
+.cmt-file-list span {
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
-.cmt-attach-row.is-over .cmt-attach-pick { color: var(--brand); border-color: var(--brand); }
+.cmt-file-list span::before { content: '📎 '; }
.tk-sec-lb { flex: 0 0 34px; font-size: 12.5px; color: var(--faint); }
.tk-seg { display: flex; gap: 6px; flex-wrap: wrap; }
/* 面板里的状态徽标比卡片上的大一档(这里是要点的按钮,不是标签) */
diff --git a/assets/js/task_panel.js b/assets/js/task_panel.js
index 201d7aa..8ae2b7f 100644
--- a/assets/js/task_panel.js
+++ b/assets/js/task_panel.js
@@ -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 +=` 拼字符串 ——
+ * 文件名是用户可控的(`.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,否则浏览器不认这是落点(还会当"打开文件") */
diff --git a/templates/tasks/_panel.php b/templates/tasks/_panel.php
index 6ed7ef4..5bcb9a3 100644
--- a/templates/tasks/_panel.php
+++ b/templates/tasks/_panel.php
@@ -436,22 +436,29 @@ $tpLate = $tpDue !== '' && $tpStatus !== 'done' && \App\Task::isOverdue($tpD
⚠️ 用原生 `` 而不是拖拽框:评论是一次性提交,
拖拽框那套(`data-tk-drop-box`)是"点了立刻上传",两者语义不同。 */ ?>
`** ——
- `` 的 `files` 是只读的,得用 `DataTransfer` 重建
- 一个 FileList 赋回去。这样表单提交时浏览器才会真的带上文件,
- 否则只是"看起来加上了",提交是空的。 */ ?>
+ ⇒ 原来是一个 `padding:4px 10px` 的小胶囊 + 一行小字提示,
+ 拖动落点只有 ~26px 高,触屏上根本不好瞄,桌面上也经常拖偏。
+ ⇒ 改成与**「任务附件」那块完全一样**的 `.upbox`:
+ 高 96px、虚线大框、加号图标 + 说明文字。
+ 同一个交互在一个页面里不该长两个样子,也不该一个大一个小。
+
+ ⚠️ 这块的语义是"挂到评论表单、点发表才一起提交",
+ 而上面「任务附件」是"拖上去立刻上传" —— 外观一致但行为不同,
+ 所以说明文字要写清"随这条评论一起发出",避免用户以为已经传上去了。
+ ⚠️ `data-cmt-drop` 仍是拖拽落点标记(task_panel.js 认它),
+ 不要改成 `data-tk-drop-box`(那是立刻上传那条路)。 */ ?>