Files
paper-doc/frontend/src/api/templates.ts
T
govin 4d749b3592 frontend: add the paper list, document view, and paragraph editor
The 论文 menu now carries the papers themselves, read from a pinia store, so a
paper opens straight from the rail; every page that changes the list reloads
that store, and a filter box appears once there are more than six.

PapersView is the library — search, create, edit, open, single and batch
delete. PaperDetailView is the writing surface: it renders the server's
document verbatim, with an edit button beside every paragraph (written or not,
because content only ever enters a paper through the editor), citation markers
numbered in reading order to match the 参考文献 list, and a 切换模板 dialog that
previews the impact before re-shaping the document.

The paragraph editor shows one paragraph as the paper will read it — every
sentence on its own line, in sort order, each editable, each able to carry
citations — and writes the paragraph back whole. Whitespace-only lines are
dropped on save; a citation with an empty 引用内容 is refused; and 所属段落
moves the paragraph to another position, appended after what is already there.
2026-09-18 17:29:16 +08:00

110 lines
3.0 KiB
TypeScript

import http from './client'
import type { BatchDeleteResult, PageQuery, PageResult } from './types'
/** One placement of a library field inside a template, with its typography. */
export interface TemplateField {
id: number
field_id: number
/** Display position. The only thing that decides render order. */
sort: number
name: string
level: number
font_size: number
font_color: string
}
/** A template as it appears in the list table. */
export interface TemplateListItem {
id: number
name: string
abstract: string | null
field_count: number
created_at: string
updated_at: string
}
/** A template with its outline, already ordered by `sort`. */
export interface TemplateDetail {
id: number
name: string
abstract: string | null
fields: TemplateField[]
created_at: string
updated_at: string
}
/** One entry of the selection sent on create/update. */
export interface TemplateFieldInput {
field_id: number
sort: number
}
export interface TemplatePayload {
name: string
abstract: string | null
fields: TemplateFieldInput[]
}
export async function listTemplates(
query: PageQuery = {},
): Promise<PageResult<TemplateListItem>> {
const params: Record<string, unknown> = {}
if (query.keyword) params.keyword = query.keyword
if (query.page) params.page = query.page
if (query.page_size) params.page_size = query.page_size
const { data } = await http.get<PageResult<TemplateListItem>>('/templates', { params })
return data
}
export async function getTemplate(id: number): Promise<TemplateDetail> {
const { data } = await http.get<TemplateDetail>(`/templates/${id}`)
return data
}
/** The API caps `page_size` at 200; a picker wants every template, so it loops. */
const TEMPLATE_PAGE_SIZE = 200
/**
* Fetch every template, following pagination.
*
* A template picker that showed only the first page would silently hide the
* template the user is looking for, and the paper view needs the whole list
* before it can offer a switch without a search round trip.
*/
export async function fetchAllTemplates(): Promise<TemplateListItem[]> {
const all: TemplateListItem[] = []
let page = 1
for (;;) {
const result = await listTemplates({ page, page_size: TEMPLATE_PAGE_SIZE })
all.push(...result.items)
if (page >= result.pages || result.items.length === 0) {
return all
}
page += 1
}
}
export async function createTemplate(payload: TemplatePayload): Promise<TemplateDetail> {
const { data } = await http.post<TemplateDetail>('/templates', payload)
return data
}
export async function updateTemplate(
id: number,
payload: Partial<TemplatePayload>,
): Promise<TemplateDetail> {
const { data } = await http.patch<TemplateDetail>(`/templates/${id}`, payload)
return data
}
export async function deleteTemplate(id: number): Promise<void> {
await http.delete(`/templates/${id}`)
}
export async function batchDeleteTemplates(ids: number[]): Promise<BatchDeleteResult> {
const { data } = await http.post<BatchDeleteResult>('/templates/batch-delete', { ids })
return data
}