Files
ouc-slide-latex-template/theme/beamerinnerthemeOUCSlide.sty
T
laoyang e60e1d768b 新增「扁平简约图示组件」:9 个纯 TikZ 绘图元素
设计基调:圆角色块 + 实心纯色 + 白字 + 细箭头。
**无渐变、无阴影、无立体**(已量化验证:PDF 中 shading 对象数为 0)。
不依赖 smartdiagram 等外部绘图宏包,全部用 TikZ 自绘。

九个组件(颜色列表写在方括号里,可写一个或多个):
  \flatflow[颜色]{条目1, 条目2, ...}          横向箭头流程,自动等分满宽
  \flatstack[颜色]{...}                       纵向流程
  \flatcycle[颜色]{...}                       循环图(弧线箭头)
  \flathub[颜色]{中心}{环绕1, 环绕2, ...}      中心辐射
  \flatsteps[颜色]{...}                       阶梯递进
  \flattime[颜色]{年份列表}{事件列表}           时间轴(上下交替)
  \flatquad[颜色]{左上}{右上}{左下}{右下}       四象限
  \flatpyramid[颜色]{顶层, ..., 底层}          金字塔
  \flatcompare[左色][右色]{左标题}{左内容}{右标题}{右内容}   左右对比

颜色支持列表循环:\flatflow[oucblue,oucred]{...} 两个块不同色;
\flatflow[oucred,oucblue]{六个块} 则按红蓝红蓝循环取色。

防错:颜色是可选参数,若误写成花括号,会报
「图示组件的颜色要写在方括号里」的明确错误(不会静默出错)。

实现中踩到的三个本机 expl3/pgf 坑(均已写进代码注释):
 - 长度做乘除一律用 \pgfmathsetlength,且表达式里只能是「长度」或「纯数字」,
   绝不能出现计数器(否则静默算成 0pt)。
 - \foreach 每轮迭代会开组,内层 \def 出组即失效,需用 \global\def。
 - \ifstrequal 在本机恒为假,字符串比较必须用 \ifx。

另修正一处定义顺序问题:组件必须放在 \ExplSyntaxOff 之后,
否则 `@` 不是字母类别,\fl@... / fl@i@... 这类名字会被解析错。

已验证:TL2022~2026 全部 0 错误 0 溢出;跨版本渲染逐像素一致;
slides.tex 新增 5 页演示,README 新增「图示组件(扁平风格)」一节。
2026-10-13 15:06:04 +08:00

595 lines
22 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%% 版本:1.0
%% 作者:杨永全
%% 本文件的主要目的,是为了定义列表等内部元素的样式
%% 完成时间:2022-05-09
\RequirePackage{listings,verbatim}%引入代码包
\RequirePackage{caption}
\usepackage{tcolorbox} %引入彩色框
\tcbuselibrary{most} %引入所有包
%列表样式,使用了fontawesome图标
\setbeamertemplate{itemize item}{\faBookmark}
\setbeamertemplate{itemize subitem}{\faHandORight}
\setbeamertemplate{itemize subsubitem}{\faSquare}
%超链接的颜色等信息
\hypersetup{
%colorlinks=true, %超链接颜色,全部使用海大红。
linkcolor=oucred,
urlcolor=oucred,
citecolor=oucred,
anchorcolor=oucred,
}
%代码块的一些设定,原模板代码
\renewcommand{\ttdefault}{pcr}
\lstset{
frame=shadowbox,
numbers=left,
numberstyle=\small,
basicstyle=\ttfamily\upshape\small,
breaklines=true,
backgroundcolor=\color{oucsilver10},
xleftmargin=0.8cm,
framexleftmargin=0em,
keywordstyle=\bfseries\color{oucblue},
stringstyle=\color{oucblue},
commentstyle=\color{mygreen},
rulesepcolor= \color{oucsilver40},
rulecolor=\color{oucblue},
deletekeywords={compile},
escapechar=\&,
escapeinside={<@}{@>}
}
%% 代码块标题设定
\DeclareCaptionFormat{codecaptionformat}{%
\parbox{\textwidth}{\hspace{2em}\textcolor{violet}\faCode~\textcolor{violet}{#1#2}\hspace{.5em}#3}%代码块caption的设置
}
\captionsetup[lstlisting]{format=codecaptionformat}
%设置信息框titlebox
\newtcolorbox{titlebox}[2][]
{
title=#1, %标题
colback = white, %标题背景是白色
colframe = #2, %边框颜色
coltitle = #2, %标题颜色
boxrule=.5pt,%边框的粗细,细一点感觉更精致
boxed title style={size=normal,colframe=white,boxrule=0pt},%标题栏没有背景色和边框,只有文字
left=5pt, right=5pt, top=5pt, bottom=0pt,%内部文字边距
boxsep=5pt,
colbacktitle = white, enhanced,%标题的背景,白色
attach boxed title to top center={yshift=-4mm},%把标题放在中间
opacityupper={100}, %上面边框透明度
}
%设置笔记提示框
\newtcolorbox{notebox}[1][]
{
enhanced,
arc=2pt,%圆角
boxrule=.5pt,%边框
left=5pt, right=5pt, top=0pt, bottom=0pt,%文字边距
boxsep=5pt,
colframe=oucblue,
colback=oucblue10,
colbacktitle=oucblue10,
coltext=oucblue,
title={\textcolor{oucblue}{\faStickyNoteO\hspace{.5em}\bfseries{\textbf{#1}}}},
halign=left,
}
%设置重要提示框
\newtcolorbox{importantbox}[1][]
{
enhanced,
arc=2pt,
boxrule=.5pt,
left=5pt, right=5pt, top=0pt, bottom=0pt,
boxsep=5pt,
colframe=oucred,
colback=oucred10,
colbacktitle=oucred10,
coltext=oucred,
title={\textcolor{oucred}{\faExclamationCircle\hspace{.5em}\bfseries{\textbf{#1}}}},
halign=left,
}
%列表框
\newtcolorbox{vcolorbox}[2][]
{
boxrule=0.5pt,%边框,很细
arc=0pt,outer arc=0pt,%不要圆角
left=5pt, right=5pt, top=0pt, bottom=0pt,
leftrule=5pt, %左侧边框的宽度
colframe=#2,%边框的颜色
colback=white,
colbacktitle=white,
coltext=black,%内部文字的颜色,如果要和标题保持一致,可改为#2
title={\textcolor{#2}{\bfseries{\textbf{#1}}}},%标题样式
halign=left,
}
%要点框
\newtcolorbox{pointbox}[3][]
{
colback = white, %背景是白色
boxrule=0.5pt,%边框的粗细,细一点感觉更精致
colframe = #2, %边框颜色
fonttitle = \bfseries,%标题加粗
colbacktitle = #2, enhanced,
left=2pt, right=2pt, top=5pt, bottom=0pt,%内部文字边距
attach boxed title to top center={yshift=-2mm},
width=\textwidth,
height=#3,
title=#1
}
%% ======================================================================
%% 要点框自动排布:pointboxgrid
%% ----------------------------------------------------------------------
%% 把任意多个要点自动排成「每行最多 4 个」的网格:
%% \begin{pointboxgrid}{高度}
%% \pointitem{标题}{颜色}{内容}
%% ...
%% \end{pointboxgrid}
%%
%% 排布规则(行数尽可能少,并尽量均匀):
%% 1~4 个 → 一行
%% 5 个 → 上行 3 + 下行 2
%% 6 个 → 3 + 3
%% 7 个 → 4 + 3
%% 8 个 → 4 + 4
%% 9 个 → 3 + 3 + 3
%% 10 个 → 4 + 3 + 3
%% 11 个 → 4 + 4 + 3
%% 12 个 → 4 + 4 + 4
%% 一般地:行数 = ceil(N/4),前 N mod 行数 行每行多放 1 个。
%%
%% 说明:
%% - 高度参数与 pointbox 的 height 含义相同(固定高度,内容别超出)。
%% - 较多行时请自行减小高度,避免总高超出页面(建议行数 ≤ 4)。
%% - 「每行最多几个」由 \ouc_pbg_max 控制;间距由 \ouc_pbg_gap /
%% \ouc_pbg_vgap 控制,需要时在导言区改这两个长度。
%% ======================================================================
\ExplSyntaxOn
\int_new:N \l_ouc_pbg_cnt
\int_new:N \l_ouc_pbg_n
\int_new:N \l_ouc_pbg_rows
\int_new:N \l_ouc_pbg_base
\int_new:N \l_ouc_pbg_rem
\int_new:N \l_ouc_pbg_k
\int_new:N \l_ouc_pbg_km
\int_new:N \l_ouc_pbg_i
\int_new:N \l_ouc_pbg_max
\dim_new:N \l_ouc_pbg_est
\dim_new:N \l_ouc_pbg_colw
\dim_new:N \l_ouc_pbg_gap
\dim_new:N \l_ouc_pbg_vgap
\dim_new:N \l_ouc_pbg_h
\int_set:Nn \l_ouc_pbg_max { 4 }
\dim_new:N \l_ouc_pbg_avail
\dim_set:Nn \l_ouc_pbg_avail { 243pt }
\dim_set:Nn \l_ouc_pbg_gap { 8pt }
\dim_set:Nn \l_ouc_pbg_vgap { 6pt }
%% 收集一个要点:内容按原样存进带索引的宏,不做任何展开
\NewDocumentCommand \pointitem { m m m }
{
\int_incr:N \l_ouc_pbg_cnt
\tl_set:cn { l_ouc_pbg_t_ \int_use:N \l_ouc_pbg_cnt } {#1}
\tl_set:cn { l_ouc_pbg_c_ \int_use:N \l_ouc_pbg_cnt } {#2}
\tl_set:cn { l_ouc_pbg_b_ \int_use:N \l_ouc_pbg_cnt } {#3}
}
%% 画一个要点框:按序号直接取用存好的宏
\cs_new_protected:Npn \ouc_pbg_emit:n #1
{
\begin{pointbox}
[ \tl_use:c { l_ouc_pbg_t_#1 } ]
{ \tl_use:c { l_ouc_pbg_c_#1 } }
{ \dim_use:N \l_ouc_pbg_h }
\tl_use:c { l_ouc_pbg_b_#1 }
\end{pointbox}
}
%% 列宽 = (正文宽 - (k-1)*间距) / k
%% 注:某些 TeX Live 下 expl3 的维度乘法(*)会静默算出 0,
%% 因此走 fp_eval + dim_to_fp 的浮点路径,稳妥。
\cs_new_protected:Nn \ouc_pbg_setcolw:
{
\int_set:Nn \l_ouc_pbg_km { \int_use:N \l_ouc_pbg_k - 1 }
\dim_set:Nn \l_ouc_pbg_colw
{
\fp_eval:n
{
( \dim_to_fp:n { \textwidth }
- \int_use:N \l_ouc_pbg_km * \dim_to_fp:n { \l_ouc_pbg_gap } )
/ \int_use:N \l_ouc_pbg_k
}
pt
}
}
\cs_new_protected:Nn \ouc_pbg_typeset:
{
\int_set:Nn \l_ouc_pbg_n { \int_use:N \l_ouc_pbg_cnt }
\int_compare:nNnT { \int_use:N \l_ouc_pbg_n } > { 0 }
{
%% 行数 = ceil(N / max)
\int_set:Nn \l_ouc_pbg_rows
{
\int_div_truncate:nn
{ \int_use:N \l_ouc_pbg_n + \int_use:N \l_ouc_pbg_max - 1 }
{ \int_use:N \l_ouc_pbg_max }
}
%% 均匀分配:前 rem 行每行多 1 个(上行不少于下行)
\int_set:Nn \l_ouc_pbg_base
{
\int_div_truncate:nn
{ \int_use:N \l_ouc_pbg_n } { \int_use:N \l_ouc_pbg_rows }
}
\int_set:Nn \l_ouc_pbg_rem
{
\int_use:N \l_ouc_pbg_n
- \int_use:N \l_ouc_pbg_base * \int_use:N \l_ouc_pbg_rows
}
%% 预估总高度并与正文可用高度比较(公式按实测校准):
%% 总高 ≈ 69pt(首行含标题条) + 行数*高度 + (行数-1)*25pt(行间距)
\dim_set:Nn \l_ouc_pbg_est
{
\fp_eval:n
{
69
+ \int_use:N \l_ouc_pbg_rows * \dim_to_fp:n { \l_ouc_pbg_h }
+ ( \int_use:N \l_ouc_pbg_rows - 1 ) * 25
}
pt
}
\ifdim \l_ouc_pbg_est > \l_ouc_pbg_avail
\PackageWarning{oucslide}
{
要点框共 \int_use:N \l_ouc_pbg_n\space 个,排成
\int_use:N \l_ouc_pbg_rows\space 行、每框高 \dim_use:N \l_ouc_pbg_h,
预估总高 \dim_use:N \l_ouc_pbg_est\space 已超过正文可用高度
\dim_use:N \l_ouc_pbg_avail,可能超出页面底部。
请把高度改小(4 行时建议不超过 24pt)或减少要点个数
}
\fi
\int_zero:N \l_ouc_pbg_i
\int_step_inline:nn { \int_use:N \l_ouc_pbg_rows }
{
\int_set:Nn \l_ouc_pbg_k { \int_use:N \l_ouc_pbg_base }
%% 注意:此处的比较必须用 \ifnum。
%% expl3 的 \int_compare:nNnT 在这套 TeX Live 下判定有误,
%% 会把所有 ##1 都判成"小于等于",导致行内个数分配失效。
\ifnum ##1 > \int_use:N \l_ouc_pbg_rem \else
\int_incr:N \l_ouc_pbg_k
\fi
\ouc_pbg_setcolw:
%% 必须用 [onlytextwidth],否则默认 columns 会带来
%% 左/右边距,4 列时左右各溢出约 10pt。
\begin{columns}[onlytextwidth]
\int_step_inline:nn { \int_use:N \l_ouc_pbg_k }
{
\int_incr:N \l_ouc_pbg_i
\begin{column}{\dim_use:N \l_ouc_pbg_colw}
\ouc_pbg_emit:n { \int_use:N \l_ouc_pbg_i }
\end{column}
}
\end{columns}
\ifnum ##1 < \int_use:N \l_ouc_pbg_rows
\vspace{\dim_use:N \l_ouc_pbg_vgap}
\fi
}
}
}
%% 友好设置:\pointboxsetup{每行最多几个}{横向间距}{纵向间距}
%% 例如 \pointboxsetup{5}{6pt}{4pt}
\NewDocumentCommand \pointboxsetup { m m m }
{
\int_set:Nn \l_ouc_pbg_max {#1}
\dim_set:Nn \l_ouc_pbg_gap {#2}
\dim_set:Nn \l_ouc_pbg_vgap {#3}
}
\NewDocumentEnvironment { pointboxgrid } { m }
{ \int_zero:N \l_ouc_pbg_cnt \dim_set:Nn \l_ouc_pbg_h {#1} }
{ \ouc_pbg_typeset: }
\ExplSyntaxOff
%% ======================================================================
%文本框textbox
\newtcolorbox{textbox}[1]{
colback=#1!5!white,
colframe=#1,
boxrule=0.5pt,
arc=2pt,
}
%标题框headbox
\newtcolorbox{headbox}[1]{
colback=#1,
colframe=#1,
boxrule=0.5pt,
arc=2pt,
coltext=white,
fontupper= \bfseries,
left=0pt, right=0pt, top=0pt, bottom=0pt,%内部文字边距
}
%一些元素的中文名称
\renewcommand{\figurename}{图}
\renewcommand{\tablename}{表}
\renewcommand{\lstlistingname}{代码}
\renewcommand{\abstractname}{摘要}
%% ======================================================================
%% 扁平简约图示组件 (flat diagrams)
%% ---------------------------------------------------------------------
%% 设计基调:圆角块 + 实心纯色 + 白字 + 细箭头
%% **无渐变、无阴影、无立体**
%%
%% 所有组件都支持「颜色列表」:写一个颜色则全部同色;
%% 写多个颜色(逗号分隔)则按顺序循环取色。
%% \flatflow{oucblue} % 4 个块全为海大蓝
%% \flatflow{oucblue,oucred,oucpine} % 4 个块循环取这 3 色
%% \flatflow{oucblue,oucred,oucpine,oucdusk,oucamber} % 5 色各不同
%% ======================================================================
\RequirePackage{tikz}
\usetikzlibrary{positioning,arrows.meta,calc,shapes.geometric}
%% ---------------- 防错:颜色参数必须写在 [] 里 ----------------
%% \flatflow 这类命令的「颜色列表」是可选参数,必须写成方括号 [颜色]。
%% 若误写成花括号 {颜色},颜色会被当成第一个条目(静默出错、极难排查),
%% 因此这里检查每个条目是不是模板的颜色名,是则明确报错。
\newcommand{\flcheckitems}[1]{%
\foreach \flci in {#1}{%
%% 注意:\foreach 每轮迭代会开组,内层的 \def 出组即失效,
%% 因此这里必须用 \global\def(否则检测永远不触发)。
\global\def\flhit{0}%
\foreach \flck in {oucblue,ouclightblue,oucred,ouccherry,oucbrown,%
oucgolden,oucsilver,oucpine,oucdusk,oucamber}{%
%% 注意:此处 \ifstrequal 在本机不生效(恒为假),必须用 \ifx
\ifx\flci\flck\global\def\flhit{1}\fi
}%
\ifnum\flhit=1
\PackageError{oucslide}{图示组件的颜色要写在方括号里}{%
命令 \string\flatflow[颜色列表]{条目1, 条目2} 中的颜色是可选参数,%
必须用方括号 [ ],不能用花括号 { }。\MessageBreak
现在「\flci」被当成了条目内容(它看起来是颜色名)。}%
\fi
}%
}
%% ---------------- 辅助:长度 / 计数 / 取色 ----------------
\newlength\flgap
\newlength\flbw
\newcount\flcnt
\def\flcntval{0}
%% 数条目个数,并存成**纯数字宏**(本机 pgfmath 的算式里不能出现计数器)
\newcommand{\flcount}[1]{%
\flcnt=0
\foreach \flzz in {#1}{\global\advance\flcnt by 1}%
\xdef\flcntval{\the\flcnt}%
}
\newcount\flcolcnt
\def\flcoln{0}
\newcommand{\flcolcount}[1]{%
\flcolcnt=0
\foreach \flzz in {#1}{\global\advance\flcolcnt by 1}%
\xdef\flcoln{\the\flcolcnt}%
}
\def\flcur{oucblue}
\def\flmain{oucblue}
%% 第 #2 个块取颜色列表 #1 中的颜色(超出则循环),结果存进 \flcur
\newcommand{\flpick}[2]{%
\pgfmathtruncatemacro{\flidx}{mod(#2-1,\flcoln)+1}%
\foreach \flzz [count=\fljj] in {#1}{%
\ifnum\fljj=\flidx \xdef\flcur{\flzz}\fi
}%
}
%% 取列表第一项作为「主色」(箭头、轴线等用)
\newcommand{\flsetmain}[1]{\flpick{#1}{1}\xdef\flmain{\flcur}}
%% ---------------- 公共样式 ----------------
\tikzset{
flblk/.style={rectangle, rounded corners=2pt, draw=none,
text=white, align=center, inner sep=4pt, font=\small},
flblkl/.style={rectangle, rounded corners=2pt, draw=none,
align=center, inner sep=4pt, font=\footnotesize},
flarr/.style={line width=1pt, -{Stealth[length=1.7mm,width=1.4mm]}}
}
%% ================== 1. 横向箭头流程 ==================
%% \flatflow[颜色列表]{条目1, 条目2, ...}
\NewDocumentCommand \flatflow { O{oucblue} m }{{%
\flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}%
\setlength{\flgap}{16pt}%
\pgfmathsetlength{\flbw}{(0.99\textwidth-(\flcntval-1)*\flgap)/\flcntval}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathtruncatemacro{\flprev}{\fli-1}
\ifnum\fli=1
\node[flblk, fill=\flcur, minimum height=20pt,
text width=\dimexpr\flbw-8pt\relax] (fn1) at (0,0) {\flx};
\else
\node[flblk, fill=\flcur, minimum height=20pt,
text width=\dimexpr\flbw-8pt\relax,
right=\flgap of fn\flprev] (fn\fli) {\flx};
\fi
}%
\foreach \flx [count=\fli] in {#2} {
\ifnum\fli>1
\pgfmathtruncatemacro{\flprev}{\fli-1}
\draw[flarr, draw=\flmain!70!black] (fn\flprev.east) -- (fn\fli.west);
\fi
}%
\end{tikzpicture}%
}}
%% ================== 2. 纵向流程 ==================
\NewDocumentCommand \flatstack { O{oucblue} m }{{%
\flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathtruncatemacro{\flprev}{\fli-1}
\ifnum\fli=1
\node[flblk, fill=\flcur, minimum height=18pt,
minimum width=0.7\textwidth] (fn1) at (0,0) {\flx};
\else
\node[flblk, fill=\flcur, minimum height=18pt,
minimum width=0.7\textwidth, below=8pt of fn\flprev] (fn\fli) {\flx};
\fi
}%
\foreach \flx [count=\fli] in {#2} {
\ifnum\fli>1
\pgfmathtruncatemacro{\flprev}{\fli-1}
\draw[flarr, draw=\flmain!70!black] (fn\flprev.south) -- (fn\fli.north);
\fi
}%
\end{tikzpicture}%
}}
%% ================== 3. 循环图 ==================
\NewDocumentCommand \flatcycle { O{oucblue} m }{{%
\flcheckitems{#2}\flcount{#2}\flcolcount{#1}\flsetmain{#1}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathsetmacro{\flang}{90-360/\flcntval*(\fli-1)}
\node[circle, fill=\flcur, text=white, align=center, inner sep=1pt,
minimum size=32pt, font=\footnotesize] (cn\fli) at (\flang:34pt) {\flx};
}%
\foreach \flx [count=\fli] in {#2} {
\pgfmathtruncatemacro{\flnext}{mod(\fli,\flcntval)+1}
\draw[flarr, draw=\flmain!50!black] (cn\fli) to[bend left=20] (cn\flnext);
}%
\end{tikzpicture}%
}}
%% ================== 4. 中心辐射 ==================
%% \flathub[颜色列表]{中心文字}{环绕1, 环绕2, ...}
\NewDocumentCommand \flathub { O{oucblue} m m }{{%
\flcheckitems{#3}\flcount{#3}\flcolcount{#1}\flsetmain{#1}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#3} {
\flpick{#1}{\fli+1}% % 环绕从颜色列表第 2 项起取色
\pgfmathsetmacro{\flang}{90-360/\flcntval*(\fli-1)}
\node[rectangle, rounded corners=2pt, draw=\flcur!30!white, line width=0.6pt,
fill=\flcur!7!white, text=\flcur!55!black, align=center, inner sep=3pt,
font=\footnotesize, minimum height=17pt] (ln\fli) at (\flang:3.1cm) {\flx};
\draw[draw=\flcur!40!white, line width=0.7pt] (0,0) -- (ln\fli);
}%
\flsetmain{#1}%
\node[circle, fill=\flmain, text=white, align=center, inner sep=2pt,
minimum size=40pt, font=\small\bfseries] at (0,0) {#2};
\end{tikzpicture}%
}}
%% ================== 5. 阶梯递进 ==================
\NewDocumentCommand \flatsteps { O{oucblue} m }{{%
\flcheckitems{#2}\flcolcount{#1}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathsetlengthmacro{\flsx}{(\fli-1)*2.6cm}
\pgfmathsetlengthmacro{\flsy}{(\fli-1)*13pt}
\node[flblk, fill=\flcur, minimum height=16pt, minimum width=2.4cm,
anchor=south west] at (\flsx,\flsy) {\flx};
}%
\end{tikzpicture}%
}}
%% ================== 6. 时间轴 ==================
%% \flattime[颜色列表]{年份列表}{事件列表}
\NewDocumentCommand \flattime { O{oucblue} m m }{{%
\flcheckitems{#2}\flcheckitems{#3}\flcount{#2}\flcolcount{#1}\flsetmain{#1}%
\begin{tikzpicture}
\pgfmathsetlengthmacro{\flstep}{0.9\textwidth/\flcntval}%
\draw[line width=1.8pt, draw=\flmain,
-{Stealth[length=2mm,width=1.6mm]}] (0,0) -- (0.9\textwidth,0);
\foreach \fly [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathsetlengthmacro{\flxx}{(\fli-0.5)*\flstep}
\node[circle, fill=\flcur, minimum size=6pt, inner sep=0pt] at (\flxx,0) {};
\node[rectangle, fill=\flcur, text=white, inner sep=2.5pt,
font=\footnotesize\bfseries, rounded corners=1pt]
at (\flxx,0.22cm) {\fly};
}%
\foreach \fle [count=\fli] in {#3} {
\flpick{#1}{\fli}%
\pgfmathsetlengthmacro{\flxx}{(\fli-0.5)*\flstep}
\ifodd\fli
\node[flblkl, fill=\flcur!7!white, draw=\flcur!25!white, line width=0.5pt,
text=\flcur!55!black, text width=2.3cm, anchor=south]
at (\flxx,0.45cm) {\fle};
\else
\node[flblkl, fill=\flcur!7!white, draw=\flcur!25!white, line width=0.5pt,
text=\flcur!55!black, text width=2.3cm, anchor=north]
at (\flxx,-0.22cm) {\fle};
\fi
}%
\end{tikzpicture}%
}}
%% ================== 7. 四象限 ==================
%% \flatquad[颜色列表]{左上}{右上}{左下}{右下}
\NewDocumentCommand \flatquad { O{oucblue} m m m m }{{%
\flcolcount{#1}%
\flpick{#1}{1}\xdef\flcA{\flcur}%
\flpick{#1}{2}\xdef\flcB{\flcur}%
\flpick{#1}{3}\xdef\flcC{\flcur}%
\flpick{#1}{4}\xdef\flcD{\flcur}%
\begin{tikzpicture}
\node[flblkl, fill=\flcA!8!white, text=\flcA!55!black, text width=4.5cm,
minimum height=34pt] (q1) at (0,0) {#2};
\node[flblkl, fill=\flcB!14!white, text=\flcB!55!black, text width=4.5cm,
minimum height=34pt, right=3pt of q1] (q2) {#3};
\node[flblkl, fill=\flcC!14!white, text=\flcC!55!black, text width=4.5cm,
minimum height=34pt, below=3pt of q1] (q3) {#4};
\node[flblkl, fill=\flcD!8!white, text=\flcD!55!black, text width=4.5cm,
minimum height=34pt, right=3pt of q3] (q4) {#5};
\end{tikzpicture}%
}}
%% ================== 8. 金字塔 ==================
%% \flatpyramid[颜色列表]{顶层, 第二层, ...} (按书写顺序自上而下)
\NewDocumentCommand \flatpyramid { O{oucblue} m }{{%
\flcheckitems{#2}\flcolcount{#1}%
\begin{tikzpicture}
\foreach \flx [count=\fli] in {#2} {
\flpick{#1}{\fli}%
\pgfmathsetlengthmacro{\flw}{(1.6+(\fli-1)*1.6)*1cm}
\pgfmathsetlengthmacro{\flyy}{-(\fli-1)*15.5pt}
\node[trapezium, trapezium left angle=76, trapezium right angle=104,
draw=none, fill=\flcur, text=white, align=center,
inner sep=1pt, minimum height=15pt, minimum width=\flw]
at (0,\flyy) {\flx};
}%
\end{tikzpicture}%
}}
%% ================== 9. 左右对比 ==================
%% \flatcompare[左色][右色]{左标题}{左内容}{右标题}{右内容}
\NewDocumentCommand \flatcompare { O{oucblue} O{oucamber} m m m m }{{%
\begin{tikzpicture}
\node[flblk, fill=#1, minimum width=4.7cm, minimum height=19pt] (h1) at (0,0) {#3};
\node[flblk, fill=#2, minimum width=4.7cm, minimum height=19pt,
right=8pt of h1] (h2) {#5};
\node[flblkl, fill=#1!7!white, text=#1!55!black, text width=4.5cm,
minimum height=54pt, below=3pt of h1] {#4};
\node[flblkl, fill=#2!7!white, text=#2!55!black, text width=4.5cm,
minimum height=54pt, below=3pt of h2] {#6};
\end{tikzpicture}%
}}