Complete GreLin resume template with sample PDF and shared brand colors.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MORRO 2026-06-23 14:39:21 +08:00
parent 5267384253
commit 226f74c22c
19 changed files with 5216 additions and 308 deletions

7
.gitignore vendored
View File

@ -1,5 +1,10 @@
# Typst 编译产物 # Typst 编译产物(示例 PDF 除外)
*.pdf *.pdf
!resume/sample.pdf
# IDE / 编辑器本地配置
.cursor/
.vscode/
# 本地照片(可选提交) # 本地照片(可选提交)
# resume/assets/profile.jpeg # resume/assets/profile.jpeg

View File

@ -1,43 +1,34 @@
# GreLin · Typst 文档模板 # GreLin · Typst 文档模板
西安高岭绿能团队内部 Typst 模板库,按文档类型分目录维护,用于融资 BP、data room 等场景 GreLin 内部 Typst 模板库,按文档类型分目录维护。
## 目录 ## 目录
| 文件夹 | 说明 | 入口文件 | | 文件夹 | 说明 | 入口文件 |
|--------|------|----------| |--------|------|----------|
| [`resume/`](resume/) | 团队简历(双栏中文版) | `cv.typ` | | [`resume/`](resume/) | 团队简历 | `cv.typ` |
| [`feasibility-report/`](feasibility-report/) | 可行性研究报告 | `report.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 1. 用 VS Code / Cursor 打开本仓库
# 简历 2. 打开要编辑的入口文件,例如 `resume/cv.typ`
cd resume 3. **看效果(预览)**`Ctrl+Shift+P` → 输入 `Typst Preview` → 回车;侧边会实时显示排版效果(快捷键 `Ctrl+K` 然后 `V`
typst compile cv.typ 4. **导出 PDF**`Ctrl+Shift+P` → 输入 `Typst Export PDF` → 回车PDF 默认生成在同目录(如 `resume/sample.pdf`
# 可行性报告 仓库根目录的 [`typst.toml`](typst.toml) 将项目根设为 GreLin以便 `resume/``feasibility/` 共享 [`brand/colors.typ`](brand/colors.typ)。命令行编译示例:
cd feasibility-report
typst compile report.typ
```
## 推送到 Gitea
```bash ```bash
cd GreLin typst compile resume/cv.typ resume/sample.pdf --root .
git init
git add .
git commit -m "init: GreLin typst templates"
git remote add origin <your-gitea-repo-url>
git push -u origin main
``` ```
## 约定 已填好的示例 PDF 见 [`resume/sample.pdf`](resume/sample.pdf)。
- 每个文档类型独立文件夹,互不混用 不熟悉 Typst可参考社区维护的 [小蓝书Typst 中文教程)](https://tutorial.typst.dev/);英文版见 [官方入门教程](https://typst.app/docs/tutorial/)。
- 品牌色与 HyperLabel `foundationPalette` 保持一致
- 个人/公司数据写在各模板的 `data/config.typ`

13
brand/colors.typ Normal file
View 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") // 浅底标题文字

View File

@ -1,11 +1,6 @@
// HyperLabel 基础色板 #import "../../brand/colors.typ": *
#let color-ink = rgb("#08090B") #let color-accent = color-lime-mid
#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 color-divider = color-muted.lighten(68%)
#let heading-font = ("Microsoft YaHei", "Segoe UI") #let heading-font = ("Microsoft YaHei", "Segoe UI")

View File

@ -4,12 +4,15 @@ GreLin 双栏中文简历 Typst 模板HyperLabel 配色),用于融资 BP
## 快速开始 ## 快速开始
在仓库根目录执行(需共享 `brand/colors.typ`
```bash ```bash
cd resume typst compile resume/cv.typ resume/sample.pdf --root .
typst compile cv.typ typst watch resume/cv.typ --root .
typst watch cv.typ
``` ```
示例输出:[`sample.pdf`](sample.pdf)(已填内容,供版式参考)。
## 定制 ## 定制
| 文件 | 说明 | | 文件 | 说明 |
@ -27,6 +30,7 @@ typst watch cv.typ
``` ```
resume/ resume/
├── cv.typ ├── cv.typ
├── sample.pdf
├── data/ ├── data/
│ ├── config.typ │ ├── config.typ
│ ├── layout.typ │ ├── layout.typ

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -10,24 +10,24 @@
#set text( #set text(
font: body-font, font: body-font,
size: size-base, size: size-body,
weight: "regular", weight: "regular",
fill: color-body, fill: color-body,
lang: "zh", 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() #page-corners()
#hero-section() #hero-section()
#header-divider() #header-divider()
#grid( #grid(
columns: (sidebar-column, 0.55pt, main-column), columns: (sidebar-column, stroke-divider-column, main-column),
column-gutter: (space-lg, space-xl), column-gutter: (space-column-gap, space-column-gap),
align: (top, top, top), align: (top, top, top),
[ [
#section-title("联系", sidebar: true) #section-title("联系", sidebar: true, above: space-none)
#contact-block() #contact-block()
#sidebar-section-divider() #sidebar-section-divider()
@ -38,9 +38,9 @@
#section-title("技能", sidebar: true) #section-title("技能", sidebar: true)
#skills-section-block() #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() #experiences-block()
#section-title("项目经历") #section-title("项目经历")

View File

@ -7,72 +7,51 @@
#let company-city = "陕西西安" #let company-city = "陕西西安"
#let author-name = "齐梦璠" #let author-name = "齐梦璠"
#let author-title = "软件研发" #let author-title = "软件研发 | 系统架构"
#let author-summary = [ #let author-summary = [
西北大学软件工程本硕。具备从 0 到 1 构建业务系统的能力:在美团 AI 应用组参与大模型 Agent 类应用的工程落地;现于高岭绿能负责矿石智能分选系统的软件全链路交付,将算法能力转化为可部署、可运维的工业产品。 西北大学软件工程本硕。具备从 0 到 1 构建业务系统的能力:在美团 AI 应用组参与大模型 Agent 类应用的工程落地;现于高岭绿能负责矿石分选系统的软件全链路交付,将算法能力转化为可部署、可运维的工业产品。
] ]
#let author-address = "陕西西安" #let author-address = "陕西西安"
#let author-phone = "19829062833" #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 = "https://3506897731.github.io/"
#let author-website-label = "3506897731.github.io" #let author-website-label = "3506897731.github.io"
// 证件照2 寸)— 源文件C:/Users/35068/PKB/GoodPhoto/证件照/证件照-ABC.jpeg #let language_profile = (
#let author-photo = image( name: "英语",
"../assets/profile.jpeg", level: 0.75,
width: 100%, label: "读写",
height: 100%,
fit: "cover",
) )
// 证件照路径(空字符串表示无照片)
#let author-photo-path = "../assets/profile.jpeg"
#let education = ( #let education = (
( (
degree: "软件工程 · 硕士", degree: "软件工程 / 硕士",
school: "西北大学", school: "西北大学",
period: "2022 2025", period: "2022 2025",
location: "西安", location: "西安",
note: none,
), ),
( (
degree: "软件工程 · 学士", degree: "软件工程 / 学士",
school: "西北大学", school: "西北大学",
period: "2018 2022", period: "2018 2022",
location: "西安", location: "西安",
note: none,
), ),
) )
#let abilities = ( #let abilities = (
(name: "全栈开发", level: 0.9), (name: "全栈开发", level: 0.9),
(name: "AI Agent 工程化", level: 0.85), (name: "系统架构", level: 0.75),
(name: "后端 / API", level: 0.82),
(name: "工业系统对接", level: 0.78),
) )
// 技术栈分类(侧栏 pill 标签) // 技术栈 pill 标签
#let tech-categories = ( #let tech-tags = (
( "Rust", "Python", "Java", "React", "Tauri", "RAG", "Linux", "MySQL", "Redis",
title: "全栈", "LLM Agent",
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 = ( #let experiences = (
@ -83,10 +62,9 @@
start: "2025.06", start: "2025.06",
end: "至今", end: "至今",
description: [ description: [
- 主导分选系统软件全链路建设:控制端、算法推理服务接入、运营管理后台、产线部署与运维监控。 - 负责矿石分选系统软件研发,覆盖运维工具、运营后台与产线部署联调。
- 与算法负责人协作完成模型服务化对接,设计推理回调、异常降级与日志追踪,保障产线连续稳定运行。 - 与算法团队协作完成模型服务化接入,设计推理回调、异常降级与运行监控体系。
- 搭建运行数据采集与可视化看板,支撑分选效率、设备状态与告警的实时监测与快速定位。 - 产线落地 1 条 / 1 客户现场,单线 25 t/h精矿 A/S 4.0+、尾矿 A/S 1.7-、回收率 30%+。
- 产线落地1 条产线、1 个客户现场已上线稳定运行2026.06.22 起),单线处理能力 25 t/h连续稳定运行 20 小时、月均运行 24 天。
], ],
), ),
( (
@ -96,35 +74,34 @@
start: "2024.07", start: "2024.07",
end: "2025.06", end: "2025.06",
description: [ description: [
- 参与大模型 Agent 应用工程落地负责对话编排、工具调用Function Calling与业务工作流的系统集成 - 参与大模型 Agent 应用工程化,负责前后端模块开发与 LLM 服务对接
- 完成 Agent 应用前后端开发及 LLM 服务对接,支撑智能助手、自动化任务等多场景功能快速迭代上线 - 完成对话编排、工具调用与业务系统集成,独立承担功能从设计到上线交付
- 基于 RAG、Multi-Agent 等架构模式,将 AI 能力嵌入现有业务系统,独立承担模块从设计到上线的完整交付 - 交付 Skill 工具累计覆盖数千名用户、下载量 1 万+;定时任务类能力日均服务约 200 名用户
], ],
), ),
) )
#let projects = ( #let projects = (
( (
name: "智能矿石分选软件平台", name: "HyperOre 矿石分选",
org: company-name, org: company-name,
location: "西安", location: "西安",
start: "2025.06", start: "2025.06",
end: "至今", end: "至今",
description: [ description: [
- 能力体现:将铝土矿分选算法从实验环境工程化为产线可运行系统,覆盖「算法接入 → 设备控制 → 运营后台 → 监控运维」完整闭环。 - 面向铝土矿产线的矿石分选系统,实现高光谱采集、模型推理与喷阀执行的闭环控制。
- 交付模块:设备控制软件、算法推理服务网关、运营管理后台、产线监控与数据采集。 - 负责产线运维工具与核心软件模块交付;基于 Rust 实现 Ratatui 运维控制台与 ONNX 推理集成,完成「采集 → 推理 → 喷阀」流程联调。
- 业务指标(铝土矿,我方 / 第三方 / 客户测试):精矿 A/S 4.0+、尾矿 A/S 1.7-、回收率 30%+;单线处理量 25 t/h1 条产线、1 个客户现场2026.06.22 起稳定运行。
], ],
), ),
( (
name: "大模型 Agent 业务应用", name: "HyperLabel 高光谱标注工作站",
org: meituan-company-name, org: company-name,
location: "北京", location: "西安",
start: "2024", start: "2025.06",
end: "2025", end: "至今",
description: [ description: [
- 能力体现:具备将前沿 AI 能力Agent、RAG、工具调用快速产品化并集成进业务系统的工程经验 - 高光谱标注桌面工作站,面向矿石分选场景的训练数据生产与样本管理
- 交付内容Agent 对话引擎接入、业务 API 工具链、运营管理后台及上线运维支持 - 负责 Tauri + React 前端,以及项目、样本、标签等业务的 CRUD 后端接口
], ],
), ),
) )

View File

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

View File

@ -1,72 +1,78 @@
// HyperLabel 基础色板 — 来源HyperLabel/ComponentsTestPage.tsx foundationPalette #import "../../brand/colors.typ": *
#let color-ink = rgb("#08090B") // 墨黑:主标题 #let color-accent-bright = color-lime-deep
#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-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-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 heading-font = ("Microsoft YaHei", "Segoe UI")
#let body-font = ("Microsoft YaHei UI", "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-width = 33mm
#let photo-height = 40mm #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 sidebar-column = 0.85fr
#let main-column = 2.15fr #let main-column = 2.15fr
// 侧栏标题与内容间距(为常规段落的 2 倍)
#let space-sidebar-title-gap = space-md * 2
// 页边装饰条:高度 +20%,向内偏移
#let corner-bar-width = 5mm #let corner-bar-width = 5mm
#let corner-bar-height = 21.6mm #let corner-bar-height = 21.6mm
#let corner-bar-inset = 11mm #let corner-bar-inset = 11mm
@ -74,6 +80,6 @@
#let page-margin = ( #let page-margin = (
top: 2cm, top: 2cm,
bottom: 2cm, bottom: 2cm,
left: 1.8cm, left: 2cm,
right: 1.8cm, right: 2cm,
) )

4944
resume/sample.pdf Normal file

File diff suppressed because it is too large Load Diff

3
typst.toml Normal file
View File

@ -0,0 +1,3 @@
# GreLin Typst monorepo — 项目根目录,供 resume / feasibility 共享 brand/colors.typ
[project]