166 lines
6.5 KiB
TypeScript
166 lines
6.5 KiB
TypeScript
import { useState } from 'react'
|
||
import { Button, Card, Descriptions, Input, Modal, Progress, Space, Tag, Typography } from '@arco-design/web-react'
|
||
import { IconArrowLeft, IconCalendar, IconCheckCircle, IconCheckCircleFill, IconClockCircle, IconPlus } from '@arco-design/web-react/icon'
|
||
import type { ProjectWorkspace, TaskItem } from './project-types'
|
||
|
||
const { Title, Text, Paragraph } = Typography
|
||
|
||
export function ProjectTasks({
|
||
activeWorkspace,
|
||
activeTaskID,
|
||
onOpenTask,
|
||
onCloseTask,
|
||
onCreateTask,
|
||
onCreateProjectTag,
|
||
}: {
|
||
activeWorkspace: ProjectWorkspace
|
||
activeTaskID: string | null
|
||
onOpenTask: (taskID: string) => void
|
||
onCloseTask: () => void
|
||
onSelectItem: (title: string) => void
|
||
onCreateTask: () => void
|
||
onCreateProjectTag: (name: string) => void
|
||
}) {
|
||
const { tasks, project, tags } = activeWorkspace
|
||
const activeTask = tasks.find((task) => task.id === activeTaskID)
|
||
const pendingTasks = tasks.filter((task) => !task.completed)
|
||
const completedTasks = tasks.filter((task) => task.completed)
|
||
const projectTags = tags.filter((tag) => tag !== 'all' && tag !== '全部')
|
||
const [tagModalOpen, setTagModalOpen] = useState(false)
|
||
const [tagName, setTagName] = useState('')
|
||
|
||
if (activeTask) {
|
||
return <TaskDetail activeWorkspace={activeWorkspace} task={activeTask} onBack={onCloseTask} />
|
||
}
|
||
|
||
function submitTag() {
|
||
const nextTag = tagName.trim()
|
||
if (!nextTag) return
|
||
onCreateProjectTag(nextTag)
|
||
setTagName('')
|
||
setTagModalOpen(false)
|
||
}
|
||
|
||
return (
|
||
<div className="project-channel-page project-tasks-page overview-page">
|
||
<div className="overview-head">
|
||
<div>
|
||
<Title heading={4}>工作计划</Title>
|
||
<Text type="secondary">{project.name} 的标签、未完成计划和完成记录。</Text>
|
||
</div>
|
||
<Button type="primary" icon={<IconPlus />} onClick={onCreateTask}>新建任务</Button>
|
||
</div>
|
||
|
||
<Card className="queue-section task-tag-section" bordered>
|
||
<div className="section-header">
|
||
<Title heading={6}>标签</Title>
|
||
<Button className="tag-create-button" size="mini" type="text" icon={<IconPlus />} onClick={() => setTagModalOpen(true)}>新建</Button>
|
||
</div>
|
||
<Space wrap>
|
||
<Tag color="arcoblue">全部</Tag>
|
||
{projectTags.map((tag) => (
|
||
<Tag key={tag} color="gray">{tag}</Tag>
|
||
))}
|
||
</Space>
|
||
</Card>
|
||
|
||
<Card className="queue-section" bordered>
|
||
<div className="section-header">
|
||
<Title heading={6}>未完成的计划</Title>
|
||
<Tag color="arcoblue">{pendingTasks.length}</Tag>
|
||
</div>
|
||
<div className="task-plan-card-grid">
|
||
{pendingTasks.map((task) => (
|
||
<button className="task-plan-card" key={task.id} onClick={() => onOpenTask(task.id)}>
|
||
<span className="task-plan-title"><IconCheckCircleFill />{task.title}</span>
|
||
<Paragraph ellipsis={{ rows: 2 }} type="secondary">{task.summary || '暂无正文'}</Paragraph>
|
||
<div className="task-plan-meta">
|
||
<Tag color="arcoblue">{task.tag || '未设置标签'}</Tag>
|
||
<span><IconCalendar /> 创建 {task.createdAt}</span>
|
||
</div>
|
||
</button>
|
||
))}
|
||
{pendingTasks.length === 0 && <Text type="secondary">当前没有未完成计划</Text>}
|
||
</div>
|
||
</Card>
|
||
|
||
<Card className="queue-section" bordered>
|
||
<div className="section-header">
|
||
<Title heading={6}>完成的计划</Title>
|
||
<Tag color="green">{completedTasks.length}</Tag>
|
||
</div>
|
||
{completedTasks.map((task) => (
|
||
<button className="completed-plan-row" key={task.id} onClick={() => onOpenTask(task.id)}>
|
||
<span className="completed-plan-main">
|
||
<Text className="completed-plan-title"><IconCheckCircle />{task.title}</Text>
|
||
<Text type="secondary" ellipsis={{ showTooltip: true }}>{task.summary || '暂无正文'}</Text>
|
||
</span>
|
||
<span>{task.createdAt}</span>
|
||
<span>{task.completedAt || '未知'}</span>
|
||
<span><IconClockCircle /> {task.duration || '未知'}</span>
|
||
</button>
|
||
))}
|
||
{completedTasks.length === 0 && <Text type="secondary">当前没有完成计划</Text>}
|
||
</Card>
|
||
|
||
<Modal
|
||
className="action-modal"
|
||
title="新建标签"
|
||
visible={tagModalOpen}
|
||
onCancel={() => {
|
||
setTagModalOpen(false)
|
||
setTagName('')
|
||
}}
|
||
onOk={submitTag}
|
||
>
|
||
<Space direction="vertical" size={12} className="action-form">
|
||
<label>
|
||
<Text>标签名称</Text>
|
||
<Input autoFocus placeholder="例如:设计、客户、重要" value={tagName} onChange={setTagName} onPressEnter={submitTag} />
|
||
</label>
|
||
</Space>
|
||
</Modal>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
function TaskDetail({ activeWorkspace, task, onBack }: { activeWorkspace: ProjectWorkspace; task: TaskItem; onBack: () => void }) {
|
||
const percent = Number.parseInt(task.progress, 10)
|
||
|
||
return (
|
||
<div className="project-channel-page project-task-detail-page overview-page">
|
||
<div className="overview-head">
|
||
<div>
|
||
<Button type="text" icon={<IconArrowLeft />} onClick={onBack}>返回任务列表</Button>
|
||
<Title heading={4}>{task.title}</Title>
|
||
<Text type="secondary">{activeWorkspace.project.name} 的任务详情</Text>
|
||
</div>
|
||
<Tag color={task.completed ? 'green' : 'arcoblue'}>{task.tag || '未设置标签'}</Tag>
|
||
</div>
|
||
|
||
<Card className="queue-section task-detail-card" bordered>
|
||
<Descriptions
|
||
colon=":"
|
||
column={2}
|
||
data={[
|
||
{ label: '所属项目', value: activeWorkspace.project.name },
|
||
{ label: '负责人', value: task.owner },
|
||
{ label: '创建时间', value: task.createdAt },
|
||
{ label: '完成状态', value: task.completed ? '已完成' : '未完成' },
|
||
{ label: '完成时间', value: task.completedAt || '未完成' },
|
||
{ label: '耗时', value: task.duration || '未完成' },
|
||
]}
|
||
/>
|
||
<div className="task-detail-progress">
|
||
<Text type="secondary">任务进度</Text>
|
||
<Progress percent={Number.isNaN(percent) ? 0 : percent} />
|
||
</div>
|
||
<div className="task-detail-summary">
|
||
<Title heading={6}>任务说明</Title>
|
||
<Text>{task.summary || '暂无任务说明'}</Text>
|
||
</div>
|
||
</Card>
|
||
</div>
|
||
)
|
||
}
|