// 快速添加的标题解析: 时间词前缀 + 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 // 截断落在分隔符上: 分隔符留在标题末尾很难看, 去掉尾部连续分隔符/空白 // (\p{P} 覆盖中英文全部标点; 之前用 SEPARATOR.source 重组字符类有转义坑) const title = text.slice(0, cut).replace(/[\s\p{P}]+$/u, '').trim() return { title, 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 } }