简历概述的成果数字改为自动统计(列几条算几条) #14

概述原来手写「论文 40 余篇、专利 20 余项」,每次增删条目都要回头改。
改为正文标记、概述取数:

  \item ...论文... \cvtagcount{SCI}     % 显示 SCI 并计数
  \item ...软著... \cvmark{软著}        % 只计数
  概述:论文 \cvnum*{SCI,EI,核心} 篇(SCI \cvnum{SCI} 篇)

共注册 11 类:SCI/EI/核心/专利/第一发明人/软著/科研项目/主持/负责/转化/教研。

关键技术:不用 aux,改独立数据文件 \jobname.cvnum。
中文键经 aux 往返后 \ifcsname 全部失效(xeCJK 把汉字设为活动字符,
键的 token 形态不一致;ASCII 键正常);独立文件里中文键完全正常。
累加与显示分离(cvacc@ 每遍从 0 起 / cvnum@ 读自上一遍),
否则概述在正文之前会让上一遍的值被本遍叠加,数字翻倍。

踩坑:\cvmark 不能放 \experience 的关键词参数(会被 \foreach 拆开,
报 Misplaced \omit),必须放描述参数内;也不能放 longtable 单元格之外。

验证:0 error / 0 Overfull / 0 Underfull,3 页;加标记后版面与基线
逐行一致;连续 5 次编译数字稳定;增删条目后数字自动跟随。

⚠️ 口径变化:自动统计只数列出来的条目。示例原来写「40 余篇」但正文
只列 9 篇代表作,改成自动后变「9 篇」。README 已写明这两种口径的区别。
项目总经费仍手填(\experience 无经费字段)。
This commit is contained in:
2026-10-27 14:56:34 +08:00
parent 8e3017f359
commit eb3b53946c
11 changed files with 309 additions and 36 deletions
+132
View File
@@ -632,3 +632,135 @@
\newcommand\emptySeparator{\multicolumn{2}{c}{}\\}
%======================================================================
% 成果统计:让概述里的数字自动来自后面章节的条目
%
% 问题:简历第一段概述通常写「发表论文 40 余篇、授权专利 12 项、成果转化
% 3 项……」。这些数字若手写,每增删一条论文就得回头改概述,极易漏改。
%
% 思路:给每条加一个**类别标记** \cvmark, 正文里自动累加计数,概述用
% \cvnum 取数。**列几条就算几条**,不去猜总数——你不列出来的成果
% 本来也不该出现在概述里。
%
% 用法(两步):
% ① 每个条目末尾标类别:
% \item ...论文信息... \cvmark{SCI}
% \item ...论文信息... \cvmark{EI}
% \cvmark 不产生任何可见输出(就是个计数动作),放条目首尾都行。
% 一条属于两类就标两次;同名类别重复标会自动累加。
%
% ② 概述里取数:
% \cvnum{SCI} 篇、\cvnum{EI} 篇、合计 \cvnum*{SCI,EI} 篇
% \cvnum* 是「多类求和」,逗号分隔(注意:是英文逗号)。
%
% ③ 空类别返回 0,不会出现 ??。所以可以先写 \cvnum{EI},
% 等真有了 EI 论文再加 \cvmark{EI},中间不会编译报错。
%
% ⚠️ 为什么用独立数据文件而不是 LaTeX 的 aux:
% 实测中文键经 aux 往返后 \ifcsname 全部匹配不上(xeCJK 把汉字设为
% 活动字符,键的 token 形态与写入时不一致;ASCII 键正常,中文键失效)。
% 故改用独立文件 \jobname.cvnum,导言区末尾 \input,键原样保留。
%
% ⚠️ 必须编译两遍(或直接用 latexmk):
% 第一遍边累加边写 \jobname.cvnum,此时概述读到的是**上一遍**的值,
% 首次编译会显示 0;第二遍才是正确数字。日志末尾有 CVSTATS 一行提示。
%
% ⚠️ \jobname.cvnum 是**生成文件**,不要手改、不要提交 git(已在
% .gitignore 里挡住)。删掉它只会让数字从 0 重新算一遍,下一遍即恢复。
%
% ⚠️ 类别名限制:
% · 不要含英文逗号(, 是 \cvnum* 的分隔符)
% · 不要含 @ % # \ { } 这些特殊字符
% · 中文、英文、汉字混排都可以
%======================================================================
\makeatletter
% ---- 数据文件 ------------------------------------------------------
\newwrite\cv@statsout
% 读:导言区末尾把上一遍写入的数字读进来。
% 此时正文尚未开始,不会和本遍的累加互相干扰。
\AtBeginDocument{\InputIfFileExists{\jobname.cvnum}{}{}}
% ---- 内部:类别 -> 计数器名 ----------------------------------------
% 累加值存在 \cvacc@<类别>(TeX 整数寄存器,不用 LaTeX counter,
% 既省事又避开 \the\csname...\endcsname 的展开陷阱)。
% 显示值存在 \cvnum@<类别>(\def 出来的宏,内容就是数字字符串)。
\newcommand\cv@accname[1]{cvacc@#1}
% 保证某类别的累加器存在
\newcommand\cv@ensureacc[1]{%
\@ifundefined{\cv@accname{#1}}%
{\expandafter\newcount\csname\cv@accname{#1}\endcsname}%
{}%
}
% ---- 标记 ----------------------------------------------------------
% \cvmark{<类别>}:把该类别的累加器 +1,无可见输出。
\newcommand\cvmark[1]{%
\cv@ensureacc{#1}%
\global\advance\csname\cv@accname{#1}\endcsname\@ne
% 顺便登记该类别的名字,收尾时按登记顺序写文件
\@ifundefined{cv@seen@\detokenize{#1}}%
{\expandafter\gdef\csname cv@seen@\detokenize{#1}\endcsname{1}%
\g@addto@macro\cv@taglist{\cv@writeone{#1}}}%
{}%
}
% ---- 输出 ----------------------------------------------------------
% \cvnum{SCI} 单类取数
% \cvnum*{SCI,EI} 多类求和
\newcommand\cvnum{\@ifstar\cv@numsum\cv@numone}
% ---- 带计数的标签:显示 + 计数一步完成 ------------------------------
% \cvtagcount{<显示文字>}[<类别>]
% 显示「显示文字」(与正文同号加粗,和原来的 \textbf{SCI} 完全一样),
% 同时给类别 +1。省略类别时用显示文字本身当类别。
%
% 为什么需要它:论文的收录类型本来就写成 \textbf{SCI} 显示在条目末尾,
% 若再单独写一个 \cvmark{SCI},一是啰嗦,二是迟早会漏标。
% 用 \cvtagcount{SCI} 一次到位,显示与计数永远同步。
%
% 用法:
% \cvtagcount{SCI} % 显示 SCI,类别也叫 SCI
% \cvtagcount{核心}[中文核心] % 显示「核心」,类别记为「中文核心」
\newcommand\cvtagcount[2][\relax]{%
\textbf{#2}%
\ifx\relax#1\relax\cvmark{#2}\else\cvmark{#1}\fi}
\newcommand\cv@numone[1]{%
\@ifundefined{cvnum@#1}{0}{\csname cvnum@#1\endcsname}}
\newcommand\cv@numsum[1]{%
\begingroup
\count@\z@
\@for\cv@k:=#1\do{%
\@ifundefined{cvnum@\cv@k}{}{\advance\count@\csname cvnum@\cv@k\endcsname\relax}%
}%
\the\count@
\endgroup}
% ---- 收尾:写数据文件 ----------------------------------------------
% 用独立的 write 流,与 aux 完全无关,因此不受 aux 读入时机影响。
\newcommand\cv@taglist{}
\newcommand\cv@writeone[1]{%
\immediate\write\cv@statsout{%
\string\cvSetNum{\detokenize{#1}}{\the\csname\cv@accname{#1}\endcsname}}%
}
\AtEndDocument{%
\immediate\openout\cv@statsout=\jobname.cvnum\relax
\cv@taglist
\immediate\closeout\cv@statsout
% 日志提示:本遍写入了多少类
\typeout{CVSTATS: 已写入 \jobname.cvnum(若概述数字为 0,请再编译一遍)}%
}
% 数据文件里调用的设置命令。
% ⚠️ 不能含 @:读回时若 @ 不是字母,命令名会被拆断。
\newcommand\cvSetNum[2]{%
\expandafter\def\csname cvnum@#1\endcsname{#2}}
\makeatother