Complete GreLin resume template with sample PDF and shared brand colors.
Co-authored-by: Cursor <cursoragent@cursor.com>
7
.gitignore
vendored
@ -1,5 +1,10 @@
|
||||
# Typst 编译产物
|
||||
# Typst 编译产物(示例 PDF 除外)
|
||||
*.pdf
|
||||
!resume/sample.pdf
|
||||
|
||||
# IDE / 编辑器本地配置
|
||||
.cursor/
|
||||
.vscode/
|
||||
|
||||
# 本地照片(可选提交)
|
||||
# resume/assets/profile.jpeg
|
||||
|
||||
39
README.md
@ -1,43 +1,34 @@
|
||||
# GreLin · Typst 文档模板
|
||||
|
||||
西安高岭绿能团队内部 Typst 模板库,按文档类型分目录维护,用于融资 BP、data room 等场景。
|
||||
GreLin 内部 Typst 模板库,按文档类型分目录维护。
|
||||
|
||||
## 目录
|
||||
|
||||
| 文件夹 | 说明 | 入口文件 |
|
||||
|--------|------|----------|
|
||||
| [`resume/`](resume/) | 团队简历(双栏中文版) | `cv.typ` |
|
||||
| [`feasibility-report/`](feasibility-report/) | 可行性研究报告 | `report.typ` |
|
||||
| [`resume/`](resume/) | 团队简历 | `cv.typ` |
|
||||
| [`feasibility/`](feasibility/) | 可行性研究报告 | `report.typ` |
|
||||
|
||||
## 环境要求
|
||||
|
||||
- [Typst](https://typst.app/) CLI(`typst compile` / `typst watch`)
|
||||
- [VS Code](https://code.visualstudio.com/) 或 [Cursor](https://cursor.com/)
|
||||
- 扩展:[Tinymist](https://marketplace.visualstudio.com/items?itemName=myriad-dreamin.tinymist)(搜索 `Tinymist` 安装即可,已集成预览与导出,无需单独装 Typst CLI)
|
||||
|
||||
> 勿安装已废弃的 `Typst LSP`、`Typst Preview`,会与 Tinymist 冲突。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 简历
|
||||
cd resume
|
||||
typst compile cv.typ
|
||||
1. 用 VS Code / Cursor 打开本仓库
|
||||
2. 打开要编辑的入口文件,例如 `resume/cv.typ`
|
||||
3. **看效果(预览)**:`Ctrl+Shift+P` → 输入 `Typst Preview` → 回车;侧边会实时显示排版效果(快捷键 `Ctrl+K` 然后 `V`)
|
||||
4. **导出 PDF**:`Ctrl+Shift+P` → 输入 `Typst Export PDF` → 回车;PDF 默认生成在同目录(如 `resume/sample.pdf`)
|
||||
|
||||
# 可行性报告
|
||||
cd feasibility-report
|
||||
typst compile report.typ
|
||||
```
|
||||
|
||||
## 推送到 Gitea
|
||||
仓库根目录的 [`typst.toml`](typst.toml) 将项目根设为 GreLin,以便 `resume/` 与 `feasibility/` 共享 [`brand/colors.typ`](brand/colors.typ)。命令行编译示例:
|
||||
|
||||
```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
|
||||
typst compile resume/cv.typ resume/sample.pdf --root .
|
||||
```
|
||||
|
||||
## 约定
|
||||
已填好的示例 PDF 见 [`resume/sample.pdf`](resume/sample.pdf)。
|
||||
|
||||
- 每个文档类型独立文件夹,互不混用
|
||||
- 品牌色与 HyperLabel `foundationPalette` 保持一致
|
||||
- 个人/公司数据写在各模板的 `data/config.typ` 中
|
||||
不熟悉 Typst?可参考社区维护的 [小蓝书(Typst 中文教程)](https://tutorial.typst.dev/);英文版见 [官方入门教程](https://typst.app/docs/tutorial/)。
|
||||
|
||||
13
brand/colors.typ
Normal file
@ -0,0 +1,13 @@
|
||||
// GreLin 品牌色板 — 全文档统一(resume / feasibility 等均 import 此文件)
|
||||
// 来源 HyperLabel foundationPalette + labelPalette
|
||||
|
||||
#let color-ink = rgb("#08090B")
|
||||
#let color-body = rgb("#2D3135")
|
||||
#let color-muted = rgb("#434950")
|
||||
#let color-bar-bg = rgb("#EFF0F2")
|
||||
#let color-page = rgb("#FDFDFD")
|
||||
|
||||
// 强调绿同色系:lime → mid(品牌主色)→ olive
|
||||
#let color-lime = rgb("#D7F754")
|
||||
#let color-lime-mid = rgb("#C4D94A") // 浅底装饰、技能条、章节强调、图标
|
||||
#let color-lime-deep = rgb("#A7C957") // 浅底标题文字
|
||||
@ -1,11 +1,6 @@
|
||||
// HyperLabel 基础色板
|
||||
#import "../../brand/colors.typ": *
|
||||
|
||||
#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-accent = color-lime-mid
|
||||
#let color-divider = color-muted.lighten(68%)
|
||||
|
||||
#let heading-font = ("Microsoft YaHei", "Segoe UI")
|
||||
@ -4,12 +4,15 @@ GreLin 双栏中文简历 Typst 模板(HyperLabel 配色),用于融资 BP
|
||||
|
||||
## 快速开始
|
||||
|
||||
在仓库根目录执行(需共享 `brand/colors.typ`):
|
||||
|
||||
```bash
|
||||
cd resume
|
||||
typst compile cv.typ
|
||||
typst watch cv.typ
|
||||
typst compile resume/cv.typ resume/sample.pdf --root .
|
||||
typst watch resume/cv.typ --root .
|
||||
```
|
||||
|
||||
示例输出:[`sample.pdf`](sample.pdf)(已填内容,供版式参考)。
|
||||
|
||||
## 定制
|
||||
|
||||
| 文件 | 说明 |
|
||||
@ -27,6 +30,7 @@ typst watch cv.typ
|
||||
```
|
||||
resume/
|
||||
├── cv.typ
|
||||
├── sample.pdf
|
||||
├── data/
|
||||
│ ├── config.typ
|
||||
│ ├── layout.typ
|
||||
|
||||
@ -1 +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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#C4D94A" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 294 B |
@ -1 +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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#C4D94A" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 285 B After Width: | Height: | Size: 285 B |
@ -1 +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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#C4D94A" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
@ -1 +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>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#C4D94A" 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>
|
||||
|
||||
|
Before Width: | Height: | Size: 480 B After Width: | Height: | Size: 480 B |
@ -10,24 +10,24 @@
|
||||
|
||||
#set text(
|
||||
font: body-font,
|
||||
size: size-base,
|
||||
size: size-body,
|
||||
weight: "regular",
|
||||
fill: color-body,
|
||||
lang: "zh",
|
||||
)
|
||||
|
||||
#set par(leading: leading-body, spacing: par-spacing-tight, justify: true)
|
||||
#set par(leading: leading-body, spacing: space-none, justify: true)
|
||||
|
||||
#page-corners()
|
||||
#hero-section()
|
||||
#header-divider()
|
||||
|
||||
#grid(
|
||||
columns: (sidebar-column, 0.55pt, main-column),
|
||||
column-gutter: (space-lg, space-xl),
|
||||
columns: (sidebar-column, stroke-divider-column, main-column),
|
||||
column-gutter: (space-column-gap, space-column-gap),
|
||||
align: (top, top, top),
|
||||
[
|
||||
#section-title("联系", sidebar: true)
|
||||
#section-title("联系", sidebar: true, above: space-none)
|
||||
#contact-block()
|
||||
#sidebar-section-divider()
|
||||
|
||||
@ -38,9 +38,9 @@
|
||||
#section-title("技能", sidebar: true)
|
||||
#skills-section-block()
|
||||
],
|
||||
line(length: 100%, stroke: color-divider + 0.85pt),
|
||||
line(length: 100%, stroke: stroke-divider-sidebar),
|
||||
[
|
||||
#section-title("工作经历")
|
||||
#section-title("工作经历", above: space-none)
|
||||
#experiences-block()
|
||||
|
||||
#section-title("项目经历")
|
||||
|
||||
@ -7,72 +7,51 @@
|
||||
#let company-city = "陕西西安"
|
||||
|
||||
#let author-name = "齐梦璠"
|
||||
#let author-title = "软件研发"
|
||||
#let author-title = "软件研发 | 系统架构"
|
||||
|
||||
#let author-summary = [
|
||||
西北大学软件工程本硕。具备从 0 到 1 构建业务系统的能力:在美团 AI 应用组参与大模型 Agent 类应用的工程落地;现于高岭绿能负责矿石智能分选系统的软件全链路交付,将算法能力转化为可部署、可运维的工业产品。
|
||||
西北大学软件工程本硕。具备从 0 到 1 构建业务系统的能力:在美团 AI 应用组参与大模型 Agent 类应用的工程落地;现于高岭绿能负责矿石分选系统的软件全链路交付,将算法能力转化为可部署、可运维的工业产品。
|
||||
]
|
||||
|
||||
#let author-address = "陕西西安"
|
||||
#let author-phone = "19829062833"
|
||||
#let author-email = "qmf19829062833@gmail.com"
|
||||
#let author-email = "19829062833@163.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 language_profile = (
|
||||
name: "英语",
|
||||
level: 0.75,
|
||||
label: "读写",
|
||||
)
|
||||
|
||||
// 证件照路径(空字符串表示无照片)
|
||||
#let author-photo-path = "../assets/profile.jpeg"
|
||||
|
||||
#let education = (
|
||||
(
|
||||
degree: "软件工程 · 硕士",
|
||||
degree: "软件工程 / 硕士",
|
||||
school: "西北大学",
|
||||
period: "2022 – 2025",
|
||||
location: "西安",
|
||||
note: none,
|
||||
),
|
||||
(
|
||||
degree: "软件工程 · 学士",
|
||||
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),
|
||||
(name: "系统架构", level: 0.75),
|
||||
)
|
||||
|
||||
// 技术栈分类(侧栏 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"),
|
||||
),
|
||||
// 技术栈 pill 标签
|
||||
#let tech-tags = (
|
||||
"Rust", "Python", "Java", "React", "Tauri", "RAG", "Linux", "MySQL", "Redis",
|
||||
"LLM Agent",
|
||||
)
|
||||
|
||||
#let experiences = (
|
||||
@ -83,10 +62,9 @@
|
||||
start: "2025.06",
|
||||
end: "至今",
|
||||
description: [
|
||||
- 主导分选系统软件全链路建设:控制端、算法推理服务接入、运营管理后台、产线部署与运维监控。
|
||||
- 与算法负责人协作完成模型服务化对接,设计推理回调、异常降级与日志追踪,保障产线连续稳定运行。
|
||||
- 搭建运行数据采集与可视化看板,支撑分选效率、设备状态与告警的实时监测与快速定位。
|
||||
- 产线落地:1 条产线、1 个客户现场已上线稳定运行(2026.06.22 起),单线处理能力 25 t/h,连续稳定运行 20 小时、月均运行 24 天。
|
||||
- 负责矿石分选系统软件研发,覆盖运维工具、运营后台与产线部署联调。
|
||||
- 与算法团队协作完成模型服务化接入,设计推理回调、异常降级与运行监控体系。
|
||||
- 产线落地 1 条 / 1 客户现场,单线 25 t/h;精矿 A/S 4.0+、尾矿 A/S 1.7-、回收率 30%+。
|
||||
],
|
||||
),
|
||||
(
|
||||
@ -96,35 +74,34 @@
|
||||
start: "2024.07",
|
||||
end: "2025.06",
|
||||
description: [
|
||||
- 参与大模型 Agent 应用工程落地,负责对话编排、工具调用(Function Calling)与业务工作流的系统集成。
|
||||
- 完成 Agent 应用前后端开发及 LLM 服务对接,支撑智能助手、自动化任务等多场景功能快速迭代上线。
|
||||
- 基于 RAG、Multi-Agent 等架构模式,将 AI 能力嵌入现有业务系统,独立承担模块从设计到上线的完整交付。
|
||||
- 参与大模型 Agent 应用工程化,负责前后端模块开发与 LLM 服务对接。
|
||||
- 完成对话编排、工具调用与业务系统集成,独立承担功能从设计到上线交付。
|
||||
- 交付 Skill 工具累计覆盖数千名用户、下载量 1 万+;定时任务类能力日均服务约 200 名用户。
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
#let projects = (
|
||||
(
|
||||
name: "智能矿石分选软件平台",
|
||||
name: "HyperOre 矿石分选",
|
||||
org: company-name,
|
||||
location: "西安",
|
||||
start: "2025.06",
|
||||
end: "至今",
|
||||
description: [
|
||||
- 能力体现:将铝土矿分选算法从实验环境工程化为产线可运行系统,覆盖「算法接入 → 设备控制 → 运营后台 → 监控运维」完整闭环。
|
||||
- 交付模块:设备控制软件、算法推理服务网关、运营管理后台、产线监控与数据采集。
|
||||
- 业务指标(铝土矿,我方 / 第三方 / 客户测试):精矿 A/S 4.0+、尾矿 A/S 1.7-、回收率 30%+;单线处理量 25 t/h;1 条产线、1 个客户现场,2026.06.22 起稳定运行。
|
||||
- 面向铝土矿产线的矿石分选系统,实现高光谱采集、模型推理与喷阀执行的闭环控制。
|
||||
- 负责产线运维工具与核心软件模块交付;基于 Rust 实现 Ratatui 运维控制台与 ONNX 推理集成,完成「采集 → 推理 → 喷阀」流程联调。
|
||||
],
|
||||
),
|
||||
(
|
||||
name: "大模型 Agent 业务应用",
|
||||
org: meituan-company-name,
|
||||
location: "北京",
|
||||
start: "2024",
|
||||
end: "2025",
|
||||
name: "HyperLabel 高光谱标注工作站",
|
||||
org: company-name,
|
||||
location: "西安",
|
||||
start: "2025.06",
|
||||
end: "至今",
|
||||
description: [
|
||||
- 能力体现:具备将前沿 AI 能力(Agent、RAG、工具调用)快速产品化并集成进业务系统的工程经验。
|
||||
- 交付内容:Agent 对话引擎接入、业务 API 工具链、运营管理后台及上线运维支持。
|
||||
- 高光谱标注桌面工作站,面向矿石分选场景的训练数据生产与样本管理。
|
||||
- 负责 Tauri + React 前端,以及项目、样本、标签等业务的 CRUD 后端接口。
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
@ -6,140 +6,110 @@
|
||||
#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-path != "" {
|
||||
image(author-photo-path, width: 100%, height: 100%, fit: "cover")
|
||||
} else {
|
||||
none
|
||||
}
|
||||
|
||||
#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 profile-photo-frame(content) = {
|
||||
if content != none {
|
||||
box(width: 100%, height: 100%, clip: true, content)
|
||||
} else {
|
||||
rect(
|
||||
width: 100%,
|
||||
height: 100%,
|
||||
fill: color-bar-bg,
|
||||
stroke: stroke-placeholder,
|
||||
)[
|
||||
#align(center + horizon)[
|
||||
#text(fill: color-muted, size: size-body)[照片]
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// 左上 / 右下装饰条
|
||||
#let page-corners() = context {
|
||||
#let page-corners() = {
|
||||
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),
|
||||
dx: -page-margin.left,
|
||||
dy: -page-margin.top + corner-bar-inset,
|
||||
block(width: corner-bar-width, height: corner-bar-height, fill: color-skill-bar),
|
||||
)
|
||||
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),
|
||||
dx: page-margin.right,
|
||||
dy: page-margin.bottom - corner-bar-inset,
|
||||
block(width: corner-bar-width, height: corner-bar-height, fill: color-skill-bar),
|
||||
)
|
||||
}
|
||||
|
||||
/// 页眉:姓名 + 职位 + 简介 + 2 寸照片
|
||||
#let hero-section() = block(width: 100%, below: space-md)[
|
||||
#let hero-section() = block(width: 100%, below: space-none)[
|
||||
#grid(
|
||||
columns: (1fr, photo-width),
|
||||
column-gutter: space-hero-gutter,
|
||||
column-gutter: space-hero-to-photo,
|
||||
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)
|
||||
#text(font: heading-font, size: size-name, weight: "bold", fill: color-ink)[#author-name]
|
||||
#v(space-hero-name-gap)
|
||||
#text(font: heading-font, size: size-heading, weight: "bold", fill: color-accent-bright)[#author-title]
|
||||
#v(space-hero-title-gap)
|
||||
#set text(weight: "regular")
|
||||
#set par(leading: leading-summary, spacing: par-spacing-tight, justify: true)
|
||||
#set par(leading: leading-summary, spacing: space-none, 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,
|
||||
)
|
||||
#box(width: photo-width, height: photo-height, clip: true, stroke: stroke-photo)[
|
||||
#profile-photo-frame(profile-photo)
|
||||
]
|
||||
]
|
||||
],
|
||||
)
|
||||
]
|
||||
|
||||
/// 页眉与正文之间的横线
|
||||
#let header-divider() = block(width: 100%, above: space-xs, below: space-section)[
|
||||
#line(length: 100%, stroke: color-divider + 0.55pt)
|
||||
#let header-divider() = block(width: 100%, above: space-block, below: space-block)[
|
||||
#line(length: 100%, stroke: stroke-divider)
|
||||
]
|
||||
|
||||
/// 分栏标题
|
||||
#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]
|
||||
#let section-title(title, sidebar: false, divider: false, above: auto, below: auto) = {
|
||||
let title-above = if above != auto {
|
||||
above
|
||||
} else if sidebar {
|
||||
space-none
|
||||
} else {
|
||||
space-block
|
||||
}
|
||||
let title-below = if below != auto { below } else { space-title-gap }
|
||||
block(width: 100%, above: title-above, below: title-below)[
|
||||
#text(font: heading-font, size: size-heading, weight: "bold", fill: color-ink)[#title]
|
||||
#if divider [
|
||||
#v(space-sm)
|
||||
#line(length: 100%, stroke: color-divider + 0.55pt)
|
||||
#v(space-inline)
|
||||
#line(length: 100%, stroke: stroke-divider)
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// 侧栏区块分隔线
|
||||
#let sidebar-section-divider() = block(
|
||||
width: 100%,
|
||||
above: space-sidebar-divider,
|
||||
below: space-xs,
|
||||
)[
|
||||
#line(length: 100%, stroke: color-divider + 0.55pt)
|
||||
#let sidebar-section-divider() = block(width: 100%, above: space-block, below: space-block)[
|
||||
#line(length: 100%, stroke: stroke-divider)
|
||||
]
|
||||
|
||||
#let accent-icon(icon) = box(
|
||||
width: 1em,
|
||||
height: 1em,
|
||||
baseline: 0.08em,
|
||||
icon,
|
||||
)
|
||||
|
||||
#let contact-line(icon, body) = block(below: space-xs)[
|
||||
#let contact-line(icon, body) = block(below: space-inline)[
|
||||
#grid(
|
||||
columns: (1em, 1fr),
|
||||
column-gutter: space-xs,
|
||||
columns: (icon-column-width, 1fr),
|
||||
column-gutter: space-micro,
|
||||
align: (center + horizon, left),
|
||||
box(width: 0.95em, height: 0.95em, baseline: 0.08em, icon),
|
||||
text(fill: color-text-light, size: size-contact)[#body],
|
||||
box(width: icon-box-size, height: icon-box-size, baseline: 0.08em, icon),
|
||||
text(fill: color-body, size: size-secondary)[#body],
|
||||
)
|
||||
]
|
||||
|
||||
#let contact-block() = block(above: 0pt)[
|
||||
#let contact-block() = block(above: space-none)[
|
||||
#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))
|
||||
@ -149,116 +119,116 @@
|
||||
]
|
||||
|
||||
/// 教育背景条目
|
||||
#let edu-entry(degree, school, period, location, note: none) = block(above: space-sm, below: space-xs)[
|
||||
#let edu-entry(degree, school, period, location) = block(above: space-none, below: space-none)[
|
||||
#text(fill: color-body, size: size-body)[#degree]
|
||||
#v(space-xxs)
|
||||
#text(fill: color-muted, size: size-meta)[
|
||||
#v(space-inline)
|
||||
#text(fill: color-text-light, size: size-secondary)[
|
||||
#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)
|
||||
for (i, item) in education.enumerate() {
|
||||
if i > 0 {
|
||||
v(space-between-entries)
|
||||
}
|
||||
edu-entry(item.degree, item.school, item.period, item.location)
|
||||
}
|
||||
}
|
||||
|
||||
/// 侧栏技能子标题
|
||||
#let sidebar-subtitle(title) = block(above: space-md, below: space-xs)[
|
||||
#text(
|
||||
font: heading-font,
|
||||
size: size-small,
|
||||
fill: color-body,
|
||||
)[#title]
|
||||
]
|
||||
#let sidebar-subtitle(title, above: auto, below: auto) = {
|
||||
let gap-above = if above != auto { above } else { space-inline }
|
||||
let gap-below = if below != auto { below } else { space-subtitle-gap }
|
||||
block(above: gap-above, below: gap-below)[
|
||||
#text(font: heading-font, size: size-body, fill: color-body)[#title]
|
||||
]
|
||||
}
|
||||
|
||||
/// 技能进度条(level: 0–1)
|
||||
#let skill-bar(name, level, subtitle: none) = block(above: space-sm, below: space-xs)[
|
||||
/// 技能进度条(level: 0–1);条与条之间仅用 above 控制间距
|
||||
#let skill-bar(name, level, subtitle: none, above: space-none) = block(above: above, below: space-none)[
|
||||
#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],
|
||||
text(fill: color-text-light, size: size-secondary)[#name],
|
||||
text(fill: color-text-light, size: size-secondary)[#subtitle],
|
||||
)
|
||||
] else [
|
||||
#text(fill: color-body, size: size-body)[#name]
|
||||
#text(fill: color-text-light, size: size-secondary)[#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))
|
||||
#v(space-inline)
|
||||
#box(width: 100%, height: skill-bar-height, radius: skill-bar-radius, clip: true)[
|
||||
#place(left + top, block(width: 100%, height: skill-bar-height, fill: color-bar-bg))
|
||||
#place(left + top, block(width: level * 100%, height: skill-bar-height, fill: color-skill-bar))
|
||||
]
|
||||
]
|
||||
|
||||
#let tech-pill(label) = box(
|
||||
fill: color-page,
|
||||
stroke: color-divider + 0.65pt,
|
||||
radius: 3pt,
|
||||
inset: (x: 5pt, y: 2pt),
|
||||
stroke: stroke-pill,
|
||||
radius: pill-radius,
|
||||
inset: (x: pill-inset-x, y: pill-inset-y),
|
||||
baseline: 0.15em,
|
||||
text(fill: color-body, size: size-tech-pill)[#label],
|
||||
text(fill: color-text-light, size: size-secondary)[#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)
|
||||
#let tech-stack-block() = block(width: 100%, above: space-none, below: space-inline)[
|
||||
#box(width: 100%)[
|
||||
#for (i, tag) in category.tags.enumerate() [
|
||||
#if i > 0 [#h(3pt)]
|
||||
#set par(spacing: pill-gap, leading: leading-pill-wrap)
|
||||
#for (i, tag) in tech-tags.enumerate() [
|
||||
#if i > 0 [#h(pill-gap)]
|
||||
#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("技术栈")
|
||||
sidebar-subtitle("能力", above: space-none)
|
||||
block(below: space-none)[
|
||||
#for (i, item) in abilities.enumerate() {
|
||||
skill-bar(
|
||||
item.name,
|
||||
item.level,
|
||||
above: if i == 0 { space-none } else { space-skill-gap },
|
||||
)
|
||||
}
|
||||
#skill-bar(
|
||||
language_profile.name,
|
||||
language_profile.level,
|
||||
subtitle: language_profile.label,
|
||||
above: space-skill-gap,
|
||||
)
|
||||
]
|
||||
sidebar-subtitle("技术栈", above: space-subsection-gap)
|
||||
tech-stack-block()
|
||||
}
|
||||
|
||||
/// 主栏经历 / 项目条目
|
||||
#let timeline-entry(entry, title-key: "company", subtitle-key: "role") = block[
|
||||
#text(fill: color-accent, size: size-subtitle, weight: "bold")[
|
||||
#text(fill: color-body, size: size-body, 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)[
|
||||
#v(space-body)
|
||||
#text(fill: color-text-light, size: size-body)[
|
||||
#entry.location#if entry.location != "" [,]#entry.start - #entry.end
|
||||
]
|
||||
#v(space-sm)
|
||||
#v(space-body)
|
||||
#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)
|
||||
#set par(leading: list-item-leading, spacing: space-none, justify: true)
|
||||
#set list(
|
||||
tight: true,
|
||||
indent: list-indent,
|
||||
body-indent: list-body-indent,
|
||||
)
|
||||
#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() = {
|
||||
|
||||
@ -1,72 +1,78 @@
|
||||
// HyperLabel 基础色板 — 来源:HyperLabel/ComponentsTestPage.tsx foundationPalette
|
||||
#import "../../brand/colors.typ": *
|
||||
|
||||
#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-bright = color-lime-deep
|
||||
#let color-accent = color-muted
|
||||
#let color-skill-bar = color-accent-bright
|
||||
#let color-skill-bar = color-lime-mid
|
||||
#let color-divider = color-muted.lighten(68%)
|
||||
|
||||
#let color-text-light = color-muted.lighten(32%) // 简介、经历描述等长正文
|
||||
#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 寸,与简介区高度更协调)
|
||||
// =============================================================================
|
||||
// GreLin Resume · Design Tokens(唯一规范源)
|
||||
// 字号四档:20 · 12 · 10.5 · 9
|
||||
// 间距三档:6 · 12 · 18(+ 4 micro · 10.5 body 级)
|
||||
// =============================================================================
|
||||
|
||||
// --- 字号 ---
|
||||
#let size-name = 20pt
|
||||
#let size-heading = 12pt
|
||||
#let size-body = 10.5pt
|
||||
#let size-secondary = 9pt
|
||||
|
||||
// --- 行距 ---
|
||||
#let leading-body = 13pt
|
||||
#let leading-summary = leading-body
|
||||
#let leading-entry = leading-body
|
||||
#let leading-pill-wrap = 1.35em
|
||||
|
||||
// --- 间距 ---
|
||||
#let space-none = 0pt
|
||||
#let space-micro = 4pt
|
||||
#let space-inline = 6pt
|
||||
#let space-body = size-body // 10.5pt:条目内块间距、列表行距
|
||||
#let space-title-gap = 12pt
|
||||
#let space-between-entries = 12pt
|
||||
#let space-block = 18pt
|
||||
#let space-hero-to-photo = space-block
|
||||
#let space-column-gap = space-title-gap
|
||||
|
||||
#let space-subtitle-gap = space-body
|
||||
#let space-subsection-gap = space-body
|
||||
#let space-skill-gap = space-body // 能力条 / 英语条之间
|
||||
#let space-hero-name-gap = space-body
|
||||
#let space-hero-title-gap = space-body
|
||||
|
||||
// --- 列表(行内换行与条目间距统一为正文级 10.5pt) ---
|
||||
#let list-item-leading = space-body
|
||||
|
||||
// --- 描边 ---
|
||||
#let stroke-divider = color-divider + 0.55pt
|
||||
#let stroke-divider-sidebar = color-divider + 0.85pt
|
||||
#let stroke-divider-column = 0.55pt
|
||||
#let stroke-photo = color-divider + 0.6pt
|
||||
#let stroke-pill = color-divider + 0.65pt
|
||||
#let stroke-placeholder = color-divider + 0.5pt
|
||||
|
||||
// --- 组件尺寸 ---
|
||||
#let photo-width = 33mm
|
||||
#let photo-height = 40mm
|
||||
#let skill-bar-height = 4pt
|
||||
#let skill-bar-radius = 1pt
|
||||
#let pill-radius = 3pt
|
||||
#let pill-inset-x = 5pt
|
||||
#let pill-inset-y = 2pt
|
||||
#let pill-gap = space-inline
|
||||
#let icon-size = 11pt
|
||||
#let icon-column-width = icon-size
|
||||
#let icon-box-size = icon-size
|
||||
#let list-indent = 0.8em
|
||||
#let list-body-indent = 0.4em
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 排版规范(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
|
||||
@ -74,6 +80,6 @@
|
||||
#let page-margin = (
|
||||
top: 2cm,
|
||||
bottom: 2cm,
|
||||
left: 1.8cm,
|
||||
right: 1.8cm,
|
||||
left: 2cm,
|
||||
right: 2cm,
|
||||
)
|
||||
|
||||
4944
resume/sample.pdf
Normal file
3
typst.toml
Normal file
@ -0,0 +1,3 @@
|
||||
# GreLin Typst monorepo — 项目根目录,供 resume / feasibility 共享 brand/colors.typ
|
||||
|
||||
[project]
|
||||