MVP: 按 07 风格重写前端 + Go 后端落地(长文/短文、编辑器、后台管理)

This commit is contained in:
Sakurasan
2026-09-20 20:56:03 +08:00
parent edee708802
commit 4d8f2de3a4
96 changed files with 6005 additions and 2602 deletions
+70
View File
@@ -0,0 +1,70 @@
import { marked } from 'marked'
import DOMPurify from 'dompurify'
marked.setOptions({ breaks: true, gfm: true })
// 编辑器预览与前台渲染共用这一个入口:先 marked 渲染,再过 DOMPurify
export function renderMarkdown(text) {
return DOMPurify.sanitize(marked.parse(text || '', { async: false }))
}
// 编辑器按 `md.render(...)` 的用法调用
export const md = { render: renderMarkdown }
export function formatDate(iso) {
if (!iso) return ''
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return iso
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return `${y} 年 ${m} 月 ${day} 日`
}
export function formatDateShort(iso) {
if (!iso) return ''
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return iso
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(
d.getDate()
).padStart(2, '0')}`
}
// 时间线上的相对时间:三天内用「几小时前」,同年省略年份
export function relativeDate(iso) {
if (!iso) return ''
const d = new Date(iso)
if (Number.isNaN(d.getTime())) return iso
const diff = Date.now() - d.getTime()
const hour = 3600 * 1000
if (diff < hour) return `${Math.max(1, Math.floor(diff / 60000))} 分钟前`
if (diff < 24 * hour) return `${Math.floor(diff / hour)} 小时前`
if (diff < 3 * 24 * hour) return `${Math.floor(diff / (24 * hour))} 天前`
const sameYear = d.getFullYear() === new Date().getFullYear()
return sameYear
? `${d.getMonth() + 1} 月 ${d.getDate()} 日`
: `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`
}
export function stripTags(html) {
const div = document.createElement('div')
div.innerHTML = html || ''
return div.textContent || ''
}
// 短文在列表里没有标题,用正文首句当索引
export function displayTitle(post) {
if (post.kind === 'short') return ''
return post.title || '无题'
}
export function excerpt(post, limit = 60) {
if (post.summary) return post.summary
const text = stripTags(post.content_html || '')
return text.length > limit ? text.slice(0, limit) + '…' : text
}
export function minutesLabel(post) {
if (post.kind === 'short') return '短文'
return `${post.reading_minutes || 1} 分钟`
}