feat(blog): 前端 posts API 封装 + 摘要/时长派生
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
edb975d818
commit
d25c65ef81
92
Client/src/api/posts.ts
Normal file
92
Client/src/api/posts.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// 博客文章接口封装。基址与 photos.ts 一致:生产留空走同源相对路径。
|
||||||
|
const API_BASE =
|
||||||
|
(import.meta.env.VITE_API_BASE as string | undefined) ?? 'http://localhost:8080'
|
||||||
|
|
||||||
|
export interface Post {
|
||||||
|
id: string
|
||||||
|
title: string
|
||||||
|
tag: string
|
||||||
|
body: string
|
||||||
|
createdAt: number
|
||||||
|
updatedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PostInput = { title: string; tag: string; body: string }
|
||||||
|
|
||||||
|
/** 保存失败的可区分原因,供 UI 给出对应提示 */
|
||||||
|
export type PostError = 'full' | 'too-large' | 'bad-input' | 'failed'
|
||||||
|
|
||||||
|
function authHeaders(token: string): Record<string, string> {
|
||||||
|
return { Authorization: `Bearer ${token}` }
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveError(status: number): PostError {
|
||||||
|
return status === 409
|
||||||
|
? 'full'
|
||||||
|
: status === 413
|
||||||
|
? 'too-large'
|
||||||
|
: status === 400
|
||||||
|
? 'bad-input'
|
||||||
|
: 'failed'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 列出当前用户全部文章(已按最新在前排序)与数量上限 */
|
||||||
|
export async function listPosts(token: string): Promise<{ items: Post[]; max: number }> {
|
||||||
|
const res = await fetch(`${API_BASE}/api/web/posts`, { headers: authHeaders(token) })
|
||||||
|
if (!res.ok) throw new Error(`list failed: ${res.status}`)
|
||||||
|
return (await res.json()) as { items: Post[]; max: number }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取单篇文章 */
|
||||||
|
export async function getPost(token: string, id: string): Promise<Post> {
|
||||||
|
const res = await fetch(`${API_BASE}/api/web/posts/${id}`, { headers: authHeaders(token) })
|
||||||
|
if (!res.ok) throw new Error(`get failed: ${res.status}`)
|
||||||
|
return (await res.json()) as Post
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新建文章,返回服务器分配的完整文章对象 */
|
||||||
|
export async function createPost(token: string, input: PostInput): Promise<Post> {
|
||||||
|
const res = await fetch(`${API_BASE}/api/web/posts`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...authHeaders(token), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(input),
|
||||||
|
})
|
||||||
|
if (res.ok) return (await res.json()) as Post
|
||||||
|
throw new Error(saveError(res.status))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新文章 */
|
||||||
|
export async function updatePost(token: string, id: string, input: PostInput): Promise<void> {
|
||||||
|
const res = await fetch(`${API_BASE}/api/web/posts/${id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { ...authHeaders(token), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(input),
|
||||||
|
})
|
||||||
|
if (!res.ok) throw new Error(saveError(res.status))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除文章(404 视为已删,不报错) */
|
||||||
|
export async function deletePost(token: string, id: string): Promise<void> {
|
||||||
|
const res = await fetch(`${API_BASE}/api/web/posts/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: authHeaders(token),
|
||||||
|
})
|
||||||
|
if (!res.ok && res.status !== 404) throw new Error(`delete failed: ${res.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 估算阅读时长(按 ~200 字/分钟,最少 1 分钟) */
|
||||||
|
export function readingTime(body: string): string {
|
||||||
|
const mins = Math.max(1, Math.round(body.trim().length / 200))
|
||||||
|
return `${mins} min`
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取正文前若干字做摘要,去掉常见 Markdown 标记 */
|
||||||
|
export function excerpt(body: string, max = 80): string {
|
||||||
|
const plain = body
|
||||||
|
.replace(/```[\s\S]*?```/g, ' ') // 代码块
|
||||||
|
.replace(/!?\[([^\]]*)\]\([^)]*\)/g, '$1') // 链接/图片 → 文本
|
||||||
|
.replace(/[#>*_`~]+/g, ' ') // 标记符号
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim()
|
||||||
|
return plain.length > max ? `${plain.slice(0, max)}…` : plain
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user