fix(dev-task): 体验优化 — 新建弹窗默认值+详情卡片分区+筛选分页+胶囊联动
- 新建弹窗:负责人默认当前用户,提示版本截止日期,超期需填写原因 - 详情抽屉:重写为卡片分区布局,展示关联需求,状态流转按钮醒目化 - 详情实时更新:改为从 store 实时读取而非 prop 快照 - 列表增加筛选:负责人/状态/阻塞/任务类型 + 分页(每页20条) - 工时展示:列表行显示 实际Xh / 预计Yh 格式 - 概览胶囊联动:dev 阶段进度由 DevTask 完成度驱动 - 概览统计卡片:替换 mock 为真实 DevTask 数据(待开发/开发中/阻塞中) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
import { X, AlertTriangle } from 'lucide-react';
|
||||
import { useDevTaskStore } from '@/stores/useDevTaskStore';
|
||||
import { useTaskCategoryStore } from '@/stores/useTaskCategoryStore';
|
||||
import { useRequirementStore } from '@/stores/useRequirementStore';
|
||||
@@ -12,11 +12,12 @@ import type { Priority } from '@/lib/derive';
|
||||
interface Props {
|
||||
versionId: string;
|
||||
requirementIds: string[];
|
||||
versionDeadline?: string;
|
||||
onClose: () => void;
|
||||
onCreated?: () => void;
|
||||
}
|
||||
|
||||
export function DevTaskCreateModal({ versionId, requirementIds, onClose, onCreated }: Props) {
|
||||
export function DevTaskCreateModal({ versionId, requirementIds, versionDeadline, onClose, onCreated }: Props) {
|
||||
const { createTask, tasks } = useDevTaskStore();
|
||||
const { categories } = useTaskCategoryStore();
|
||||
const { requirements } = useRequirementStore();
|
||||
@@ -35,16 +36,18 @@ export function DevTaskCreateModal({ versionId, requirementIds, onClose, onCreat
|
||||
const [title, setTitle] = useState('');
|
||||
const [requirementId, setRequirementId] = useState(versionReqs[0]?.id || '');
|
||||
const [categoryId, setCategoryId] = useState(categories[0]?.id || '');
|
||||
const [assigneeId, setAssigneeId] = useState('');
|
||||
const [assigneeId, setAssigneeId] = useState(user?.name || '');
|
||||
const [priority, setPriority] = useState<Priority>('P2');
|
||||
const [estimateHours, setEstimateHours] = useState<number>(8);
|
||||
const [dueDate, setDueDate] = useState('');
|
||||
const [predecessorIds, setPredecessorIds] = useState<string[]>([]);
|
||||
const [description, setDescription] = useState('');
|
||||
const [overdueReason, setOverdueReason] = useState('');
|
||||
|
||||
const selectedReq = versionReqs.find((r) => r.id === requirementId);
|
||||
const effectivePriority = selectedReq?.priority || priority;
|
||||
const canSubmit = title.trim() && requirementId && categoryId && assigneeId && estimateHours > 0;
|
||||
const isOverdue = !!(dueDate && versionDeadline && dueDate > versionDeadline);
|
||||
const canSubmit = title.trim() && requirementId && categoryId && assigneeId && estimateHours > 0 && (!isOverdue || overdueReason.trim());
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!canSubmit) return;
|
||||
@@ -65,6 +68,7 @@ export function DevTaskCreateModal({ versionId, requirementIds, onClose, onCreat
|
||||
blockedById: undefined,
|
||||
predecessorIds: predecessorIds.length > 0 ? predecessorIds : undefined,
|
||||
riskLevel: undefined,
|
||||
overdueReason: isOverdue ? overdueReason.trim() : undefined,
|
||||
createdBy: user?.name || '系统',
|
||||
});
|
||||
onCreated?.();
|
||||
@@ -118,8 +122,20 @@ export function DevTaskCreateModal({ versionId, requirementIds, onClose, onCreat
|
||||
<div>
|
||||
<label className="block text-[12px] text-[var(--ink-soft)] mb-1">截止日期</label>
|
||||
<input type="date" value={dueDate} onChange={(e) => setDueDate(e.target.value)} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none" />
|
||||
{versionDeadline && (
|
||||
<p className="text-[10px] text-[var(--ink-muted)] mt-0.5">版本截止:{versionDeadline}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isOverdue && (
|
||||
<div className="rounded-lg border border-orange-200 bg-orange-50 p-3 space-y-2">
|
||||
<div className="flex items-center gap-1.5 text-[11px] text-orange-700">
|
||||
<AlertTriangle className="h-3.5 w-3.5" />
|
||||
任务截止日期超出版本截止日期({versionDeadline}),请说明原因
|
||||
</div>
|
||||
<input value={overdueReason} onChange={(e) => setOverdueReason(e.target.value)} placeholder="超期原因(必填)" className="h-8 w-full rounded-md border border-orange-200 bg-white px-2 text-[12px] focus:border-orange-400 focus:outline-none" />
|
||||
</div>
|
||||
)}
|
||||
{versionTasks.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-[12px] text-[var(--ink-soft)] mb-1">前置任务</label>
|
||||
|
||||
Reference in New Issue
Block a user