163 lines
5.5 KiB
TypeScript
163 lines
5.5 KiB
TypeScript
|
|
import { useEffect, useState } from 'react'
|
|||
|
|
import { Input, Modal, Space, Switch, Typography } from '@arco-design/web-react'
|
|||
|
|
|
|||
|
|
const { Text } = Typography
|
|||
|
|
const { TextArea } = Input
|
|||
|
|
|
|||
|
|
export type ProjectActionModal = 'project' | 'task' | 'source' | 'cron' | null
|
|||
|
|
|
|||
|
|
export type ProjectDraft = {
|
|||
|
|
name: string
|
|||
|
|
description: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type TaskDraft = {
|
|||
|
|
title: string
|
|||
|
|
description: string
|
|||
|
|
dueAt: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type SourceDraft = {
|
|||
|
|
title: string
|
|||
|
|
file: File | null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export type CronDraft = {
|
|||
|
|
title: string
|
|||
|
|
schedule: string
|
|||
|
|
enabled: boolean
|
|||
|
|
nextRunAt: string
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function ProjectActionModals({
|
|||
|
|
activeModal,
|
|||
|
|
loading,
|
|||
|
|
onClose,
|
|||
|
|
onCreateProject,
|
|||
|
|
onCreateTask,
|
|||
|
|
onUploadSource,
|
|||
|
|
onCreateCronPlan,
|
|||
|
|
}: {
|
|||
|
|
activeModal: ProjectActionModal
|
|||
|
|
loading: boolean
|
|||
|
|
onClose: () => void
|
|||
|
|
onCreateProject: (draft: ProjectDraft) => void
|
|||
|
|
onCreateTask: (draft: TaskDraft) => void
|
|||
|
|
onUploadSource: (draft: SourceDraft) => void
|
|||
|
|
onCreateCronPlan: (draft: CronDraft) => void
|
|||
|
|
}) {
|
|||
|
|
const [project, setProject] = useState<ProjectDraft>({ name: '', description: '' })
|
|||
|
|
const [task, setTask] = useState<TaskDraft>({ title: '', description: '', dueAt: '' })
|
|||
|
|
const [source, setSource] = useState<SourceDraft>({ title: '', file: null })
|
|||
|
|
const [cron, setCron] = useState<CronDraft>({ title: '', schedule: '0 9 * * *', enabled: true, nextRunAt: '' })
|
|||
|
|
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (activeModal === null) {
|
|||
|
|
setProject({ name: '', description: '' })
|
|||
|
|
setTask({ title: '', description: '', dueAt: '' })
|
|||
|
|
setSource({ title: '', file: null })
|
|||
|
|
setCron({ title: '', schedule: '0 9 * * *', enabled: true, nextRunAt: '' })
|
|||
|
|
}
|
|||
|
|
}, [activeModal])
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<>
|
|||
|
|
<Modal
|
|||
|
|
className="action-modal"
|
|||
|
|
title="新建项目"
|
|||
|
|
visible={activeModal === 'project'}
|
|||
|
|
confirmLoading={loading}
|
|||
|
|
onCancel={onClose}
|
|||
|
|
onOk={() => onCreateProject(project)}
|
|||
|
|
>
|
|||
|
|
<Space direction="vertical" size={12} className="action-form">
|
|||
|
|
<label>
|
|||
|
|
<Text>项目名称</Text>
|
|||
|
|
<Input placeholder="例如:项目 A3" value={project.name} onChange={(name) => setProject((draft) => ({ ...draft, name }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>项目说明</Text>
|
|||
|
|
<TextArea rows={4} placeholder="项目目标、背景或协作说明" value={project.description} onChange={(description) => setProject((draft) => ({ ...draft, description }))} />
|
|||
|
|
</label>
|
|||
|
|
</Space>
|
|||
|
|
</Modal>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
className="action-modal"
|
|||
|
|
title="新建任务"
|
|||
|
|
visible={activeModal === 'task'}
|
|||
|
|
confirmLoading={loading}
|
|||
|
|
onCancel={onClose}
|
|||
|
|
onOk={() => onCreateTask(task)}
|
|||
|
|
>
|
|||
|
|
<Space direction="vertical" size={12} className="action-form">
|
|||
|
|
<label>
|
|||
|
|
<Text>任务标题</Text>
|
|||
|
|
<Input placeholder="要完成什么?" value={task.title} onChange={(title) => setTask((draft) => ({ ...draft, title }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>任务说明</Text>
|
|||
|
|
<TextArea rows={4} placeholder="补充背景、验收口径或下一步" value={task.description} onChange={(description) => setTask((draft) => ({ ...draft, description }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>截止时间</Text>
|
|||
|
|
<Input placeholder="2026-07-21T09:30:00Z,可留空" value={task.dueAt} onChange={(dueAt) => setTask((draft) => ({ ...draft, dueAt }))} />
|
|||
|
|
</label>
|
|||
|
|
</Space>
|
|||
|
|
</Modal>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
className="action-modal"
|
|||
|
|
title="上传文件"
|
|||
|
|
visible={activeModal === 'source'}
|
|||
|
|
confirmLoading={loading}
|
|||
|
|
onCancel={onClose}
|
|||
|
|
onOk={() => onUploadSource(source)}
|
|||
|
|
>
|
|||
|
|
<Space direction="vertical" size={12} className="action-form">
|
|||
|
|
<label>
|
|||
|
|
<Text>资料标题</Text>
|
|||
|
|
<Input placeholder="默认使用文件名" value={source.title} onChange={(title) => setSource((draft) => ({ ...draft, title }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>选择文件</Text>
|
|||
|
|
<input
|
|||
|
|
className="native-file-input"
|
|||
|
|
type="file"
|
|||
|
|
onChange={(event) => setSource((draft) => ({ ...draft, file: event.target.files?.[0] ?? null }))}
|
|||
|
|
/>
|
|||
|
|
</label>
|
|||
|
|
</Space>
|
|||
|
|
</Modal>
|
|||
|
|
|
|||
|
|
<Modal
|
|||
|
|
className="action-modal"
|
|||
|
|
title="新建计划任务"
|
|||
|
|
visible={activeModal === 'cron'}
|
|||
|
|
confirmLoading={loading}
|
|||
|
|
onCancel={onClose}
|
|||
|
|
onOk={() => onCreateCronPlan(cron)}
|
|||
|
|
>
|
|||
|
|
<Space direction="vertical" size={12} className="action-form">
|
|||
|
|
<label>
|
|||
|
|
<Text>计划名称</Text>
|
|||
|
|
<Input placeholder="例如:每日 Inbox 整理" value={cron.title} onChange={(title) => setCron((draft) => ({ ...draft, title }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>Cron 表达式</Text>
|
|||
|
|
<Input value={cron.schedule} onChange={(schedule) => setCron((draft) => ({ ...draft, schedule }))} />
|
|||
|
|
</label>
|
|||
|
|
<label>
|
|||
|
|
<Text>下次运行时间</Text>
|
|||
|
|
<Input placeholder="2026-07-22T08:00:00Z,可留空" value={cron.nextRunAt} onChange={(nextRunAt) => setCron((draft) => ({ ...draft, nextRunAt }))} />
|
|||
|
|
</label>
|
|||
|
|
<label className="action-switch">
|
|||
|
|
<Text>启用计划</Text>
|
|||
|
|
<Switch checked={cron.enabled} onChange={(enabled) => setCron((draft) => ({ ...draft, enabled }))} />
|
|||
|
|
</label>
|
|||
|
|
</Space>
|
|||
|
|
</Modal>
|
|||
|
|
</>
|
|||
|
|
)
|
|||
|
|
}
|