fix(assessment): clickable rules panel at top, print-layout word doc polish

- scoring rules panel moved above the sheet and auto-scrolls into view on toggle (was rendered at the very bottom, felt unresponsive)
- word export: unified single spec table with numbered section rows (one/two/three...), subtotal and total rows, editorial heading fonts
- standardized fonts and metrics: body SimSun 10.5pt, headings SimHei, numerals Times New Roman, line-height, A4 margins 72/78/66pt
- footer with PAGE/NUMPAGES fields via mso-element footer, Word opens in Print layout view
- evidence captions and rules annex restyled to match; www rebuilt
This commit is contained in:
root
2026-10-17 17:48:16 +08:00
parent 11cfeeb3a6
commit ba448946c9
19 changed files with 200 additions and 179 deletions
+154 -133
View File
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState, type ReactNode } from 'react';
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { ChevronLeft, ChevronRight, Download, FileText, Images, Printer, RotateCcw } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { attachmentList, attachmentThumb, footprintList, gradesOverview, profileGet, profileUpsert } from '@/lib/api';
@@ -38,8 +38,14 @@ function buildMhtml(html: string, images: { name: string; dataUri: string }[]):
return new Blob([parts.join('')], { type: 'multipart/related; type="text/html"; boundary="' + boundary + '"' });
}
const W_TD = 'border:0.75pt solid #4a4a4a;padding:4pt 7pt;font-size:10.5pt;vertical-align:middle';
const W_TH = W_TD + ';background:#e8eaf0;font-weight:bold;font-family:SimHei;letter-spacing:1pt';
const W_TD = 'border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:10.5pt;vertical-align:middle';
const W_TH = 'border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:10.5pt;font-family:"黑体",SimHei;letter-spacing:1pt;vertical-align:middle;background:#eef1f6';
const W_SEC = 'border:0.75pt solid #3a3a3a;padding:5pt 8pt;font-size:11pt;font-family:"黑体",SimHei;letter-spacing:1.5pt;background:#f5f6f9;text-align:left';
// Word 域代码(页码等):条件注释 + field-begin/separator/end,Word 打开时自动计算
const W_FIELD = (name: string, shown: string) =>
'<!--[if supportFields]><span style="mso-element:field-begin"></span>' + name +
'<span style="mso-element:field-separator"></span><![endif]-->' + shown +
'<!--[if supportFields]><span style="mso-element:field-end"></span><![endif]-->';
/** 评分细则参考(附在 Word 末尾,与页面「评分细则参考」同一数据源) */
function rulesAnnexHtml(r: ZcRules): string {
@@ -79,35 +85,44 @@ function rulesAnnexHtml(r: ZcRules): string {
);
}
/** 综合素质测评表:A4 纵向、黑体标题、表格化分项、附图栅格 + 细则参考附表 */
/** 综合素质测评表:A4 纵向、宋体正文/黑体标题、统一表格版式、页脚页码、附1 材料图、附2 细则 */
function zongceWordMhtml(termLabel: string, result: ZcResult, profile: Partial<ProfileInfo>, evidence: Evidence[], rules: ZcRules): Blob {
const f = (s: unknown) => esc(s);
const schoolLine = profile.school ? '<div style="text-align:center;font-size:10.5pt;letter-spacing:1pt;color:#333">' + f(profile.school) + '</div>' : '';
const schoolLine = profile.school ? '<div style="text-align:center;font-size:10.5pt;letter-spacing:2pt;color:#222">' + f(profile.school) + '</div>' : '';
const infoCell = (v: unknown, label: string) =>
'<td style="' + W_TH + ';width:14%">' + label + '</td><td style="' + W_TD + '">' + f(v || '') + '</td>';
'<td style="' + W_TH + ';width:16%;text-align:center">' + label + '</td><td style="' + W_TD + ';text-align:center">' + f(v || '') + '</td>';
// 分板块明细表
const body = result.sections.map(sec => {
// 统一分项表:各板块内嵌为整表的小节行 + 小计行,末尾总分行
const cnNum = ['一', '二', '三', '四', '五', '六', '七', '八'];
const rows: string[] = [];
result.sections.forEach((sec, si) => {
const subtotal = sec.key === 'rewards' ? sec.subtotal : Math.min(sec.subtotal, sec.cap);
const rows = sec.items.map(it =>
'<tr>' +
'<td style="' + W_TD + '">' + f(it.title) + (it.overridden ? '(已手动修正)' : '') + '</td>' +
'<td style="' + W_TD + ';color:#555;font-size:9.5pt">' + f(it.note || it.source) + '</td>' +
'<td style="' + W_TD + ';text-align:right;width:64pt" class="num">' + f(it.score) + '</td></tr>',
).join('');
return (
'<table style="border-collapse:collapse;width:100%;margin:6pt 0 0" border="1">' +
'<tr><td style="' + W_TH + ';width:34%">考评项目</td>' +
'<td style="' + W_TH + '">评分依据</td>' +
'<td style="' + W_TH + ';width:64pt;text-align:center">分值</td></tr>' +
rows +
'<tr><td colspan="2" style="' + W_TD + ';text-align:right;background:#f4f5f7"><b>' + f(sec.label) + '小计</b>(板块上限 ' + f(sec.cap) + ' 分)</td>' +
'<td style="' + W_TD + ';text-align:right;background:#f4f5f7"><b class="num">' + f(subtotal) + '</b></td></tr>' +
'</table>'
rows.push('<tr><td colspan="3" style="' + W_SEC + '">' + f(cnNum[si] ?? si + 1) + '、' + f(sec.label) + '(上限 ' + f(sec.cap) + ' 分)</td></tr>');
for (const it of sec.items) {
rows.push(
'<tr><td style="' + W_TD + '">' + f(it.title) + (it.overridden ? '<span style="color:#8a6d3b">(已手动修正)</span>' : '') + '</td>' +
'<td style="' + W_TD + ';color:#444;font-size:9.5pt;border-left-width:0.75pt">' + f(it.note || it.source) + '</td>' +
'<td style="' + W_TD + ';text-align:right;width:64pt" class="num">' + f(it.score) + '</td></tr>',
);
}
rows.push(
'<tr><td colspan="2" style="' + W_TD + ';text-align:right;background:#fafbfc">' + f(sec.label) + '小计</td>' +
'<td style="' + W_TD + ';text-align:right;background:#fafbfc" class="num"><b>' + f(subtotal) + '</b></td></tr>',
);
}).join('');
});
rows.push(
'<tr><td colspan="2" style="' + W_TD + ';text-align:center;font-family:"黑体",SimHei;font-size:12pt;letter-spacing:2pt;background:#eef1f6">综合素质测评总分</td>' +
'<td style="' + W_TD + ';text-align:right;background:#eef1f6" class="num"><b style="font-size:12pt">' + f(result.total) + '</b></td></tr>',
);
const specTable =
'<table style="border-collapse:collapse;width:100%;margin-top:10pt" border="1">' +
'<tr><td style="' + W_TH + ';width:34%;text-align:center">考评项目</td>' +
'<td style="' + W_TH + ';text-align:center">评分依据</td>' +
'<td style="' + W_TH + ';width:64pt;text-align:center">分值</td></tr>' +
rows.join('') +
'</table>';
// 附 1:证明材料——每条有附件的活动 / 竞赛记录成组,图片纵向排布
// 附1:证明材料——每条有附件的活动 / 竞赛记录成组,图片纵向排布并编图号
const images: { name: string; dataUri: string }[] = [];
let figNo = 0;
const grids = evidence.map(ev => {
@@ -115,21 +130,22 @@ function zongceWordMhtml(termLabel: string, result: ZcResult, profile: Partial<P
figNo++;
const name = 'ev' + ev.recordId + '_' + (images.length);
images.push({ name, dataUri: uri });
return '<div style="text-align:center;margin:2pt 0"><img src="' + name + '" style="width:196pt;border:0.75pt solid #8a8a8a" alt="附图"><br>' +
'<span style="font-size:9pt;color:#444">图 ' + figNo + ' ' + f(ev.title) + '</span></div>';
return '<div style="text-align:center;margin:3pt 0"><img src="' + name + '" style="width:200pt;border:0.75pt solid #5a5a5a" alt="附图"><br>' +
'<span style="font-size:9pt;color:#333;font-family:"宋体",SimSun">图 ' + figNo + ' ' + f(ev.title) + '</span></div>';
}).join('');
return '<tr><td style="' + W_TD + ';width:26%;vertical-align:top;background:#fafafa"><b style="font-size:10pt">' + f(ev.title) + '</b><br>' +
'<span style="font-size:9pt;color:#666">' + f(ev.meta) + '</span></td>' +
'<span style="font-size:9pt;color:#555">' + f(ev.meta) + '</span></td>' +
'<td style="' + W_TD + '">' + imgs + '</td></tr>';
}).join('');
const annex = grids
? '<div style="font-family:SimHei;font-size:12pt;margin:14pt 0 4pt">附 1:加分证明材料 — ' + f(evidence.length) + ' 项记录 · ' + f(figNo) + ' 张材料图</div>' +
? '<div style="font-family:"黑体",SimHei;font-size:12pt;margin:16pt 0 4pt;letter-spacing:1pt">附 1:加分证明材料</div>' +
'<div style="font-size:9.5pt;color:#555;margin:0 0 4pt">(' + f(evidence.length) + ' 项记录 · ' + f(figNo) + ' 张材料图)</div>' +
'<table style="border-collapse:collapse;width:100%" border="1">' + grids + '</table>'
: '';
// 信息栏 3×3:末格为电话号码
const info =
'<table style="border-collapse:collapse;width:100%;margin-top:10pt" border="1">' +
'<table style="border-collapse:collapse;width:100%;margin-top:12pt" border="1">' +
'<tr>' + infoCell(profile.studentId, '学 号') + infoCell(profile.name, '姓 名') + infoCell(profile.gender, '性 别') + '</tr>' +
'<tr>' + infoCell(profile.college, '学 院') + infoCell(profile.major, '专 业') + infoCell(profile.className, '班 级') + '</tr>' +
'<tr>' + infoCell(profile.political, '政治面貌') + infoCell(termLabel, '统计周期') + infoCell(profile.phone, '电话号码') + '</tr>' +
@@ -138,37 +154,38 @@ function zongceWordMhtml(termLabel: string, result: ZcResult, profile: Partial<P
const html =
'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word">' +
'<head><meta charset="utf-8"><title>学生综合素质测评表</title>' +
'<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:Zoom>100</w:Zoom><w:TrackChanges>0</w:TrackChanges></w:WordDocument></xml><![endif]-->' +
'<style>' +
'@page WordSection1 { size:595.3pt 841.9pt; margin:56.7pt 62pt 62pt; mso-footer: f1; }' +
'@page WordSection1 { size:595.3pt 841.9pt; margin:72pt 78pt 66pt; mso-footer: f1; }' +
'div.WordSection1 { page:WordSection1; }' +
'body { font-family:SimSun,serif; font-size:10.5pt; color:#1a1a1a; }' +
'h1.title { font-family:SimHei; font-size:16pt; text-align:center; letter-spacing:2pt; margin:0 0 4pt; }' +
'p.sub { text-align:center; font-size:10pt; color:#333; margin:0; }' +
'body { font-family:"宋体",SimSun,serif; font-size:10.5pt; color:#1a1a1a; line-height:1.5; }' +
'h1.title { font-family:"黑体",SimHei; font-size:18pt; text-align:center; letter-spacing:4pt; margin:2pt 0 6pt; font-weight:bold; }' +
'p.sub { text-align:center; font-size:10pt; color:#333; margin:0 0 2pt; }' +
'table { border-collapse:collapse; }' +
'.total td { background:#eef1f6; font-family:SimHei; font-size:12pt; }' +
'.num { font-family:Calibri,SimSun; }' +
'.num { font-family:"Times New Roman",SimSun; }' +
'</style></head>' +
'<body><div class="WordSection1">' +
schoolLine +
'<h1 class="title">学生综合素质测评表</h1>' +
'<p class="sub">测评学年:' + f(termLabel) + '</p>' +
info +
body +
'<table style="border-collapse:collapse;width:100%;margin-top:8pt" border="1"><tr class="total">' +
'<td style="' + W_TD + ';text-align:left;font-family:SimHei">综合素质测评总分</td>' +
'<td style="' + W_TD + ';text-align:right;width:64pt" class="num">' + f(result.total) + '</td></tr></table>' +
specTable +
annex +
rulesAnnexHtml(rules) +
'<table style="width:100%;border-collapse:collapse;margin-top:22pt;font-size:10.5pt"><tr>' +
'<table style="width:100%;border-collapse:collapse;margin-top:26pt;font-size:10.5pt"><tr>' +
'<td style="width:25%">学生签名:_____</td>' +
'<td style="width:25%">班长签名:_____</td>' +
'<td style="width:25%">辅导员签名:_____</td>' +
'<td>日期:______</td></tr></table>' +
'<p style="font-size:9pt;color:#555;margin-top:10pt">说明:本表依据《学生综合素质测评评分细则》自动生成(细则参考默认值见附 2,可在综合测评页自行调整);带「已手动修正」标记的分值为手动核定结果。导出前请核对加分记录与证明材料一致。</p>' +
'<p style="font-size:9pt;color:#555;margin-top:12pt">说明:本表依据《学生综合素质测评评分细则》自动生成(细则默认值见附 2,可自行调整);带「已手动修正」标记的分值为手动核定结果。导出前请核对加分记录与证明材料一致。</p>' +
'<div style="mso-element:footer" id="f1">' +
'<p style="text-align:center;font-size:9pt;font-family:"宋体",SimSun;margin:0">第 ' + W_FIELD('PAGE', '1') + ' 页(共 ' + W_FIELD('NUMPAGES', '1') + ' 页)</p>' +
'</div>' +
'</div></body></html>';
return buildMhtml(html, images);
}
// ---------------- 评分细则参考编辑 UI ----------------
const NumCell = ({ value, onChange, wide }: { value: number; onChange: (v: number) => void; wide?: boolean }) => (
<input type="number" step="0.05"
@@ -197,6 +214,7 @@ export function AssessmentPage() {
const [evidence, setEvidence] = useState<Evidence[]>([]);
const [initOpen, setInitOpen] = useState(false);
const [rulesOpen, setRulesOpen] = useState(false);
const rulesRef = useRef<HTMLDivElement>(null);
const [rules, setRules] = useState<ZcRules>(ZC_DEFAULT_RULES);
const [zoom, setZoom] = useState<{ src: string; title: string; index: number } | null>(null);
@@ -206,14 +224,14 @@ export function AssessmentPage() {
if (stored.zongce?.rules) setRules(stored.zongce.rules);
}, [stored]);
const records: FootprintRow[] = listQ.data?.items ?? [];
const termYears = useMemo(() => Array.from(new Set((gradesQ.data?.semesters ?? []).map(x => termYearOf(x.term)).filter(Boolean) as string[])).sort(), [gradesQ.data]);
useEffect(() => {
if (!stored) return;
setOvMap(stored.zongce?.overrides ?? {});
const years = Array.from(new Set((gradesQ.data?.semesters ?? []).map(x => termYearOf(x.term)).filter(Boolean))) as string[];
if (!year && years.length) setYear(years[0]);
if (!year && termYears.length) setYear(termYears[0]);
}, [stored, gradesQ.data]);
const records: FootprintRow[] = listQ.data?.items ?? [];
const ov: ZcOverrides = ovMap[year] ?? {};
const result = useMemo(
() => computeZongce({
@@ -226,9 +244,8 @@ export function AssessmentPage() {
);
// 学年 → 大几:优先用个人信息「入学时间」,缺省时以最早学期推算
const years = useMemo(() => Array.from(new Set((gradesQ.data?.semesters ?? []).map(x => termYearOf(x.term)).filter(Boolean) as string[])).sort(), [gradesQ.data]);
const eduStartEff = profile.eduStart || (years.length ? years[0] + '-09' : '');
const termLabel = year === 'all' || !year ? '全部学年' : gradeLabel(year, eduStartEff);
const eduStartEff = profile.eduStart || (termYears.length ? termYears[0] + '-09' : '');
const termLabel = year === 'all' || !year ? '全部学年' : gradeLabel(year, eduStartEff);
// 本学年范围内全部记录(证据画廊不再只看计分记录——有附件就展示)
const scopeIds = useMemo(() => {
@@ -326,14 +343,101 @@ export function AssessmentPage() {
</div>
<div className="flex flex-wrap items-center gap-2">
<Select className="w-[190px]" value={year} onChange={e => setYear(e.target.value)}
options={[...years.map(y => ({ value: y, label: gradeLabel(y, eduStartEff) })), { value: 'all', label: '全部学年' }]} />
<Button variant="secondary" onClick={() => setRulesOpen(v => !v)}><FileText size={13} /> 评分细则参考</Button>
options={[...termYears.map(y => ({ value: y, label: gradeLabel(y, eduStartEff) })), { value: 'all', label: '全部学年' }]} />
<Button variant="secondary" onClick={() => {
const next = !rulesOpen;
setRulesOpen(next);
if (next) setTimeout(() => rulesRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 80);
}}><FileText size={13} /> 评分细则参考</Button>
<Button variant="secondary" onClick={exportWord}><Download size={13} /> 导出 Word</Button>
<Button variant="secondary" onClick={() => setInitOpen(true)}><RotateCcw size={13} /> 初始化</Button>
<Button variant="primary" onClick={() => window.print()}><Printer size={13} /> 打印</Button>
</div>
</div>
{/* 评分细则参考:可修改,随档案保存;导出 Word 附同款参考表。置于页面顶部,点击按钮立即可见 */}
{rulesOpen && (
<div ref={rulesRef} className="no-print mb-4 scroll-mt-16 rounded-xl border border-line bg-white p-4 shadow-card">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileText size={15} className="text-neutral-500" />
<span className="text-[13.5px] font-bold text-neutral-900">评分细则参考</span>
<span className="text-[11.5px] text-neutral-400">默认值可自行调整,调整后即时生效并随档案保存</span>
</div>
<Button variant="ghost" onClick={resetRules}><RotateCcw size={13} /> 恢复默认</Button>
</div>
<div className="mt-3 grid gap-4 md:grid-cols-2">
<div>
<div className="mb-1.5 text-[12px] font-semibold text-neutral-600">基础分值</div>
<MiniTable head={['项目', '分值']}>
{Object.entries(rules.politics).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.politics as Record<string, number>)[k] = v2; })} /></td></tr>
))}
<tr><td className="border-b border-neutral-100 px-2 py-1">操行考评默认分</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.conductDefault} onChange={v2 => editRule(r => { r.conductDefault = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">诚信分默认</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.integrityDefault} onChange={v2 => editRule(r => { r.integrityDefault = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">学业成绩(绩点 ×)</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.gpaFactor} onChange={v2 => editRule(r => { r.gpaFactor = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">体育加分(成绩 ×)</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.sportsFactor} onChange={v2 => editRule(r => { r.sportsFactor = v2; })} /></td></tr>
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">公益服务</div>
<MiniTable head={['项目', '分值']}>
{Object.entries(rules.publicGood).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.publicGood as Record<string, number>)[k] = v2; })} /></td></tr>
))}
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">违纪扣分</div>
<MiniTable head={['项目', '扣分']}>
{Object.entries(rules.penalties).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.penalties as Record<string, number>)[k] = v2; })} /></td></tr>
))}
</MiniTable>
</div>
<div>
<div className="mb-1.5 text-[12px] font-semibold text-neutral-600">竞赛 / 文体奖项加分(级别 × 奖项)</div>
<table className="w-full border border-neutral-200 text-[11.5px]">
<thead><tr><th className="border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500">级别</th>
{['一等奖', '二等奖', '三等奖', '优秀奖'].map(g => <th key={g} className="border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500">{g}</th>)}</tr></thead>
<tbody>
{Object.entries(rules.table2).map(([lv, grades2]) => (
<tr key={lv}><td className="border-b border-neutral-100 px-2 py-1 font-medium text-neutral-700">{lv}</td>
{(['一等奖', '二等奖', '三等奖', '优秀奖'] as const).map(g => (
<td key={g} className="border-b border-neutral-100 px-1 py-1">
<NumCell value={(grades2 as Record<string, number>)[g] ?? 0} onChange={v2 => editRule(r => { ((r.table2 as unknown as Record<string, Record<string, number>>)[lv])[g] = v2; })} />
</td>
))}
</tr>
))}
</tbody>
</table>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">学生工作</div>
<MiniTable head={['级别 / 岗位', '分值']}>
{Object.entries(rules.table4).flatMap(([lv, jobs]) =>
Object.entries(jobs as Record<string, number>).map(([job, v]) => (
<tr key={lv + job}><td className="border-b border-neutral-100 px-2 py-1">{lv} · {job}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { ((r.table4 as unknown as Record<string, Record<string, number>>)[lv])[job] = v2; })} /></td></tr>
)),
)}
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">荣誉与证照加分</div>
<MiniTable head={['类别 / 等级', '分值']}>
{Object.entries(rules.rewards).flatMap(([cat, vals]) =>
Object.entries(vals as Record<string, number>).map(([k, v]) => (
<tr key={cat + k}><td className="border-b border-neutral-100 px-2 py-1">{cat} · {k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { ((r.rewards as unknown as Record<string, Record<string, number>>)[cat])[k] = v2; }) } /></td></tr>
)),
)}
</MiniTable>
</div>
</div>
</div>
)}
<div className="print-sheet mx-auto w-full max-w-[820px] rounded-xl border border-line bg-white shadow-card sm:p-8">
{profile.school && <div className="text-center text-[11px] tracking-widest text-neutral-500">{profile.school}</div>}
<h1 className="mt-0.5 text-center text-[19px] font-bold tracking-wide text-neutral-900">学生综合素质测评表</h1>
@@ -431,89 +535,6 @@ export function AssessmentPage() {
</div>
)}
{/* 评分细则参考:可修改,随档案保存;导出 Word 会附同款参考表 */}
{rulesOpen && (
<div className="no-print mt-7 rounded-xl border border-line bg-white p-4 shadow-card">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<FileText size={15} className="text-neutral-500" />
<span className="text-[13.5px] font-bold text-neutral-900">评分细则参考</span>
<span className="text-[11.5px] text-neutral-400">默认值可自行调整,调整后即时生效并随档案保存</span>
</div>
<Button variant="ghost" onClick={resetRules}><RotateCcw size={13} /> 恢复默认</Button>
</div>
<div className="mt-3 grid gap-4 md:grid-cols-2">
<div>
<div className="mb-1.5 text-[12px] font-semibold text-neutral-600">基础分值</div>
<MiniTable head={['项目', '分值']}>
{Object.entries(rules.politics).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.politics as Record<string, number>)[k] = v2; })} /></td></tr>
))}
<tr><td className="border-b border-neutral-100 px-2 py-1">操行考评默认分</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.conductDefault} onChange={v2 => editRule(r => { r.conductDefault = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">诚信分默认</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.integrityDefault} onChange={v2 => editRule(r => { r.integrityDefault = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">学业成绩(绩点 ×)</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.gpaFactor} onChange={v2 => editRule(r => { r.gpaFactor = v2; })} /></td></tr>
<tr><td className="border-b border-neutral-100 px-2 py-1">体育加分(成绩 ×)</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={rules.sportsFactor} onChange={v2 => editRule(r => { r.sportsFactor = v2; })} /></td></tr>
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">公益服务</div>
<MiniTable head={['项目', '分值']}>
{Object.entries(rules.publicGood).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.publicGood as Record<string, number>)[k] = v2; })} /></td></tr>
))}
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">违纪扣分</div>
<MiniTable head={['项目', '扣分']}>
{Object.entries(rules.penalties).map(([k, v]) => (
<tr key={k}><td className="border-b border-neutral-100 px-2 py-1">{k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { (r.penalties as Record<string, number>)[k] = v2; })} /></td></tr>
))}
</MiniTable>
</div>
<div>
<div className="mb-1.5 text-[12px] font-semibold text-neutral-600">竞赛 / 文体奖项加分(级别 × 奖项)</div>
<table className="w-full border border-neutral-200 text-[11.5px]">
<thead><tr><th className="border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500">级别</th>
{['一等奖', '二等奖', '三等奖', '优秀奖'].map(g => <th key={g} className="border-b border-neutral-200 bg-neutral-50 px-2 py-1 text-left font-medium text-neutral-500">{g}</th>)}</tr></thead>
<tbody>
{Object.entries(rules.table2).map(([lv, grades2]) => (
<tr key={lv}><td className="border-b border-neutral-100 px-2 py-1 font-medium text-neutral-700">{lv}</td>
{(['一等奖', '二等奖', '三等奖', '优秀奖'] as const).map(g => (
<td key={g} className="border-b border-neutral-100 px-1 py-1">
<NumCell value={(grades2 as Record<string, number>)[g] ?? 0} onChange={v2 => editRule(r => { ((r.table2 as unknown as Record<string, Record<string, number>>)[lv])[g] = v2; })} />
</td>
))}
</tr>
))}
</tbody>
</table>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">学生工作</div>
<MiniTable head={['级别 / 岗位', '分值']}>
{Object.entries(rules.table4).flatMap(([lv, jobs]) =>
Object.entries(jobs as Record<string, number>).map(([job, v]) => (
<tr key={lv + job}><td className="border-b border-neutral-100 px-2 py-1">{lv} · {job}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { ((r.table4 as unknown as Record<string, Record<string, number>>)[lv])[job] = v2; })} /></td></tr>
)),
)}
</MiniTable>
<div className="mb-1.5 mt-3 text-[12px] font-semibold text-neutral-600">荣誉与证照加分</div>
<MiniTable head={['类别 / 等级', '分值']}>
{Object.entries(rules.rewards).flatMap(([cat, vals]) =>
Object.entries(vals as Record<string, number>).map(([k, v]) => (
<tr key={cat + k}><td className="border-b border-neutral-100 px-2 py-1">{cat} · {k}</td>
<td className="border-b border-neutral-100 px-2 py-1"><NumCell value={v} onChange={v2 => editRule(r => { ((r.rewards as unknown as Record<string, Record<string, number>>)[cat])[k] = v2; }) } /></td></tr>
)),
)}
</MiniTable>
</div>
</div>
</div>
)}
<div className="mt-6 border-t border-neutral-200 pt-3 text-[11px] leading-relaxed text-neutral-400">
计分依据:《学生综合素质测评评分细则》(默认值可在上方「评分细则参考」查看与修改)。
自动匹配条目均可手动修正分值;「初始化」清除本学年全部手动修正。导出 Word / 打印前请核对加分记录与证明材料一致。
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{r as g,f as v,j as t,S as j,t as S}from"./index-BhG5FvHJ.js";import{C as c,a as d,b as x}from"./card-BtFEK_ax.js";import{u as N,i as u,a as m}from"./useChart-BfaP0epD.js";import{u as k,i as w,a as C,b as L,c as R,d as $,e as z,f as T}from"./echarts-BIWefrIc.js";k([w,C,L,R,$,z,T]);function q(){const[l,n]=g.useState(""),i=N(),e=v({queryKey:["stats",l||void 0],queryFn:()=>S(l?Number(l):void 0)}).data,p=u("2"),r=u("3"),h=m(()=>({grid:{left:34,right:12,top:14,bottom:24},tooltip:{trigger:"axis"},xAxis:{type:"category",data:(e==null?void 0:e.monthly.map(a=>a.ym.slice(2)))??[],axisLine:{lineStyle:{color:r}},axisTick:{show:!1},axisLabel:{color:r,fontSize:10}},yAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},series:[{type:"bar",data:(e==null?void 0:e.monthly.map(a=>a.count))??[],barMaxWidth:18,itemStyle:{color:"#3056d3",borderRadius:[3,3,0,0]}}]}),[e,i]),b=m(()=>{const a=((e==null?void 0:e.by_type)??[]).filter(s=>s.count>0);return{tooltip:{trigger:"item"},legend:{orient:"vertical",right:4,top:"middle",textStyle:{color:p,fontSize:11},itemWidth:10,itemHeight:10},series:[{type:"pie",radius:["48%","72%"],center:["34%","50%"],label:{show:!1},data:a.map(s=>({name:s.label,value:s.count,itemStyle:{color:s.color||"#64748b"}}))}]}},[e,i]),y=m(()=>{const a=((e==null?void 0:e.by_level)??[]).filter(s=>s.count>0).slice().reverse();return{grid:{left:64,right:24,top:8,bottom:22},tooltip:{trigger:"axis"},xAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},yAxis:{type:"category",data:a.map(s=>s.label),axisLine:{show:!1},axisTick:{show:!1},axisLabel:{color:p,fontSize:11}},series:[{type:"bar",data:a.map(s=>({value:s.count,itemStyle:{color:s.color||"#3056d3",borderRadius:[0,3,3,0]}})),barMaxWidth:14}]}},[e,i]),f=((e==null?void 0:e.metrics)??[]).slice(0,3);return t.jsxs("div",{className:"mx-auto max-w-[1080px] px-3 py-4 sm:px-6 sm:py-5",children:[t.jsxs("div",{className:"mb-4 flex flex-wrap items-center justify-between gap-3",children:[t.jsxs("div",{className:"flex flex-wrap gap-3",children:[t.jsx(o,{label:"总记录",value:(e==null?void 0:e.total)??0}),t.jsx(o,{label:l?l+" 年新增":"本年新增",value:(e==null?void 0:e.total_this_year)??0}),t.jsx(o,{label:"附件总数",value:(e==null?void 0:e.attachment_total)??0}),f.map(a=>t.jsx(o,{label:a.label,value:a.value,unit:a.unit??""},a.key))]}),t.jsx(j,{className:"w-[120px]",value:l,onChange:a=>n(a.target.value),options:[{value:"",label:"全部年份"},...((e==null?void 0:e.years)??[]).map(a=>({value:String(a),label:a+" 年"}))]})]}),!(e!=null&&e.metrics.length)&&t.jsx("div",{className:"mb-4 rounded-md border border-dashed border-line px-4 py-2 text-[12px] text-ink-3",children:"提示:在「统计」配置中注册指标(如志愿时长、读书本数),带单位的数值字段即可在此量化展示。"}),t.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[t.jsxs(c,{children:[t.jsx(d,{title:"近 12 个月活跃度"}),t.jsx(x,{children:t.jsx("div",{ref:h,className:"h-[220px]"})})]}),t.jsxs(c,{children:[t.jsx(d,{title:"类型分布"}),t.jsx(x,{children:t.jsx("div",{ref:b,className:"h-[220px]"})})]}),t.jsxs(c,{className:"lg:col-span-2",children:[t.jsx(d,{title:"级别分布"}),t.jsx(x,{children:t.jsx("div",{ref:y,className:"h-[200px]"})})]})]})]})}function o({label:l,value:n,unit:i}){return t.jsxs("div",{className:"min-w-[120px] rounded-lg border border-line bg-surface px-4 py-3 shadow-card",children:[t.jsx("div",{className:"text-[12px] text-ink-3",children:l}),t.jsxs("div",{className:"num mt-0.5 text-[22px] font-semibold leading-tight text-ink-1",children:[n,i&&t.jsx("span",{className:"ml-1 text-[12px] font-normal text-ink-3",children:i})]})]})}export{q as DashboardPage};
import{r as g,f as v,j as t,S as j,t as S}from"./index-Bmyzyg8t.js";import{C as c,a as d,b as x}from"./card-Diwu5WOM.js";import{u as N,i as u,a as m}from"./useChart-rSzOuKiQ.js";import{u as k,i as w,a as C,b as L,c as R,d as $,e as z,f as T}from"./echarts-BIWefrIc.js";k([w,C,L,R,$,z,T]);function q(){const[l,n]=g.useState(""),i=N(),e=v({queryKey:["stats",l||void 0],queryFn:()=>S(l?Number(l):void 0)}).data,p=u("2"),r=u("3"),h=m(()=>({grid:{left:34,right:12,top:14,bottom:24},tooltip:{trigger:"axis"},xAxis:{type:"category",data:(e==null?void 0:e.monthly.map(a=>a.ym.slice(2)))??[],axisLine:{lineStyle:{color:r}},axisTick:{show:!1},axisLabel:{color:r,fontSize:10}},yAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},series:[{type:"bar",data:(e==null?void 0:e.monthly.map(a=>a.count))??[],barMaxWidth:18,itemStyle:{color:"#3056d3",borderRadius:[3,3,0,0]}}]}),[e,i]),b=m(()=>{const a=((e==null?void 0:e.by_type)??[]).filter(s=>s.count>0);return{tooltip:{trigger:"item"},legend:{orient:"vertical",right:4,top:"middle",textStyle:{color:p,fontSize:11},itemWidth:10,itemHeight:10},series:[{type:"pie",radius:["48%","72%"],center:["34%","50%"],label:{show:!1},data:a.map(s=>({name:s.label,value:s.count,itemStyle:{color:s.color||"#64748b"}}))}]}},[e,i]),y=m(()=>{const a=((e==null?void 0:e.by_level)??[]).filter(s=>s.count>0).slice().reverse();return{grid:{left:64,right:24,top:8,bottom:22},tooltip:{trigger:"axis"},xAxis:{type:"value",splitLine:{lineStyle:{color:"rgba(128,138,155,0.15)"}},axisLabel:{color:r,fontSize:10}},yAxis:{type:"category",data:a.map(s=>s.label),axisLine:{show:!1},axisTick:{show:!1},axisLabel:{color:p,fontSize:11}},series:[{type:"bar",data:a.map(s=>({value:s.count,itemStyle:{color:s.color||"#3056d3",borderRadius:[0,3,3,0]}})),barMaxWidth:14}]}},[e,i]),f=((e==null?void 0:e.metrics)??[]).slice(0,3);return t.jsxs("div",{className:"mx-auto max-w-[1080px] px-3 py-4 sm:px-6 sm:py-5",children:[t.jsxs("div",{className:"mb-4 flex flex-wrap items-center justify-between gap-3",children:[t.jsxs("div",{className:"flex flex-wrap gap-3",children:[t.jsx(o,{label:"总记录",value:(e==null?void 0:e.total)??0}),t.jsx(o,{label:l?l+" 年新增":"本年新增",value:(e==null?void 0:e.total_this_year)??0}),t.jsx(o,{label:"附件总数",value:(e==null?void 0:e.attachment_total)??0}),f.map(a=>t.jsx(o,{label:a.label,value:a.value,unit:a.unit??""},a.key))]}),t.jsx(j,{className:"w-[120px]",value:l,onChange:a=>n(a.target.value),options:[{value:"",label:"全部年份"},...((e==null?void 0:e.years)??[]).map(a=>({value:String(a),label:a+" 年"}))]})]}),!(e!=null&&e.metrics.length)&&t.jsx("div",{className:"mb-4 rounded-md border border-dashed border-line px-4 py-2 text-[12px] text-ink-3",children:"提示:在「统计」配置中注册指标(如志愿时长、读书本数),带单位的数值字段即可在此量化展示。"}),t.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[t.jsxs(c,{children:[t.jsx(d,{title:"近 12 个月活跃度"}),t.jsx(x,{children:t.jsx("div",{ref:h,className:"h-[220px]"})})]}),t.jsxs(c,{children:[t.jsx(d,{title:"类型分布"}),t.jsx(x,{children:t.jsx("div",{ref:b,className:"h-[220px]"})})]}),t.jsxs(c,{className:"lg:col-span-2",children:[t.jsx(d,{title:"级别分布"}),t.jsx(x,{children:t.jsx("div",{ref:y,className:"h-[200px]"})})]})]})]})}function o({label:l,value:n,unit:i}){return t.jsxs("div",{className:"min-w-[120px] rounded-lg border border-line bg-surface px-4 py-3 shadow-card",children:[t.jsx("div",{className:"text-[12px] text-ink-3",children:l}),t.jsxs("div",{className:"num mt-0.5 text-[22px] font-semibold leading-tight text-ink-1",children:[n,i&&t.jsx("span",{className:"ml-1 text-[12px] font-normal text-ink-3",children:i})]})]})}export{q as DashboardPage};
@@ -1,4 +1,4 @@
import{c as $,f as O,j as e,E as H,w as U,u as k,d as C,r as u,g as y,x as W,y as J,B as j,P as E,I as g,z as Y,C as A,A as V,D as B,F as X,m as S,l as Q,T as q,H as Z,J as z,K as M,S as T,M as D}from"./index-BhG5FvHJ.js";import{C as w,a as G,b as F}from"./card-BtFEK_ax.js";import{u as ee,i as K,a as P}from"./useChart-BfaP0epD.js";import{u as se,i as te,b as ae,a as re,c as ne,d as le,e as ie,f as oe}from"./echarts-BIWefrIc.js";/**
import{c as $,f as O,j as e,E as H,w as U,u as k,d as C,r as u,g as y,x as W,y as J,B as j,P as E,I as g,z as Y,C as A,A as V,D as B,F as X,m as S,l as Q,T as q,H as Z,J as z,K as M,S as T,M as D}from"./index-Bmyzyg8t.js";import{C as w,a as G,b as F}from"./card-Diwu5WOM.js";import{u as ee,i as K,a as P}from"./useChart-rSzOuKiQ.js";import{u as se,i as te,b as ae,a as re,c as ne,d as le,e as ie,f as oe}from"./echarts-BIWefrIc.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as E,u as P,d as S,r as p,f as q,g as F,j as e,B as f,l as B,ac as L,H as z,J as k,S as M,I as Q,ad as _,ae as K,af as v,ag as T}from"./index-BhG5FvHJ.js";/**
import{c as E,u as P,d as S,r as p,f as q,g as F,j as e,B as f,l as B,ac as L,H as z,J as k,S as M,I as Q,ad as _,ae as K,af as v,ag as T}from"./index-Bmyzyg8t.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as fe,j as e,a as N,r as s,b as he,u as ve,d as ge,e as je,f as L,g as ye,I as be,S as k,B as w,P as Q,h as te,L as se,G as ae,R as Ne,i as ne,k as ke,l as we,T as Ce,E as le,m as Ee,n as Se,o as _e,C as ze,p as qe,q as Le,s as Re,t as De,v as Ie}from"./index-BhG5FvHJ.js";/**
import{c as fe,j as e,a as N,r as s,b as he,u as ve,d as ge,e as je,f as L,g as ye,I as be,S as k,B as w,P as Q,h as te,L as se,G as ae,R as Ne,i as ne,k as ke,l as we,T as Ce,E as le,m as Ee,n as Se,o as _e,C as ze,p as qe,q as Le,s as Re,t as De,v as Ie}from"./index-Bmyzyg8t.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1,4 +1,4 @@
import{c as B,d as ve,f as te,r as E,j as e,B as R,ah as Ne,af as L,J as U,S as se,ad as ce,P as q,H as we,a9 as Ce,T as ne,I as W,ag as Se,v as _e}from"./index-BhG5FvHJ.js";import{C as ie,a as ae,b as re}from"./card-BtFEK_ax.js";import{R as le,P as ze,C as Re}from"./rotate-ccw-BDKjoLu6.js";/**
import{c as B,d as ve,f as te,r as E,j as e,B as R,ah as Ne,af as L,J as U,S as se,ad as ce,P as q,H as we,a9 as Ce,T as ne,I as W,ag as Se,v as _e}from"./index-Bmyzyg8t.js";import{C as ie,a as ae,b as re}from"./card-Diwu5WOM.js";import{R as le,P as ze,C as Re}from"./rotate-ccw-DBQnyfoK.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as D,u as K,d as I,f as L,r,g as q,j as e,B as p,P as F,N as O,m as _,l as H,T as P,C as R,O as G,H as J,J as i,I as j,Q as U,S as z,U as V,V as W,q as X}from"./index-BhG5FvHJ.js";import{C as Y}from"./card-BtFEK_ax.js";/**
import{c as D,u as K,d as I,f as L,r,g as q,j as e,B as p,P as F,N as O,m as _,l as H,T as P,C as R,O as G,H as J,J as i,I as j,Q as U,S as z,U as V,V as W,q as X}from"./index-Bmyzyg8t.js";import{C as Y}from"./card-Diwu5WOM.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1 +1 @@
import{j as s,a}from"./index-BhG5FvHJ.js";function i({className:r,...e}){return s.jsx("div",{className:a("rounded-lg border border-line bg-surface shadow-card",r),...e})}function t({title:r,extra:e,className:n}){return s.jsxs("div",{className:a("flex min-h-11 flex-wrap items-center justify-between gap-2 border-b border-line px-4 py-1.5",n),children:[s.jsx("h3",{className:"text-[13px] font-semibold text-ink-1",children:r}),e&&s.jsx("div",{className:"flex flex-wrap items-center gap-2",children:e})]})}function c({className:r,...e}){return s.jsx("div",{className:a("p-4",r),...e})}export{i as C,t as a,c as b};
import{j as s,a}from"./index-Bmyzyg8t.js";function i({className:r,...e}){return s.jsx("div",{className:a("rounded-lg border border-line bg-surface shadow-card",r),...e})}function t({title:r,extra:e,className:n}){return s.jsxs("div",{className:a("flex min-h-11 flex-wrap items-center justify-between gap-2 border-b border-line px-4 py-1.5",n),children:[s.jsx("h3",{className:"text-[13px] font-semibold text-ink-1",children:r}),e&&s.jsx("div",{className:"flex flex-wrap items-center gap-2",children:e})]})}function c({className:r,...e}){return s.jsx("div",{className:a("p-4",r),...e})}export{i as C,t as a,c as b};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{c as t}from"./index-BhG5FvHJ.js";/**
import{c as t}from"./index-Bmyzyg8t.js";/**
* @license lucide-react v0.475.0 - ISC
*
* This source code is licensed under the ISC license.
@@ -1 +1 @@
import{r}from"./index-BhG5FvHJ.js";import{g as f}from"./echarts-BIWefrIc.js";function p(){const[n,o]=r.useState(0);return r.useEffect(()=>{const s=()=>o(t=>t+1);return window.addEventListener("fp-theme-change",s),()=>window.removeEventListener("fp-theme-change",s)},[]),n}function d(n){return getComputedStyle(document.documentElement).getPropertyValue("--text-"+n).trim()||"#454e5c"}function h(n,o){const s=r.useRef(null),[t,i]=r.useState(null),u=r.useRef(n);return u.current=n,r.useEffect(()=>{if(!t)return;const e=f(t);s.current=e,e.setOption(u.current(),{notMerge:!0});const c=new ResizeObserver(()=>e.resize());return c.observe(t),()=>{c.disconnect(),e.dispose(),s.current=null}},[t]),r.useEffect(()=>{var e;(e=s.current)==null||e.setOption(u.current(),{notMerge:!0})},[t,...o]),e=>i(e)}export{h as a,d as i,p as u};
import{r}from"./index-Bmyzyg8t.js";import{g as f}from"./echarts-BIWefrIc.js";function p(){const[n,o]=r.useState(0);return r.useEffect(()=>{const s=()=>o(t=>t+1);return window.addEventListener("fp-theme-change",s),()=>window.removeEventListener("fp-theme-change",s)},[]),n}function d(n){return getComputedStyle(document.documentElement).getPropertyValue("--text-"+n).trim()||"#454e5c"}function h(n,o){const s=r.useRef(null),[t,i]=r.useState(null),u=r.useRef(n);return u.current=n,r.useEffect(()=>{if(!t)return;const e=f(t);s.current=e,e.setOption(u.current(),{notMerge:!0});const c=new ResizeObserver(()=>e.resize());return c.observe(t),()=>{c.disconnect(),e.dispose(),s.current=null}},[t]),r.useEffect(()=>{var e;(e=s.current)==null||e.setOption(u.current(),{notMerge:!0})},[t,...o]),e=>i(e)}export{h as a,d as i,p as u};
+2 -2
View File
@@ -9,8 +9,8 @@
<link rel="icon" href="/icons/pwa-192.png" />
<link rel="apple-touch-icon" href="/icons/pwa-192.png" />
<title>足迹 Footprints</title>
<script type="module" crossorigin src="/assets/index-BhG5FvHJ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CRJEsjf-.css">
<script type="module" crossorigin src="/assets/index-Bmyzyg8t.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D9E2kUSE.css">
</head>
<body>
<div id="root"></div>