123a5cce6d
Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
import type { CalcHistoryCreate, CalcHistoryEntry } from "@/lib/history/types";
|
||
|
||
async function parseApiError(res: Response): Promise<string> {
|
||
try {
|
||
const data = (await res.json()) as { error?: string };
|
||
return data.error ?? `请求失败 (${res.status})`;
|
||
} catch {
|
||
return `请求失败 (${res.status})`;
|
||
}
|
||
}
|
||
|
||
export async function loadHistory(): Promise<CalcHistoryEntry[]> {
|
||
const res = await fetch("/api/history", { cache: "no-store" });
|
||
if (!res.ok) {
|
||
throw new Error(await parseApiError(res));
|
||
}
|
||
const data = (await res.json()) as { items: CalcHistoryEntry[] };
|
||
return data.items ?? [];
|
||
}
|
||
|
||
export async function saveHistoryEntry(
|
||
entry: CalcHistoryCreate,
|
||
): Promise<CalcHistoryEntry> {
|
||
const res = await fetch("/api/history", {
|
||
method: "POST",
|
||
cache: "no-store",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(entry),
|
||
});
|
||
if (!res.ok) {
|
||
throw new Error(await parseApiError(res));
|
||
}
|
||
const data = (await res.json()) as { entry: CalcHistoryEntry };
|
||
return data.entry;
|
||
}
|
||
|
||
export async function deleteHistoryEntry(id: string): Promise<void> {
|
||
const res = await fetch(`/api/history/${encodeURIComponent(id)}`, {
|
||
method: "DELETE",
|
||
cache: "no-store",
|
||
});
|
||
if (!res.ok) {
|
||
throw new Error(await parseApiError(res));
|
||
}
|
||
}
|
||
|
||
export function downloadMarkdown(content: string, filename: string) {
|
||
const blob = new Blob([content], { type: "text/markdown;charset=utf-8" });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement("a");
|
||
a.href = url;
|
||
a.download = filename.endsWith(".md") ? filename : `${filename}.md`;
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
function formatBaziInputLines(entry: CalcHistoryEntry): string[] {
|
||
if (!entry.baziInput) {
|
||
return [];
|
||
}
|
||
const { baziInput } = entry;
|
||
const gender = baziInput.gender === "male" ? "男" : "女";
|
||
const hour = baziInput.unknownHour ? "时辰不详" : baziInput.time;
|
||
return [
|
||
`- 出生地域:${baziInput.birthPlaceName}`,
|
||
`- 阳历生日:${baziInput.date} ${hour}`,
|
||
`- 性别:${gender}`,
|
||
];
|
||
}
|
||
|
||
function formatBaziChartLines(entry: CalcHistoryEntry): string[] {
|
||
if (!entry.baziChart) {
|
||
return [];
|
||
}
|
||
const { baziChart } = entry;
|
||
const { pillars } = baziChart;
|
||
return [
|
||
`- 农历:${baziChart.lunarDate}`,
|
||
`- 四柱:${pillars.year.ganZhi} ${pillars.month.ganZhi} ${pillars.day.ganZhi} ${pillars.time.ganZhi}`,
|
||
`- 真太阳时:${baziChart.trueSolarTime}`,
|
||
];
|
||
}
|
||
|
||
export function buildHistoryMarkdown(entry: CalcHistoryEntry): string {
|
||
const lines = [
|
||
`# ${entry.title}`,
|
||
"",
|
||
`- 类型:${entry.mode}`,
|
||
`- 时间:${new Date(entry.createdAt).toLocaleString("zh-CN")}`,
|
||
`- 问事:${entry.question}`,
|
||
"",
|
||
...formatBaziInputLines(entry),
|
||
...formatBaziChartLines(entry),
|
||
...Object.entries(entry.meta)
|
||
.filter(([key]) => !["出生地域", "阳历生日", "农历", "性别"].includes(key))
|
||
.map(([k, v]) => `- ${k}:${v}`),
|
||
];
|
||
|
||
if (entry.hexagram) {
|
||
lines.push(
|
||
`- 卦象:${entry.hexagram.guaTitle}`,
|
||
`- 卦辞:${entry.hexagram.guaResult}`,
|
||
);
|
||
}
|
||
|
||
lines.push("", "---", "", entry.completion);
|
||
return lines.join("\n");
|
||
}
|