feat(resume): initialize (reset to defaults with confirmation) and reload actions

This commit is contained in:
root
2026-10-17 14:39:12 +08:00
parent 5f3482538f
commit a0dc671006
16 changed files with 119 additions and 66 deletions
+49 -2
View File
@@ -1,13 +1,13 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { ArrowDown, ArrowUp, ChevronRight, Download, GripVertical, PanelRightClose, PanelRightOpen, Plus, Printer, Trash2 } from 'lucide-react';
import { ArrowDown, ArrowUp, ChevronRight, Download, GripVertical, PanelRightClose, PanelRightOpen, Plus, Printer, RefreshCw, RotateCcw, Trash2 } from 'lucide-react';
import { footprintList, profileGet, profileUpsert } from '@/lib/api';
import type { FootprintRow, ProfileInfo, ResumeConfig, ResumeItemCfg, ResumeSectionCfg } from '@/lib/types';
import {
ACCENT_CHOICES, assembleSections, defaultResumeConfig, migrateResumeConfig,
RESUME_SECTION_DEFS, TEMPLATES, type ResumeTemplate,
} from '@/lib/resume';
import { Button, Card, CardBody, CardHeader, Field, Input, Select, Textarea, useToast } from '@/components/ui';
import { Button, Card, CardBody, CardHeader, Dialog, Field, Input, Select, Textarea, useToast } from '@/components/ui';
const INFO_KEY: Record<string, string> = {
姓名: 'name', 性别: 'gender', 出生年月: 'birth', 民族: 'nation', 籍贯: 'native',
@@ -154,6 +154,30 @@ export function ResumePage() {
const sectionTypes = (key: string) => RESUME_SECTION_DEFS.find(d => d.key === key)?.types?.join(' / ') ?? '固定板块';
// 初始化:配置重置为默认模板(含种子内容),清除全部个性化修改
const [initOpen, setInitOpen] = useState(false);
const resetResume = () => {
const fresh = defaultResumeConfig();
setConfig(fresh);
setOpenEditors(new Set());
void profileUpsert({ info: { ...profile, selfIntro }, resume: fresh })
.then(() => profileQ.refetch())
.then(() => { setInitOpen(false); toast('success', '简历已初始化为默认模板'); })
.catch(e => toast('error', e.message));
};
// 重新载入:从服务器拉最新记录与配置,丢弃本地未保存状态
const reloadAll = async () => {
try {
const [p] = await Promise.all([profileQ.refetch(), listQ.refetch()]);
const stored = p.data?.content;
if (stored) {
setConfig(migrateResumeConfig(stored.resume));
setSelfIntro((stored.info as ProfileInfo | undefined)?.selfIntro ?? '');
}
toast('success', '已重新载入最新数据');
} catch (e) { toast('error', (e as Error).message); }
};
const exportConfig = () => {
const blob = new Blob([JSON.stringify(cfg, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
@@ -182,6 +206,12 @@ export function ResumePage() {
{panelOpen ? <PanelRightClose size={13} /> : <PanelRightOpen size={13} />}
{panelOpen ? '收起设置' : '展开设置'}
</Button>
<Button variant="secondary" onClick={() => void reloadAll()}>
<RefreshCw size={13} /> 重新载入
</Button>
<Button variant="ghost" className="text-danger hover:bg-danger-subtle" onClick={() => setInitOpen(true)}>
<RotateCcw size={13} /> 初始化简历
</Button>
<Button variant="secondary" onClick={exportWord}><Download size={13} /> 导出 Word</Button>
<Button variant="primary" onClick={() => window.print()}><Printer size={13} /> 打印 / PDF</Button>
</div>
@@ -459,6 +489,23 @@ export function ResumePage() {
</div>
)}
</div>
<Dialog
open={initOpen}
onClose={() => setInitOpen(false)}
width={440}
title="初始化简历"
footer={<>
<Button variant="ghost" onClick={() => setInitOpen(false)}>取消</Button>
<Button variant="primary" className="!bg-danger !text-white" disabled={false} onClick={resetResume}>
<RotateCcw size={13} /> 确认初始化
</Button>
</>}>
<div className="text-[12.5px] leading-relaxed text-ink-2">
将清除全部个性化修改(板块调整、自定义条目、文字覆盖、样式设置),
恢复为默认模板与示例内容。
<div className="mt-2 text-ink-3">记录数据与个人信息不受影响,随时可重新修改。</div>
</div>
</Dialog>
</div>
);
}