194 lines
6.8 KiB
TypeScript
194 lines
6.8 KiB
TypeScript
import { useEffect, useState } from 'react'
|
||
import { Input, Modal, Select, 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
|
||
identifier: string
|
||
icon: string
|
||
background: string
|
||
description: string
|
||
}
|
||
|
||
export type TaskDraft = {
|
||
title: string
|
||
description: string
|
||
dueAt: string
|
||
tag: 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,
|
||
tagOptions,
|
||
}: {
|
||
activeModal: ProjectActionModal
|
||
loading: boolean
|
||
onClose: () => void
|
||
onCreateProject: (draft: ProjectDraft) => void
|
||
onCreateTask: (draft: TaskDraft) => void
|
||
onUploadSource: (draft: SourceDraft) => void
|
||
onCreateCronPlan: (draft: CronDraft) => void
|
||
tagOptions: string[]
|
||
}) {
|
||
const [project, setProject] = useState<ProjectDraft>({ name: '', identifier: '', icon: '', background: '', description: '' })
|
||
const [task, setTask] = useState<TaskDraft>({ title: '', description: '', dueAt: '', tag: '' })
|
||
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: '', identifier: '', icon: '', background: '', description: '' })
|
||
setTask({ title: '', description: '', dueAt: '', tag: '' })
|
||
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>
|
||
<Input placeholder="例如:A3 / EXP / DATA" value={project.identifier} onChange={(identifier) => setProject((draft) => ({ ...draft, identifier }))} />
|
||
</label>
|
||
<label>
|
||
<Text>项目图标</Text>
|
||
<Input placeholder="例如:A3 / 🚀 / compass" value={project.icon} onChange={(icon) => setProject((draft) => ({ ...draft, icon }))} />
|
||
</label>
|
||
<label>
|
||
<Text>项目背景</Text>
|
||
<Input placeholder="例如:#165DFF 或背景图片 URL" value={project.background} onChange={(background) => setProject((draft) => ({ ...draft, background }))} />
|
||
</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>
|
||
<label>
|
||
<Text>所属标签</Text>
|
||
<Select
|
||
allowCreate
|
||
placeholder="选择或输入标签"
|
||
value={task.tag}
|
||
onChange={(tag) => setTask((draft) => ({ ...draft, tag }))}
|
||
>
|
||
{tagOptions.map((tag) => (
|
||
<Select.Option key={tag} value={tag}>{tag}</Select.Option>
|
||
))}
|
||
</Select>
|
||
</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>
|
||
</>
|
||
)
|
||
}
|