init: GreLin typst templates

This commit is contained in:
MORRO 2026-06-22 17:15:27 +08:00
commit 5267384253
26 changed files with 840 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Typst 编译产物
*.pdf
# 本地照片(可选提交)
# resume/assets/profile.jpeg
# 系统文件
.DS_Store
Thumbs.db

43
README.md Normal file
View File

@ -0,0 +1,43 @@
# GreLin · Typst 文档模板
西安高岭绿能团队内部 Typst 模板库,按文档类型分目录维护,用于融资 BP、data room 等场景。
## 目录
| 文件夹 | 说明 | 入口文件 |
|--------|------|----------|
| [`resume/`](resume/) | 团队简历(双栏中文版) | `cv.typ` |
| [`feasibility-report/`](feasibility-report/) | 可行性研究报告 | `report.typ` |
## 环境要求
- [Typst](https://typst.app/) CLI`typst compile` / `typst watch`
## 快速开始
```bash
# 简历
cd resume
typst compile cv.typ
# 可行性报告
cd feasibility-report
typst compile report.typ
```
## 推送到 Gitea
```bash
cd GreLin
git init
git add .
git commit -m "init: GreLin typst templates"
git remote add origin <your-gitea-repo-url>
git push -u origin main
```
## 约定
- 每个文档类型独立文件夹,互不混用
- 品牌色与 HyperLabel `foundationPalette` 保持一致
- 个人/公司数据写在各模板的 `data/config.typ`

View File

@ -0,0 +1,32 @@
# Feasibility Report · 可行性研究报告模板
GreLin 可行性报告 Typst 模板,适用于项目论证、内部评审或 BP 附件。
## 快速开始
```bash
cd feasibility-report
typst compile report.typ
typst watch report.typ
```
## 定制
| 文件 | 说明 |
|------|------|
| [`data/config.typ`](data/config.typ) | 公司名、项目名、版本、日期、各章节正文 |
| [`data/theme.typ`](data/theme.typ) | 配色与字号 |
| [`data/layout.typ`](data/layout.typ) | 封面、摘要、章节组件 |
## 章节结构
1. 项目概述
2. 建设必要性与市场分析
3. 技术方案
4. 实施计划
5. 投资估算与资金用途
6. 效益分析
7. 风险分析与对策
8. 结论
`data/config.typ``sections` 数组中逐章填写内容即可。

View File

@ -0,0 +1,64 @@
// =============================================================================
// 可行性报告 — 修改此处定制内容
// =============================================================================
#let company-name = "西安高岭绿能科技有限公司"
#let project-name = "智能矿石分选系统建设项目"
#let report-version = "v0.1"
#let report-date = "2026 年 6 月"
#let report-summary = [
本项目旨在建设面向铝土矿等矿石场景的智能分选软硬件一体化系统,将分选算法工程化为可部署、可运维的产线产品,形成从算法接入、设备控制、运营后台到监控运维的完整闭环,支撑绿色高效矿物加工。
]
#let sections = (
(
title: "一、项目概述",
body: [
说明项目背景、建设目标、建设内容与预期成果。可引用 BP 中的产品定位与目标客户。
],
),
(
title: "二、建设必要性与市场分析",
body: [
行业痛点、政策与环保驱动、目标市场规模与竞争格局。建议配合 BP 市场章节数据。
],
),
(
title: "三、技术方案",
body: [
系统架构(感知、推理、控制、后台、运维)、核心技术路线、与现有产线对接方式。
可补充已验证指标:精矿 A/S、尾矿 A/S、回收率、处理量等。
],
),
(
title: "四、实施计划",
body: [
阶段划分(研发验证 → 试点部署 → 规模化推广)、里程碑、团队分工与关键依赖。
],
),
(
title: "五、投资估算与资金用途",
body: [
硬件、软件、人员、市场等费用结构。与 BP 财务预测保持一致。
],
),
(
title: "六、效益分析",
body: [
经济效益(降本增效、回收率提升)、社会效益与环保效益。尽量量化。
],
),
(
title: "七、风险分析与对策",
body: [
技术、市场、交付、政策等风险及应对措施。
],
),
(
title: "八、结论",
body: [
综合可行性判断与建议。
],
),
)

View File

@ -0,0 +1,43 @@
#import "theme.typ": *
#import "config.typ": *
#let report-title-block() = block(width: 100%, below: space-section)[
#align(center)[
#text(font: heading-font, size: size-title, weight: "bold", fill: color-ink)[
#project-name
]
#v(6pt)
#text(font: heading-font, size: size-h2, fill: color-muted)[可行性研究报告]
#v(10pt)
#text(size: size-meta, fill: color-muted)[
#company-name #sym.bar #report-version #sym.bar #report-date
]
]
#v(space-section)
#line(length: 100%, stroke: color-divider + 0.6pt)
]
#let section-block(section) = block(width: 100%, above: space-section, below: space-block)[
#text(font: heading-font, size: size-h1, weight: "bold", fill: color-ink)[#section.title]
#v(6pt)
#box(width: 18pt, height: 3pt, fill: color-accent)
#v(8pt)
#set par(leading: leading-body, spacing: 6pt, justify: true)
#set text(size: size-body, fill: color-body)
#section.body
]
#let summary-block() = block(width: 100%, above: space-block, below: space-section)[
#text(font: heading-font, size: size-h2, weight: "bold", fill: color-ink)[摘要]
#v(6pt)
#box(
width: 100%,
fill: color-bar-bg,
inset: 10pt,
radius: 4pt,
)[
#set par(leading: leading-body, justify: true)
#set text(size: size-body, fill: color-body)
#report-summary
]
]

View File

@ -0,0 +1,29 @@
// HyperLabel 基础色板
#let color-ink = rgb("#08090B")
#let color-body = rgb("#2D3135")
#let color-muted = rgb("#434950")
#let color-accent = rgb("#D7F754")
#let color-bar-bg = rgb("#EFF0F2")
#let color-page = rgb("#FDFDFD")
#let color-divider = color-muted.lighten(68%)
#let heading-font = ("Microsoft YaHei", "Segoe UI")
#let body-font = ("Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI")
#let size-title = 22pt
#let size-h1 = 14pt
#let size-h2 = 12pt
#let size-body = 10.5pt
#let size-meta = 9pt
#let leading-body = 12.5pt
#let space-section = 14pt
#let space-block = 10pt
#let page-margin = (
top: 2.2cm,
bottom: 2cm,
left: 2.2cm,
right: 2.2cm,
)

View File

@ -0,0 +1,36 @@
#import "data/config.typ": *
#import "data/theme.typ": *
#import "data/layout.typ": *
#set page(
paper: "a4",
margin: page-margin,
fill: color-page,
header: context {
if counter(page).get().first() > 1 [
#set text(size: size-meta, fill: color-muted)
#grid(
columns: (1fr, 1fr),
[#company-name],
align(right)[#project-name · 可行性报告],
)
#v(4pt)
#line(length: 100%, stroke: color-divider + 0.5pt)
]
},
footer: context {
set text(size: size-meta, fill: color-muted)
align(center)[#counter(page).display("1")]
},
)
#set text(font: body-font, lang: "zh", size: size-body, fill: color-body)
#set par(leading: leading-body, justify: true)
#set heading(numbering: none)
#report-title-block()
#summary-block()
#for section in sections {
section-block(section)
}

37
resume/README.md Normal file
View File

@ -0,0 +1,37 @@
# Resume · 团队简历模板
GreLin 双栏中文简历 Typst 模板HyperLabel 配色),用于融资 BP data room 附录。
## 快速开始
```bash
cd resume
typst compile cv.typ
typst watch cv.typ
```
## 定制
| 文件 | 说明 |
|------|------|
| [`data/config.typ`](data/config.typ) | 个人信息、教育、能力、技术栈、经历、项目 |
| [`data/theme.typ`](data/theme.typ) | 配色、字号、间距 |
| [`data/layout.typ`](data/layout.typ) | 版式组件(一般无需改动) |
## 照片
将证件照放入 `assets/profile.jpeg`,或在 `data/config.typ` 中设置 `author-photo` 路径。
## 结构
```
resume/
├── cv.typ
├── data/
│ ├── config.typ
│ ├── layout.typ
│ └── theme.typ
└── assets/
├── profile.jpeg
└── icons/
```

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#6B6B6B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="18.5" cy="17.5" r="3.5"/><circle cx="5.5" cy="17.5" r="3.5"/><circle cx="15" cy="5" r="1"/><path d="M12 17.5V14l-3-3 4-3 2 3h2"/></svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#6B6B6B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>

After

Width:  |  Height:  |  Size: 318 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#6B6B6B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><line x1="6" x2="10" y1="11" y2="11"/><line x1="8" x2="8" y1="9" y2="13"/><line x1="15" x2="15.01" y1="12" y2="12"/><line x1="18" x2="18.01" y1="10" y2="10"/><path d="M17.32 5H6.68a4 4 0 0 0-3.978 3.59c-.006.052-.01.101-.017.152C2.604 9.416 2 14.456 2 16a3 3 0 0 0 3 3c1 0 1.5-.5 2-1l1.414-1.414A2 2 0 0 1 9.828 16h4.344a2 2 0 0 1 1.414.586L17 18c.5.5 1 1 2 1a3 3 0 0 0 3-3c0-1.545-.604-6.584-.685-7.258-.007-.05-.011-.1-.017-.151A4 4 0 0 0 17.32 5z"/></svg>

After

Width:  |  Height:  |  Size: 637 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#D7F754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#D7F754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/></svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#D7F754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0"/><circle cx="12" cy="10" r="3"/></svg>

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#D7F754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"/></svg>

After

Width:  |  Height:  |  Size: 480 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#6B6B6B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>

After

Width:  |  Height:  |  Size: 394 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#434950" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v3M12 18v3M3 12h3M18 12h3"/><circle cx="12" cy="12" r="4"/><path d="m4.9 4.9 2.1 2.1M16.9 16.9l2.1 2.1M19.1 4.9l-2.1 2.1M7.1 16.9l-2.1 2.1"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#434950" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>

After

Width:  |  Height:  |  Size: 256 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#434950" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.66 4.03 3 9 3s9-1.34 9-3V5"/><path d="M3 12c0 1.66 4.03 3 9 3s9-1.34 9-3"/></svg>

After

Width:  |  Height:  |  Size: 320 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#434950" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 10h-5V7l-5 8h5v3z"/><path d="M4 19h16"/></svg>

After

Width:  |  Height:  |  Size: 238 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#434950" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>

After

Width:  |  Height:  |  Size: 261 B

BIN
resume/assets/profile.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

49
resume/cv.typ Normal file
View File

@ -0,0 +1,49 @@
#import "data/config.typ": *
#import "data/theme.typ": *
#import "data/layout.typ": *
#set page(
paper: "a4",
margin: page-margin,
fill: color-page,
)
#set text(
font: body-font,
size: size-base,
weight: "regular",
fill: color-body,
lang: "zh",
)
#set par(leading: leading-body, spacing: par-spacing-tight, justify: true)
#page-corners()
#hero-section()
#header-divider()
#grid(
columns: (sidebar-column, 0.55pt, main-column),
column-gutter: (space-lg, space-xl),
align: (top, top, top),
[
#section-title("联系", sidebar: true)
#contact-block()
#sidebar-section-divider()
#section-title("教育", sidebar: true)
#education-block()
#sidebar-section-divider()
#section-title("技能", sidebar: true)
#skills-section-block()
],
line(length: 100%, stroke: color-divider + 0.85pt),
[
#section-title("工作经历")
#experiences-block()
#section-title("项目经历")
#projects-block()
],
)

130
resume/data/config.typ Normal file
View File

@ -0,0 +1,130 @@
// =============================================================================
// 个人信息 — 修改此处即可定制简历
// =============================================================================
#let company-name = "西安高岭绿能科技有限公司"
#let meituan-company-name = "北京三快在线科技有限公司"
#let company-city = "陕西西安"
#let author-name = "齐梦璠"
#let author-title = "软件研发"
#let author-summary = [
西北大学软件工程本硕。具备从 0 到 1 构建业务系统的能力:在美团 AI 应用组参与大模型 Agent 类应用的工程落地;现于高岭绿能负责矿石智能分选系统的软件全链路交付,将算法能力转化为可部署、可运维的工业产品。
]
#let author-address = "陕西西安"
#let author-phone = "19829062833"
#let author-email = "qmf19829062833@gmail.com"
#let author-website = "https://3506897731.github.io/"
#let author-website-label = "3506897731.github.io"
// 证件照2 寸)— 源文件C:/Users/35068/PKB/GoodPhoto/证件照/证件照-ABC.jpeg
#let author-photo = image(
"../assets/profile.jpeg",
width: 100%,
height: 100%,
fit: "cover",
)
#let education = (
(
degree: "软件工程 · 硕士",
school: "西北大学",
period: "2022 2025",
location: "西安",
note: none,
),
(
degree: "软件工程 · 学士",
school: "西北大学",
period: "2018 2022",
location: "西安",
note: none,
),
)
#let abilities = (
(name: "全栈开发", level: 0.9),
(name: "AI Agent 工程化", level: 0.85),
(name: "后端 / API", level: 0.82),
(name: "工业系统对接", level: 0.78),
)
// 技术栈分类(侧栏 pill 标签)
#let tech-categories = (
(
title: "全栈",
icon: "frontend",
tags: ("Vue", "React", "Python", "Java", "REST API"),
),
(
title: "AI 工程",
icon: "ai",
tags: ("LLM Agent", "RAG"),
),
(
title: "数据库",
icon: "database",
tags: ("MySQL", "Redis"),
),
(
title: "DevOps",
icon: "devops",
tags: ("Docker", "Linux"),
),
)
#let experiences = (
(
company: company-name,
role: "研发工程师",
location: "西安",
start: "2025.06",
end: "至今",
description: [
- 主导分选系统软件全链路建设:控制端、算法推理服务接入、运营管理后台、产线部署与运维监控。
- 与算法负责人协作完成模型服务化对接,设计推理回调、异常降级与日志追踪,保障产线连续稳定运行。
- 搭建运行数据采集与可视化看板,支撑分选效率、设备状态与告警的实时监测与快速定位。
- 产线落地1 条产线、1 个客户现场已上线稳定运行2026.06.22 起),单线处理能力 25 t/h连续稳定运行 20 小时、月均运行 24 天。
],
),
(
company: meituan-company-name,
role: "全栈工程师",
location: "北京",
start: "2024.07",
end: "2025.06",
description: [
- 参与大模型 Agent 应用工程落地负责对话编排、工具调用Function Calling与业务工作流的系统集成。
- 完成 Agent 应用前后端开发及 LLM 服务对接,支撑智能助手、自动化任务等多场景功能快速迭代上线。
- 基于 RAG、Multi-Agent 等架构模式,将 AI 能力嵌入现有业务系统,独立承担模块从设计到上线的完整交付。
],
),
)
#let projects = (
(
name: "智能矿石分选软件平台",
org: company-name,
location: "西安",
start: "2025.06",
end: "至今",
description: [
- 能力体现:将铝土矿分选算法从实验环境工程化为产线可运行系统,覆盖「算法接入 → 设备控制 → 运营后台 → 监控运维」完整闭环。
- 交付模块:设备控制软件、算法推理服务网关、运营管理后台、产线监控与数据采集。
- 业务指标(铝土矿,我方 / 第三方 / 客户测试):精矿 A/S 4.0+、尾矿 A/S 1.7-、回收率 30%+;单线处理量 25 t/h1 条产线、1 个客户现场2026.06.22 起稳定运行。
],
),
(
name: "大模型 Agent 业务应用",
org: meituan-company-name,
location: "北京",
start: "2024",
end: "2025",
description: [
- 能力体现:具备将前沿 AI 能力Agent、RAG、工具调用快速产品化并集成进业务系统的工程经验。
- 交付内容Agent 对话引擎接入、业务 API 工具链、运营管理后台及上线运维支持。
],
),
)

276
resume/data/layout.typ Normal file
View File

@ -0,0 +1,276 @@
#import "theme.typ": *
#import "config.typ": *
#let icon-map-pin = image("../assets/icons/map-pin.svg")
#let icon-phone = image("../assets/icons/phone.svg")
#let icon-mail = image("../assets/icons/mail.svg")
#let icon-globe = image("../assets/icons/globe.svg")
#let icon-tech-frontend = image("../assets/icons/tech-frontend.svg")
#let icon-tech-backend = image("../assets/icons/tech-backend.svg")
#let icon-tech-ai = image("../assets/icons/tech-ai.svg")
#let icon-tech-database = image("../assets/icons/tech-database.svg")
#let icon-tech-devops = image("../assets/icons/tech-devops.svg")
#let tech-category-icon(name) = {
if name == "frontend" { icon-tech-frontend }
else if name == "backend" { icon-tech-backend }
else if name == "ai" { icon-tech-ai }
else if name == "database" { icon-tech-database }
else { icon-tech-devops }
}
#let profile-photo = if author-photo != none {
box(width: 100%, height: 100%, clip: true, author-photo)
} else {
rect(
width: 100%,
height: 100%,
fill: color-bar-bg,
stroke: color-divider + 0.5pt,
)[
#align(center + horizon)[
#text(fill: color-muted, size: size-caption)[照片]
]
]
}
/// 左上 / 右下装饰条
#let page-corners() = context {
place(
top + left,
dx: -page.margin.left,
dy: -page.margin.top + corner-bar-inset,
block(width: corner-bar-width, height: corner-bar-height, fill: color-accent-bright),
)
place(
bottom + right,
dx: page.margin.right,
dy: page.margin.bottom - corner-bar-inset,
block(width: corner-bar-width, height: corner-bar-height, fill: color-accent-bright),
)
}
/// 页眉:姓名 + 职位 + 简介 + 2 寸照片
#let hero-section() = block(width: 100%, below: space-md)[
#grid(
columns: (1fr, photo-width),
column-gutter: space-hero-gutter,
align: (top, top),
[
#text(
font: heading-font,
size: size-name,
weight: "bold",
fill: color-ink,
)[#author-name]
#v(space-xs)
#text(
font: heading-font,
size: size-subtitle,
weight: "bold",
fill: color-accent-bright,
)[#author-title]
#v(space-md)
#set text(weight: "regular")
#set par(leading: leading-summary, spacing: par-spacing-tight, justify: true)
#text(fill: color-text-light, size: size-body)[#author-summary]
],
[
#align(right)[
#box(
width: photo-width,
height: photo-height,
clip: true,
stroke: color-divider + 0.6pt,
profile-photo,
)
]
],
)
]
/// 页眉与正文之间的横线
#let header-divider() = block(width: 100%, above: space-xs, below: space-section)[
#line(length: 100%, stroke: color-divider + 0.55pt)
]
/// 分栏标题
#let section-title(title, sidebar: false, divider: false) = {
let above = if sidebar { space-section } else { space-between-sections }
let below = if sidebar { space-sidebar-title-gap } else { space-sm }
block(width: 100%, above: above, below: below)[
#text(
font: heading-font,
size: size-section,
weight: "bold",
fill: color-ink,
)[#title]
#if divider [
#v(space-sm)
#line(length: 100%, stroke: color-divider + 0.55pt)
]
]
}
/// 侧栏区块分隔线
#let sidebar-section-divider() = block(
width: 100%,
above: space-sidebar-divider,
below: space-xs,
)[
#line(length: 100%, stroke: color-divider + 0.55pt)
]
#let accent-icon(icon) = box(
width: 1em,
height: 1em,
baseline: 0.08em,
icon,
)
#let contact-line(icon, body) = block(below: space-xs)[
#grid(
columns: (1em, 1fr),
column-gutter: space-xs,
align: (center + horizon, left),
box(width: 0.95em, height: 0.95em, baseline: 0.08em, icon),
text(fill: color-text-light, size: size-contact)[#body],
)
]
#let contact-block() = block(above: 0pt)[
#contact-line(icon-map-pin, author-address)
#contact-line(icon-phone, link("tel:" + author-phone, author-phone))
#contact-line(icon-mail, link("mailto:" + author-email, author-email))
#if author-website != "" {
contact-line(icon-globe, link(author-website, author-website-label))
}
]
/// 教育背景条目
#let edu-entry(degree, school, period, location, note: none) = block(above: space-sm, below: space-xs)[
#text(fill: color-body, size: size-body)[#degree]
#v(space-xxs)
#text(fill: color-muted, size: size-meta)[
#school#if period != "" [ · #period]#if location != "" [ · #location]
]
#if note != none [
#v(space-xxs)
#text(fill: color-muted, size: size-small)[#note]
]
]
#let education-block() = {
for item in education {
edu-entry(item.degree, item.school, item.period, item.location, note: item.note)
}
}
/// 侧栏技能子标题
#let sidebar-subtitle(title) = block(above: space-md, below: space-xs)[
#text(
font: heading-font,
size: size-small,
fill: color-body,
)[#title]
]
/// 技能进度条level: 01
#let skill-bar(name, level, subtitle: none) = block(above: space-sm, below: space-xs)[
#if subtitle != none [
#grid(
columns: (1fr, auto),
align: horizon,
text(fill: color-body, size: size-body)[#name],
text(fill: color-muted, size: size-meta)[#subtitle],
)
] else [
#text(fill: color-body, size: size-body)[#name]
]
#v(space-xs)
#box(width: 100%, height: 4pt, radius: 1pt, clip: true)[
#place(left + top, block(width: 100%, height: 4pt, fill: color-bar-bg))
#place(left + top, block(width: level * 100%, height: 4pt, fill: color-skill-bar))
]
]
#let tech-pill(label) = box(
fill: color-page,
stroke: color-divider + 0.65pt,
radius: 3pt,
inset: (x: 5pt, y: 2pt),
baseline: 0.15em,
text(fill: color-body, size: size-tech-pill)[#label],
)
#let tech-category-block(category) = block(above: space-sm, below: space-xxs)[
#grid(
columns: (0.8em, 1fr),
column-gutter: space-xs,
align: horizon,
box(width: 0.8em, height: 0.8em, baseline: 0.1em, tech-category-icon(category.icon)),
text(font: heading-font, weight: "bold", size: size-tech-category, fill: color-ink)[#category.title]
)
#v(space-xs)
#box(width: 100%)[
#for (i, tag) in category.tags.enumerate() [
#if i > 0 [#h(3pt)]
#tech-pill(tag)
]
]
]
#let tech-stack-block() = block(width: 100%, above: space-xxs)[
#for category in tech-categories {
tech-category-block(category)
}
]
#let skills-section-block() = {
sidebar-subtitle("能力")
for item in abilities {
skill-bar(item.name, item.level)
}
sidebar-subtitle("技术栈")
tech-stack-block()
}
/// 主栏经历 / 项目条目
#let timeline-entry(entry, title-key: "company", subtitle-key: "role") = block[
#text(fill: color-accent, size: size-subtitle, weight: "bold")[
#if subtitle-key != none [
#entry.at(title-key) / #entry.at(subtitle-key)
] else [
#entry.at(title-key)
]
]
#v(space-xs)
#text(fill: color-muted, size: size-meta)[
#entry.location#if entry.location != "" []#entry.start - #entry.end
]
#v(space-sm)
#set text(weight: "regular", fill: color-text-light, size: size-body)
#set par(leading: leading-entry, spacing: par-spacing-tight, justify: true)
#set list(spacing: par-spacing-tight, indent: 0.8em, body-indent: 0.4em)
#entry.description
]
#let project-entry(entry) = timeline-entry(entry, title-key: "name", subtitle-key: "org")
/// 工作经历条目
#let experience-entry(entry) = timeline-entry(entry, title-key: "company", subtitle-key: "role")
#let experiences-block() = {
for (i, entry) in experiences.enumerate() {
if i > 0 { v(space-between-entries) }
experience-entry(entry)
}
}
#let projects-block() = {
for (i, entry) in projects.enumerate() {
if i > 0 { v(space-between-entries) }
project-entry(entry)
}
}

79
resume/data/theme.typ Normal file
View File

@ -0,0 +1,79 @@
// HyperLabel 基础色板 — 来源HyperLabel/ComponentsTestPage.tsx foundationPalette
#let color-ink = rgb("#08090B") // 墨黑:主标题
#let color-body = rgb("#2D3135") // 石墨:正文
#let color-muted = rgb("#434950") // 石板灰:次要信息
#let color-accent-bright = rgb("#D7F754") // 强调绿:点缀、技能条、装饰条
#let color-bar-bg = rgb("#EFF0F2") // 银灰:进度条底、标签背景
#let color-page = rgb("#FDFDFD") // 纸灰:页面背景
// 语义映射
#let color-accent = color-muted
#let color-skill-bar = color-accent-bright
#let color-divider = color-muted.lighten(68%)
#let color-text-light = color-muted.lighten(32%) // 简介、经历描述等长正文
#let heading-font = ("Microsoft YaHei", "Segoe UI")
#let body-font = ("Microsoft YaHei UI", "Microsoft YaHei", "Segoe UI")
// 页眉证件照(略短于标准 2 寸,与简介区高度更协调)
#let photo-width = 33mm
#let photo-height = 40mm
// ---------------------------------------------------------------------------
// 排版规范A4 中文简历 · 10.5pt 基准 · 4pt 间距网格)
// ---------------------------------------------------------------------------
#let size-base = 10.5pt
#let size-name = 20pt
#let size-subtitle = 11pt
#let size-section = 11pt
#let size-body = 10.5pt
#let size-meta = 9pt
#let size-small = 9pt
#let size-caption = 8.5pt
#let size-contact = 8pt
#let size-tech-pill = 7pt
#let size-tech-category = 8pt
// 行距10.5pt 正文约 1.2 倍(中文简历常用)
#let leading-normal = 12.5pt
#let leading-body = leading-normal
#let leading-summary = leading-normal
#let leading-entry = leading-normal
#let par-spacing-tight = 3pt
#let space-xxs = 2pt
#let space-xs = 4pt
#let space-sm = 6pt
#let space-md = 8pt
#let space-lg = 12pt
#let space-xl = 16pt
#let space-section = 14pt
#let space-entry = 10pt
#let space-sidebar-divider = 10pt
#let space-hero-gutter = 24pt
// 主栏:条目之间 = 一行正文;区块之间 = 小标题级间距
#let space-between-entries = leading-body
#let space-between-sections = space-xl
// 双栏比例(左栏约 28%
#let sidebar-column = 0.85fr
#let main-column = 2.15fr
// 侧栏标题与内容间距(为常规段落的 2 倍)
#let space-sidebar-title-gap = space-md * 2
// 页边装饰条:高度 +20%,向内偏移
#let corner-bar-width = 5mm
#let corner-bar-height = 21.6mm
#let corner-bar-inset = 11mm
#let page-margin = (
top: 2cm,
bottom: 2cm,
left: 1.8cm,
right: 1.8cm,
)