feat(V0.2): 标题30字+智能拆分 / 时间标记 MM-dd / 时间词前缀 (T-TK-14~16)
Docker Build and Push / build-image (push) Successful in 1m25s
Docker Build and Push / build-image (push) Successful in 1m25s
- 标题上限收紧至 30 字 (服务端校验+详情编辑框); 快速添加输入框 放宽到 200 字: 超长时在 30 字内分隔符处截断为标题, 完整原文进描述 - Task.markDate (DateTime, 迁移 20261025120000): 详情页 DatePicker 选择/清除, 列表行与看板卡显示 MM-dd 小标签 - 快速添加时间词前缀: 今天/明天/后天/本周/下周/本月/下月 开头 (后跟分隔符) 自动设为时间标记并移出标题; 本周=本周日, 本月=月末 - 需求/功能/数据库设计文档同步更新 T-TK-14~16 与 Task 表字段
This commit is contained in:
+19
-3
@@ -70,10 +70,11 @@ router.post('/projects/:id/tasks', async (req, res, next) => {
|
||||
const project = await loadProjectForUser(req.params.id, userId)
|
||||
if (!project) return res.status(404).json({ error: '项目不存在或无权限' })
|
||||
|
||||
const { title, description = '', parentId, assigneeId, memberIds = [], tags = [], kind } = req.body || {}
|
||||
const { title, description = '', parentId, assigneeId, memberIds = [], tags = [], kind, markDate } = req.body || {}
|
||||
const cleanTitle = String(title || '').trim()
|
||||
if (!cleanTitle) return res.status(400).json({ error: '任务标题不能为空' })
|
||||
if (cleanTitle.length > 200) return res.status(400).json({ error: '任务标题不能超过 200 字' })
|
||||
if (cleanTitle.length > 30) return res.status(400).json({ error: '任务标题不能超过 30 字' })
|
||||
const mark = parseMarkDate(markDate)
|
||||
|
||||
const columns = await loadColumns(project.id)
|
||||
if (!columns.length) return res.status(400).json({ error: '项目没有可用的看板列' })
|
||||
@@ -108,6 +109,7 @@ router.post('/projects/:id/tasks', async (req, res, next) => {
|
||||
title: cleanTitle,
|
||||
description: String(description || ''),
|
||||
kind: kind ? String(kind).trim().slice(0, 20) : null,
|
||||
markDate: mark,
|
||||
status: firstCol.boundStatus || 'todo',
|
||||
assigneeId: finalAssignee,
|
||||
creatorId: userId,
|
||||
@@ -158,6 +160,14 @@ router.get('/tasks/:id', async (req, res, next) => {
|
||||
} catch (e) { next(e) }
|
||||
})
|
||||
|
||||
// 时间标记: 接受 YYYY-MM-DD (或完整 ISO), 存 DateTime; 非法输入 400 由调用方处理
|
||||
function parseMarkDate(v) {
|
||||
if (v === null || v === undefined || v === '') return null
|
||||
const d = new Date(v)
|
||||
if (Number.isNaN(d.getTime())) return undefined // 非法
|
||||
return d
|
||||
}
|
||||
|
||||
// PUT /api/tasks/:id 关键: 列/状态联动裁决
|
||||
router.put('/tasks/:id', async (req, res, next) => {
|
||||
try {
|
||||
@@ -174,7 +184,7 @@ router.put('/tasks/:id', async (req, res, next) => {
|
||||
if (body.title !== undefined) {
|
||||
const t = String(body.title).trim()
|
||||
if (!t) return res.status(400).json({ error: '任务标题不能为空' })
|
||||
if (t.length > 200) return res.status(400).json({ error: '任务标题不能超过 200 字' })
|
||||
if (t.length > 30) return res.status(400).json({ error: '任务标题不能超过 30 字' })
|
||||
data.title = t
|
||||
}
|
||||
if (body.description !== undefined) data.description = String(body.description)
|
||||
@@ -183,6 +193,12 @@ router.put('/tasks/:id', async (req, res, next) => {
|
||||
const k = String(body.kind || '').trim().slice(0, 20)
|
||||
data.kind = k || null
|
||||
}
|
||||
// 时间标记: 完整日期存储
|
||||
if (body.markDate !== undefined) {
|
||||
const mark = parseMarkDate(body.markDate)
|
||||
if (mark === undefined) return res.status(400).json({ error: '时间标记格式非法' })
|
||||
data.markDate = mark
|
||||
}
|
||||
if (body.extras !== undefined) {
|
||||
if (!Array.isArray(body.extras)) return res.status(400).json({ error: '扩展信息格式非法' })
|
||||
data.extras = body.extras.map((e) => ({
|
||||
|
||||
Reference in New Issue
Block a user