- 任务详情: 移除底部「拷贝到其他项目 / 删除任务」按钮 (⋯ 菜单仍保留入口), 清理相关死代码 - 列表子任务: 改为与普通任务同构的两行布局 (标题 + 左侧状态点/状态文字, 字体略小) - 列表任务卡片第二行不再显示看板分组标签
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import StatusDot from './StatusDot.jsx'
|
||||
|
||||
const statusLabel = (s) => (s === 'done' ? '完成' : s === 'doing' ? '执行中' : '未开始')
|
||||
|
||||
// 列表视图的一行 (Todoist 风格, T-UI-01)
|
||||
// 点击行体 → 打开详情; 长按 400ms 后左右拖动换列 (bindDrag 来自 useRowDrag)
|
||||
// 子任务与普通任务同构: 标题一行 + 状态元信息一行 (字体略小), 不显示分组标签
|
||||
// 复选框 / ⋯ 是独立控件: pointerdown 不冒泡, 不发起拖动,
|
||||
// 避免长按行后抬手时合成的 click 落在控件上误触状态切换
|
||||
export default function TaskRow({
|
||||
@@ -27,18 +30,20 @@ export default function TaskRow({
|
||||
</div>
|
||||
|
||||
{indent ? (
|
||||
<>
|
||||
<div className="sub-main">
|
||||
<span className="sub-title">{task.title}</span>
|
||||
<StatusDot status={task.status} pill />
|
||||
</>
|
||||
<div className="task-meta sub">
|
||||
<StatusDot status={task.status} />
|
||||
<span>{statusLabel(task.status)}</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="task-main">
|
||||
<div className="task-title">{task.title}</div>
|
||||
<div className="task-meta">
|
||||
<StatusDot status={task.status} />
|
||||
<span>{task.status === 'done' ? '完成' : task.status === 'doing' ? '执行中' : '未开始'}</span>
|
||||
{task.column?.name && <span className="col">{task.column.name}</span>}
|
||||
<span>{statusLabel(task.status)}</span>
|
||||
<span className={`assignee${task.assignee ? '' : ' none'}`}>
|
||||
{task.assignee?.name || '未指派'}
|
||||
</span>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ActionSheet, Dialog, Popup, Toast } from 'antd-mobile'
|
||||
import { ActionSheet, Popup, Toast } from 'antd-mobile'
|
||||
import { taskApi } from '../../api/task.js'
|
||||
import { STATUS_OPTIONS, STATUS_LABEL } from './date.js'
|
||||
import UserPicker from '../../components/task/UserPicker.jsx'
|
||||
import StatusDot from '../../components/task/StatusDot.jsx'
|
||||
import CopyTaskPopup from '../../components/task/CopyTaskPopup.jsx'
|
||||
import useKeyboardInset from '../../hooks/useKeyboardInset.js'
|
||||
import useOverlayBack from '../../hooks/useOverlayBack.js'
|
||||
|
||||
@@ -25,7 +24,6 @@ export default function TaskDetail({ task, columns, members, tasks, onClose, onS
|
||||
const [newSub, setNewSub] = useState('')
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [picker, setPicker] = useState(null) // 'assignee' | 'members'
|
||||
const [copyOpen, setCopyOpen] = useState(false)
|
||||
const [parentPick, setParentPick] = useState(false) // 转为子任务: 选择父任务
|
||||
|
||||
// 键盘弹出插入量: 抬升弹层内容, 输入框不被遮挡
|
||||
@@ -132,25 +130,7 @@ export default function TaskDetail({ task, columns, members, tasks, onClose, onS
|
||||
}
|
||||
}
|
||||
|
||||
async function removeTask() {
|
||||
const ok = await Dialog.confirm({
|
||||
content: subs.length
|
||||
? `删除该任务?将一并删除 ${subs.length} 条子任务。`
|
||||
: '删除该任务?',
|
||||
confirmText: '删除',
|
||||
})
|
||||
if (!ok) return
|
||||
try {
|
||||
await taskApi.remove(task.id)
|
||||
Toast.show({ content: '已删除' })
|
||||
onSaved?.()
|
||||
} catch (e) {
|
||||
Toast.show({ content: e.message })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Popup
|
||||
return ( <Popup
|
||||
visible
|
||||
onMaskClick={onClose}
|
||||
position="bottom"
|
||||
@@ -278,10 +258,6 @@ export default function TaskDetail({ task, columns, members, tasks, onClose, onS
|
||||
</div>
|
||||
|
||||
<div className="btn-primary" onClick={save}>{saving ? '保存中…' : '保存'}</div>
|
||||
<div className="btn-ghost" onClick={() => setCopyOpen(true)}>拷贝到其他项目</div>
|
||||
<div className="btn-danger" onClick={removeTask}>
|
||||
删除任务{subs.length > 0 && <span className="del-hint">(将一并删除 {subs.length} 条子任务)</span>}
|
||||
</div>
|
||||
|
||||
<UserPicker
|
||||
visible={picker === 'assignee'}
|
||||
@@ -301,13 +277,6 @@ export default function TaskDetail({ task, columns, members, tasks, onClose, onS
|
||||
onConfirm={(ids) => setForm((p) => ({ ...p, memberIds: Array.isArray(ids) ? ids : [] }))}
|
||||
/>
|
||||
|
||||
<CopyTaskPopup
|
||||
task={task}
|
||||
visible={copyOpen}
|
||||
onClose={() => setCopyOpen(false)}
|
||||
onDone={onChanged}
|
||||
/>
|
||||
|
||||
{/* 转为子任务: 从项目内顶层任务中选父任务 */}
|
||||
<Popup
|
||||
visible={parentPick}
|
||||
|
||||
@@ -963,20 +963,21 @@ body.me-safe-top .app-safe-top { height: 0; }
|
||||
.task-title { font-size: 14px; display: flex; align-items: center; gap: 6px; word-break: break-word; }
|
||||
.task-row.done .task-title { color: #a0a0a0; text-decoration: line-through; }
|
||||
.task-meta { display: flex; align-items: center; gap: 7px; margin-top: 4px; font-size: 11px; color: #999; flex-wrap: wrap; }
|
||||
.task-meta .col { color: var(--color-primary); background: #f0f5ff; border-radius: 4px; padding: 0 5px; }
|
||||
.task-meta .assignee { font-size: 12px; font-weight: 600; color: var(--color-text); }
|
||||
.task-meta .assignee.none { color: #b8bcc4; font-weight: 400; }
|
||||
.task-meta .members { font-size: 10.5px; color: #a7adb6; }
|
||||
.task-right { display: flex; align-items: center; flex-shrink: 0; }
|
||||
.more { color: #c2c7cf; font-size: 16px; line-height: 1; padding: 0 2px; cursor: pointer; }
|
||||
.sub-row {
|
||||
display: flex; align-items: center; gap: 8px; background: #fff; border-radius: 8px;
|
||||
display: flex; align-items: flex-start; gap: 8px; background: #fff; border-radius: 8px;
|
||||
padding: 8px 12px 8px 30px; margin: -2px 12px 7px 26px; box-shadow: 0 1px 2px rgba(0,0,0,.03);
|
||||
position: relative; cursor: pointer;
|
||||
}
|
||||
.sub-row::before { content: ""; position: absolute; left: 14px; top: 0; bottom: 0; width: 1px; background: #e9ecf0; }
|
||||
.sub-row .box { width: 16px; height: 16px; }
|
||||
.sub-title { flex: 1; font-size: 13px; color: #444; }
|
||||
.sub-row .box { width: 16px; height: 16px; margin-top: 2px; }
|
||||
.sub-main { flex: 1; min-width: 0; }
|
||||
.sub-title { display: block; font-size: 13px; color: #444; }
|
||||
.sub-row .task-meta { font-size: 10.5px; margin-top: 2px; gap: 5px; }
|
||||
.sub-row.done .sub-title { color: #a8a8a8; text-decoration: line-through; }
|
||||
.collapse { font-size: 11px; color: var(--color-primary); padding: 2px 0 8px 30px; margin: 0 12px; cursor: pointer; }
|
||||
.sub-add-hint { font-size: 11px; color: #b8bcc4; padding: 0 0 8px 44px; cursor: pointer; }
|
||||
|
||||
Reference in New Issue
Block a user