feat(V0.2): 标题30字+智能拆分 / 时间标记 MM-dd / 时间词前缀 (T-TK-14~16)
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:
jif2.zhang
2026-10-25 11:47:44 +08:00
parent fd27242e4e
commit 74e1e7dd13
13 changed files with 303 additions and 16 deletions
+73
View File
@@ -0,0 +1,73 @@
// 快速添加的标题解析: 时间词前缀 + 30 字分隔符截断 (T-TK-14/16)
export const MAX_TITLE = 30
// 分隔符: 空格 / 中英文标点 / 斜杠等 —— 截断优先落在分隔符上, 不切词
const SEPARATOR = /[\s,,。;;、!!??::“”"'’‘()()\[\]【】\-_/\\|]/
/**
* 长文本拆分: ≤30 字原样; 超长时在 30 字内(且不少于 8 字)找最后一个分隔符截断,
* 前段为标题, **完整原文**作为描述 (整体进详情, 不丢内容)。
*/
export function splitTitle(raw) {
const text = String(raw || '').trim()
if (text.length <= MAX_TITLE) return { title: text, desc: '' }
let cut = -1
for (let i = MAX_TITLE; i >= 8; i--) {
if (SEPARATOR.test(text[i - 1])) { cut = i; break }
}
if (cut < 0) cut = MAX_TITLE
return { title: text.slice(0, cut).trim(), desc: text }
}
// 时间词 → 日期 (返回 YYYY-MM-DD):
// 今天/明天/后天 = 当日 +0/+1/+2
// 本周 = 本周日, 下周 = 下周日 (周一定义为一周开始)
// 本月 = 本月最后一天, 下月 = 下月最后一天
const toISO = (d) => {
const p = (n) => String(n).padStart(2, '0')
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`
}
export function dateFromWord(word, now = new Date()) {
const day = (n) => { const d = new Date(now); d.setDate(d.getDate() + n); return toISO(d) }
const endOfWeek = (offsetWeeks) => {
const d = new Date(now)
const dow = (d.getDay() + 6) % 7 // 周一=0
d.setDate(d.getDate() + offsetWeeks * 7 + (6 - dow))
return toISO(d)
}
const endOfMonth = (offsetMonths) => {
const d = new Date(now.getFullYear(), now.getMonth() + offsetMonths + 1, 0)
return toISO(d)
}
switch (word) {
case '今天': return day(0)
case '明天': return day(1)
case '后天': return day(2)
case '本周': return endOfWeek(0)
case '下周': return endOfWeek(1)
case '本月': return endOfMonth(0)
case '下月': return endOfMonth(1)
default: return null
}
}
// 匹配最长的时间词前缀 (后面跟分隔符或直接结束)
const TIME_WORDS = ['本周', '下周', '本月', '下月', '今天', '明天', '后天']
/**
* 解析时间词前缀: "明天 交周报" → { markDate: '2026-10-26', rest: '交周报' }
* 无匹配时 rest 为原文, markDate 为 null。
*/
export function parseTimePrefix(raw) {
const text = String(raw || '').trim()
for (const w of TIME_WORDS) {
if (text.startsWith(w)) {
const after = text.slice(w.length)
if (after === '' || SEPARATOR.test(after[0])) {
return { markDate: dateFromWord(w), rest: after.trim() }
}
}
}
return { markDate: null, rest: text }
}