首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
{{ define "web/404" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 - 页面未找到</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
:root {
|
||||
--primary: #6D8B94;
|
||||
--secondary: #A8B896;
|
||||
--accent: #E3E5E0;
|
||||
--text: #333333;
|
||||
--text-light: #666666;
|
||||
--bg: #FFFFFF;
|
||||
--border: #E8E8E8;
|
||||
--radius: 0.50rem;
|
||||
}
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 与首页 hero 一致的柔和光晕装饰 */
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -20%;
|
||||
left: -10%;
|
||||
width: 31.25rem;
|
||||
height: 31.25rem;
|
||||
background: radial-gradient(circle, rgba(109,139,148,0.06) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
body::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -20%;
|
||||
right: -10%;
|
||||
width: 25rem;
|
||||
height: 25rem;
|
||||
background: radial-gradient(circle, rgba(168,184,150,0.06) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.error-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
padding: 2.50rem 2rem;
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.error-illust {
|
||||
font-size: 4rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 1.50rem;
|
||||
opacity: 0.85;
|
||||
animation: breathe 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
0%, 100% { transform: translateY(0); opacity: 0.85; }
|
||||
50% { transform: translateY(-0.38rem); opacity: 1; }
|
||||
}
|
||||
|
||||
.error-code {
|
||||
font-size: 5rem;
|
||||
font-weight: 200;
|
||||
color: var(--text);
|
||||
line-height: 1;
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.25rem;
|
||||
}
|
||||
|
||||
.error-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.50rem;
|
||||
letter-spacing: 0.12rem;
|
||||
}
|
||||
|
||||
.error-desc {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-light);
|
||||
line-height: 1.7;
|
||||
margin-bottom: 2rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.btn-home {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
padding: 0.62rem 1.75rem;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: var(--radius);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.btn-home:hover {
|
||||
background: #5A757D;
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 2.50rem;
|
||||
height: 0.06rem;
|
||||
background: var(--border);
|
||||
margin: 0 auto 1.50rem;
|
||||
}
|
||||
|
||||
@media (max-width: 37.50rem) {
|
||||
.error-code { font-size: 4rem; }
|
||||
.error-illust { font-size: 3rem; }
|
||||
.error-card { padding: 2rem 1.50rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="error-card">
|
||||
<div class="error-illust">
|
||||
<i class="far fa-compass"></i>
|
||||
</div>
|
||||
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-title">页面走丢了</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<p class="error-desc">
|
||||
您访问的页面不存在或已被移除
|
||||
</p>
|
||||
|
||||
<a href="/" class="btn-home">
|
||||
<i class="fas fa-home"></i> 返回首页
|
||||
</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,424 @@
|
||||
{{ define "component-avatar" }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "component-basic-info" }}
|
||||
<div class="basic-info">
|
||||
<div class="info-header">
|
||||
<h1 class="name">{{.Name}}</h1>
|
||||
<h2 class="title">{{.Title}}</h2>
|
||||
</div>
|
||||
<div class="contact-row">
|
||||
{{ if .Email }}<div class="contact-tag email-protected" onclick="revealEmail(this)" data-email="{{.Email}}"><i class="fas fa-envelope"></i> {{.Email}}</div>{{ end }}
|
||||
{{ if .Phone }}<div class="contact-tag"><i class="fas fa-phone"></i> {{.Phone}}</div>{{ end }}
|
||||
{{ if .Location }}<div class="contact-tag"><i class="fas fa-map-marker-alt"></i> {{.Location}}</div>{{ end }}
|
||||
{{ if .Website }}<div class="contact-tag"><i class="fas fa-globe"></i> <a href="{{.Website}}" target="_blank">{{.Website}}</a></div>{{ end }}
|
||||
</div>
|
||||
<div class="meta-row">
|
||||
{{ if .ExperienceYears }}<div class="meta-tag"><i class="fas fa-clock"></i> {{.ExperienceYears}}</div>{{ end }}
|
||||
{{ if .JobTarget }}<div class="meta-tag"><i class="fas fa-bullseye"></i> {{.JobTarget}}</div>{{ end }}
|
||||
{{ if .Industry }}<div class="meta-tag"><i class="fas fa-building"></i> {{.Industry}}</div>{{ end }}
|
||||
</div>
|
||||
{{ if .Summary }}
|
||||
<div class="summary">{{.Summary}}</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-core-advantages" }}
|
||||
{{ if .BasicInfo.CoreAdvantages }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-star"></i> 核心优势</h2>
|
||||
<div class="section-content">
|
||||
<ul class="advantage-list">
|
||||
{{ range .BasicInfo.CoreAdvantages }}
|
||||
<li><i class="fas fa-check-circle"></i> {{.}}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-experience" }}
|
||||
{{ if .Experience }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-briefcase"></i> 工作经验</h2>
|
||||
<div class="section-content">
|
||||
{{ range .Experience }}
|
||||
<article class="item">
|
||||
<div class="item-header">
|
||||
<span class="company">{{.Company}}</span>
|
||||
<span class="period">{{.StartDate}} - {{.EndDate}}</span>
|
||||
</div>
|
||||
<div class="position">{{.Position}}</div>
|
||||
{{ if .Platforms }}
|
||||
<div class="platforms">
|
||||
{{ range .Platforms }}
|
||||
<span class="platform-tag">{{.}}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .Description }}
|
||||
<div class="description">{{.Description}}</div>
|
||||
{{ end }}
|
||||
{{ if .Highlights }}
|
||||
<ul class="highlights">
|
||||
{{ range .Highlights }}
|
||||
<li><i class="fas fa-circle"></i> {{.}}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-education" }}
|
||||
{{ if .Education }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-graduation-cap"></i> 教育背景</h2>
|
||||
<div class="section-content">
|
||||
{{ range .Education }}
|
||||
<article class="item">
|
||||
<div class="item-header">
|
||||
<span class="school">{{.School}}</span>
|
||||
<span class="period">{{.StartDate}} - {{.EndDate}}</span>
|
||||
</div>
|
||||
<div class="degree">{{.Degree}} - {{.Major}}</div>
|
||||
{{ if .GPA }}
|
||||
<div class="gpa">GPA: {{.GPA}}</div>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-skills" }}
|
||||
{{ if .Skills }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-code"></i> 技术栈</h2>
|
||||
<div class="section-content">
|
||||
{{ $categories := dict }}
|
||||
{{ range .Skills }}
|
||||
{{ $cat := .Category }}
|
||||
{{ if not (index $categories $cat) }}
|
||||
{{ $_ := set $categories $cat slice }}
|
||||
{{ end }}
|
||||
{{ $categories = merge $categories (dict $cat (append (index $categories $cat) .)) }}
|
||||
{{ end }}
|
||||
|
||||
<div class="skill-section">
|
||||
{{ range $cat, $skills := $categories }}
|
||||
<div class="skill-category">
|
||||
<div class="skill-category-title">{{ $cat }}</div>
|
||||
<div class="skill-items">
|
||||
{{ range $skills }}
|
||||
<span class="skill-tag">{{ .Name }}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-skills-progress" }}
|
||||
{{ if .Skills }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-code"></i> 技术栈</h2>
|
||||
<div class="section-content">
|
||||
{{ $levelMap := dict "beginner" "25%" "intermediate" "50%" "advanced" "75%" "expert" "100%" }}
|
||||
{{ $levelText := dict "beginner" "入门" "intermediate" "中级" "advanced" "高级" "expert" "专家" }}
|
||||
|
||||
{{ $categories := dict }}
|
||||
{{ range .Skills }}
|
||||
{{ $cat := .Category }}
|
||||
{{ if not (index $categories $cat) }}
|
||||
{{ $_ := set $categories $cat slice }}
|
||||
{{ end }}
|
||||
{{ $categories = merge $categories (dict $cat (append (index $categories $cat) .)) }}
|
||||
{{ end }}
|
||||
|
||||
<div class="skill-section">
|
||||
{{ range $cat, $skills := $categories }}
|
||||
<div class="skill-category">
|
||||
<div class="skill-category-title">{{ $cat }}</div>
|
||||
{{ range $skills }}
|
||||
{{ $width := index $levelMap .Level }}
|
||||
{{ if not $width }}{{ $width = "50%" }}{{ end }}
|
||||
{{ $text := index $levelText .Level }}
|
||||
{{ if not $text }}{{ $text = .Level }}{{ end }}
|
||||
<div class="skill-progress-item">
|
||||
<div class="skill-progress-header">
|
||||
<span class="skill-progress-name">{{ .Name }}</span>
|
||||
<span class="skill-progress-level">{{ $text }}</span>
|
||||
</div>
|
||||
<div class="skill-progress-bar">
|
||||
<div class="skill-progress-fill" style="width: {{ $width }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-projects" }}
|
||||
{{ if .Projects }}
|
||||
<section>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.12rem;">
|
||||
<h2 class="section-title"><i class="fas fa-project-diagram"></i> 项目经验</h2>
|
||||
<a href="/portfolio" style="color: #3498db; text-decoration: none; font-size: 0.9em; display: flex; align-items: center; gap: 0.31rem; transition: all 0.3s ease;">
|
||||
<i class="fas fa-folder-open"></i> 查看作品集
|
||||
</a>
|
||||
</div>
|
||||
<div class="section-content">
|
||||
{{ range $index, $project := .Projects }}
|
||||
<article class="item">
|
||||
<div class="item-header">
|
||||
<span class="project-name">{{$project.Name}}</span>
|
||||
<span class="period">{{$project.StartDate}} - {{$project.EndDate}}</span>
|
||||
</div>
|
||||
{{ if $project.URL }}
|
||||
<a href="{{$project.URL}}" target="_blank"><i class="fas fa-external-link-alt"></i> 查看项目</a>
|
||||
{{ end }}
|
||||
{{ if $project.Description }}
|
||||
<div class="description">{{$project.Description}}</div>
|
||||
{{ end }}
|
||||
{{ if $project.TechStack }}
|
||||
<div class="tech-stack">
|
||||
{{ range $project.TechStack }}
|
||||
<span class="tech-tag">{{.}}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if $project.Highlights }}
|
||||
<ul class="highlights">
|
||||
{{ range $project.Highlights }}
|
||||
<li><i class="fas fa-star"></i> {{.}}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
{{ if $project.Achievements }}
|
||||
<div class="achievements">
|
||||
<h4><i class="fas fa-trophy"></i> 项目成果</h4>
|
||||
<ul>
|
||||
{{ range $project.Achievements }}
|
||||
<li><i class="fas fa-award"></i> {{.}}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "section-summary" }}
|
||||
{{ if .BasicInfo.Summary }}
|
||||
<section>
|
||||
<h2 class="section-title"><i class="fas fa-user"></i> 个人简介</h2>
|
||||
<div class="section-content">
|
||||
<div class="sidebar-summary">{{.BasicInfo.Summary}}</div>
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ define "theme-switch" }}
|
||||
<style>
|
||||
.theme-switch {
|
||||
position: fixed !important;
|
||||
top: 1.25rem !important;
|
||||
right: 1.25rem !important;
|
||||
z-index: 2147483647 !important;
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
.theme-switch * {
|
||||
pointer-events: auto !important;
|
||||
}
|
||||
.theme-switch button {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
padding: 0.62rem 1.12rem !important;
|
||||
border-radius: 1.56rem !important;
|
||||
cursor: pointer !important;
|
||||
font-size: 0.81rem !important;
|
||||
font-weight: 500 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 0.50rem !important;
|
||||
box-shadow: 0 0.25rem 0.94rem rgba(102, 126, 234, 0.4) !important;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
white-space: nowrap !important;
|
||||
line-height: 1.4 !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
||||
pointer-events: auto !important;
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
transform: translateY(-0.12rem) !important;
|
||||
box-shadow: 0 0.38rem 1.25rem rgba(102, 126, 234, 0.5) !important;
|
||||
background: linear-gradient(135deg, #7c8eea 0%, #8b5bb3 100%) !important;
|
||||
}
|
||||
.theme-switch button:active {
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
.theme-switch button i {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
@media print {
|
||||
.theme-switch { display: none !important; }
|
||||
}
|
||||
@media (max-width: 40rem) {
|
||||
.theme-switch {
|
||||
top: 0.75rem !important;
|
||||
right: 0.75rem !important;
|
||||
}
|
||||
.theme-switch button {
|
||||
padding: 0.50rem 0.88rem !important;
|
||||
font-size: 0.75rem !important;
|
||||
}
|
||||
.theme-switch button span {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="theme-switch">
|
||||
<button id="themeToggle" type="button" title="点击切换简历主题" onclick="(function(){
|
||||
var themes = ['tech', 'modern', 'classic', 'minimalist', 'fresh', 'professional', 'sidebar', 'elegant'];
|
||||
var themeNames = {'tech':'科技极客','modern':'现代商务','classic':'传统经典','minimalist':'极简风格','fresh':'清新自然','professional':'专业商务','sidebar':'经典侧边栏','elegant':'优雅轻奢'};
|
||||
var currentTheme = '{{.Template}}';
|
||||
var currentRoute = '{{.Route}}' || 'default';
|
||||
var idx = themes.indexOf(currentTheme);
|
||||
if (idx < 0) idx = 0;
|
||||
var nextIdx = (idx + 1) % themes.length;
|
||||
var newTheme = themes[nextIdx];
|
||||
var btn = document.getElementById('themeToggle');
|
||||
if (btn) { btn.style.opacity = '0.6'; btn.style.pointerEvents = 'none'; }
|
||||
var toast = document.createElement('div');
|
||||
toast.style.cssText = 'position:fixed;top:1.25rem;left:50%;transform:translateX(-50%);padding:0.75rem 1.50rem;border-radius:0.50rem;color:white;font-size:0.88rem;z-index:2147483647;box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.15);transition:opacity 0.3s;';
|
||||
fetch('/api/resume?route=' + encodeURIComponent(currentRoute), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ template: newTheme })
|
||||
}).then(function(resp) {
|
||||
if (resp.ok) {
|
||||
toast.style.background = '#10b981';
|
||||
toast.textContent = '切换到' + themeNames[newTheme] + '主题';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function(){ toast.style.opacity = '0'; }, 1500);
|
||||
setTimeout(function(){ window.location.reload(); }, 500);
|
||||
} else {
|
||||
toast.style.background = '#ef4444';
|
||||
toast.textContent = '切换失败';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function(){ toast.style.opacity = '0'; }, 2000);
|
||||
if (btn) { btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }
|
||||
}
|
||||
}).catch(function(err) {
|
||||
toast.style.background = '#ef4444';
|
||||
toast.textContent = '切换失败,请检查网络';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function(){ toast.style.opacity = '0'; }, 2000);
|
||||
if (btn) { btn.style.opacity = '1'; btn.style.pointerEvents = 'auto'; }
|
||||
});
|
||||
})()">
|
||||
<i class="fas fa-palette"></i>
|
||||
<span>切换主题</span>
|
||||
</button>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ define "theme-script" }}
|
||||
<script>
|
||||
(function() {
|
||||
const themes = ['tech', 'modern', 'classic', 'minimalist', 'fresh', 'professional', 'sidebar', 'elegant'];
|
||||
const themeNames = {
|
||||
'tech': '科技极客',
|
||||
'modern': '现代商务',
|
||||
'classic': '传统经典',
|
||||
'minimalist': '极简风格',
|
||||
'fresh': '清新自然',
|
||||
'professional': '专业商务',
|
||||
'sidebar': '经典侧边栏',
|
||||
'elegant': '优雅轻奢'
|
||||
};
|
||||
const currentTheme = '{{.Template}}';
|
||||
const currentRoute = '{{.Route}}';
|
||||
let currentThemeIndex = themes.indexOf(currentTheme) !== -1 ? themes.indexOf(currentTheme) : 0;
|
||||
let isSwitching = false;
|
||||
|
||||
function showToast(message, type) {
|
||||
const toast = document.createElement('div');
|
||||
toast.style.cssText = 'position:fixed;top:1.25rem;left:50%;transform:translateX(-50%);padding:0.75rem 1.50rem;border-radius:0.50rem;color:white;font-size:0.88rem;z-index:2147483647;box-shadow:0 0.25rem 0.75rem rgba(0,0,0,0.15);transition:opacity 0.3s;';
|
||||
if (type === 'success') toast.style.background = '#10b981';
|
||||
else if (type === 'error') toast.style.background = '#ef4444';
|
||||
else toast.style.background = '#3b82f6';
|
||||
toast.textContent = message;
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function() { toast.style.opacity = '0'; }, 2000);
|
||||
setTimeout(function() { toast.remove(); }, 2500);
|
||||
}
|
||||
|
||||
async function switchTheme() {
|
||||
if (isSwitching) return;
|
||||
isSwitching = true;
|
||||
|
||||
const nextIndex = (currentThemeIndex + 1) % themes.length;
|
||||
const newTheme = themes[nextIndex];
|
||||
const btn = document.getElementById('themeToggle');
|
||||
|
||||
btn.style.opacity = '0.6';
|
||||
btn.style.pointerEvents = 'none';
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/resume?route=' + encodeURIComponent(currentRoute || 'default'), {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ template: newTheme })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showToast('切换到' + themeNames[newTheme] + '主题', 'success');
|
||||
currentThemeIndex = nextIndex;
|
||||
setTimeout(function() { window.location.reload(); }, 500);
|
||||
} else {
|
||||
const data = await response.json().catch(function() { return {}; });
|
||||
showToast('切换失败:' + (data.message || '未知错误'), 'error');
|
||||
btn.style.opacity = '1';
|
||||
btn.style.pointerEvents = 'auto';
|
||||
isSwitching = false;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to switch theme:', err);
|
||||
showToast('切换失败,请检查网络连接', 'error');
|
||||
btn.style.opacity = '1';
|
||||
btn.style.pointerEvents = 'auto';
|
||||
isSwitching = false;
|
||||
}
|
||||
}
|
||||
|
||||
window.__switchTheme = switchTheme;
|
||||
|
||||
const toggleBtn = document.getElementById('themeToggle');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', switchTheme);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,314 @@
|
||||
{{ define "web/classic" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: Georgia, 'Times New Roman', Times, 'SimSun', serif;
|
||||
background: #e8e8e8;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='paper'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.04' numOctaves='5'/%3E%3CfeDiffuseLighting in='noise' lighting-color='%23ffffff' surfaceScale='2'%3E%3CfeDistantLight azimuth='45' elevation='60'/%3E%3C/feDiffuseLighting%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23paper)'/%3E%3C/svg%3E");
|
||||
opacity: 0.04;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: white;
|
||||
border: 0.06rem solid #ccc;
|
||||
color: #333;
|
||||
padding: 0.44rem 0.88rem;
|
||||
border-radius: 0.19rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
border-color: #2c3e50;
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 55rem;
|
||||
margin: 2.50rem auto;
|
||||
background: white;
|
||||
box-shadow: 0 0.12rem 0.75rem rgba(0,0,0,0.12);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header.resume-header {
|
||||
background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);
|
||||
padding: 2.38rem 2.62rem;
|
||||
color: white;
|
||||
border-bottom: 0.31rem solid #e94560;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
header.resume-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 0.12rem;
|
||||
background: linear-gradient(90deg, transparent, #e94560, transparent);
|
||||
}
|
||||
header.resume-header .header-inner { display: flex; align-items: center; gap: 1.88rem; }
|
||||
header.resume-header .avatar img {
|
||||
width: 7.50rem;
|
||||
height: 7.50rem;
|
||||
border-radius: 0.12rem;
|
||||
border: 0.19rem solid white;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.4);
|
||||
}
|
||||
header.resume-header .basic-info { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
header.resume-header .basic-info .info-header { margin-bottom: 0.12rem; }
|
||||
header.resume-header .basic-info .name { font-size: 1.9em; font-weight: 700; margin-bottom: 0.38rem; letter-spacing: 0.06rem; line-height: 1.2; }
|
||||
header.resume-header .basic-info .title { font-size: 1em; color: #bdc3c7; }
|
||||
|
||||
header.resume-header .basic-info .contact-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.84em;
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
padding: 0.31rem 0.75rem;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border-radius: 0.19rem;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s ease;
|
||||
border: 0.06rem solid rgba(255,255,255,0.08);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag:hover {
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-color: rgba(255,255,255,0.15);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag i { width: 0.88rem; text-align: center; opacity: 0.7; }
|
||||
header.resume-header .basic-info .contact-tag a { color: white; text-decoration: none; position: relative; }
|
||||
header.resume-header .basic-info .contact-tag a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: white;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag a:hover::after { width: 100%; }
|
||||
header.resume-header .basic-info .contact-tag a:hover { color: #3498db; }
|
||||
|
||||
header.resume-header .basic-info .meta-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.62rem;
|
||||
font-size: 0.8em;
|
||||
color: #95a5a6;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.31rem;
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border-radius: 0.12rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag i { width: 0.75rem; text-align: center; opacity: 0.4; }
|
||||
|
||||
main.resume-body { display: flex; }
|
||||
|
||||
aside.sidebar { width: 32%; background: #f7f7f7; padding: 1.75rem 1.38rem; border-right: 0.06rem solid #e5e5e5; }
|
||||
aside.sidebar .section { margin-bottom: 1.75rem; }
|
||||
aside.sidebar .section:last-child { margin-bottom: 0; }
|
||||
aside.sidebar .section-title {
|
||||
font-size: 0.95em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 0.88rem;
|
||||
padding-bottom: 0.38rem;
|
||||
border-bottom: 0.06rem solid #ddd;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
aside.sidebar .section-title i { color: #3498db; margin-right: 0.38rem; }
|
||||
|
||||
aside.sidebar .summary { color: #555; font-size: 0.88em; line-height: 1.65; }
|
||||
|
||||
aside.sidebar .skill-items { display: flex; flex-wrap: wrap; gap: 0.38rem; }
|
||||
aside.sidebar .skill-tag {
|
||||
padding: 0.31rem 0.62rem;
|
||||
background: #e6e6e6;
|
||||
border-radius: 0.12rem;
|
||||
font-size: 0.82em;
|
||||
color: #444;
|
||||
border: 0.06rem solid #d6d6d6;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
aside.sidebar .skill-tag:hover {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
section.main-content { width: 68%; padding: 2rem; }
|
||||
section.main-content .section { margin-bottom: 2rem; }
|
||||
section.main-content .section:last-child { margin-bottom: 0; }
|
||||
section.main-content .section-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1.12rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.12rem solid #3498db;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.62rem;
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
section.main-content .section-title i { color: #3498db; }
|
||||
|
||||
.section-content .item { margin-bottom: 1.50rem; padding-bottom: 1.25rem; border-bottom: 0.06rem solid #e5e5e5; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
.section-content .item:last-child { margin-bottom: 0; border-bottom: none; padding-bottom: 0; }
|
||||
.section-content .item:hover { background: #fafafa; padding-left: 0.62rem; margin-left: -0.62rem; margin-right: -0.62rem; padding-right: 0.62rem; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.38rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: #2c3e50; font-size: 1em; }
|
||||
.item-header .period { color: #777; font-size: 0.82em; font-style: italic; }
|
||||
.item-header a { color: #3498db; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #3498db;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
|
||||
.position { color: #3498db; font-weight: 500; margin-bottom: 0.50rem; }
|
||||
.degree { color: #555; margin-bottom: 0.31rem; }
|
||||
.gpa { color: #777; font-size: 0.82em; }
|
||||
|
||||
.description { color: #555; line-height: 1.6; margin-bottom: 0.50rem; font-size: 0.9em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: #444; padding: 0.25rem 0; font-size: 0.9em; display: flex; align-items: flex-start; gap: 0.50rem; }
|
||||
.highlights li i { color: #3498db; font-size: 0.78em; }
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.31rem; margin-top: 0.50rem; }
|
||||
.tech-tag { padding: 0.19rem 0.56rem; background: #2c3e50; color: white; border-radius: 0.12rem; font-size: 0.78em; }
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.31rem; margin-top: 0.50rem; }
|
||||
.platform-tag { padding: 0.12rem 0.50rem; background: rgba(52,152,219,0.1); color: #3498db; border-radius: 0.12rem; font-size: 0.76em; border: 0.06rem solid rgba(52,152,219,0.2); }
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: #444;
|
||||
padding: 0.44rem 0;
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.50rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.advantage-list li i { color: #3498db; font-size: 0.85em; margin-top: 0.12rem; }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
main.resume-body { flex-direction: column; }
|
||||
aside.sidebar { width: 100%; border-right: none; border-bottom: 0.06rem solid #e0e0e0; }
|
||||
section.main-content { width: 100%; }
|
||||
header.resume-header .header-inner { flex-direction: column; text-align: center; }
|
||||
header.resume-header .basic-info .contact { justify-content: center; }
|
||||
.resume-container { margin: 0.62rem; }
|
||||
header.resume-header { padding: 1.56rem 1.25rem; }
|
||||
section.main-content { padding: 1.38rem; }
|
||||
.section-content .item:hover { margin: 0; padding: 0; padding-bottom: 1.25rem; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
body::before { display: none; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
header.resume-header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
aside.sidebar {
|
||||
background: #f7f7f7;
|
||||
border-right: 0.06rem solid #ddd;
|
||||
}
|
||||
.section-content .item:hover {
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 1.25rem;
|
||||
}
|
||||
a { color: #3498db; text-decoration: underline; }
|
||||
@page { margin: 12mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
<div class="header-inner">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</div>
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
<aside class="sidebar">
|
||||
{{ template "section-summary" . }}
|
||||
{{ template "section-skills" . }}
|
||||
</aside>
|
||||
<section class="main-content">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,306 @@
|
||||
{{ define "web/contact" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>联系我 - {{.BasicInfo.Name}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--primary-dark: #2980b9;
|
||||
--secondary-color: #2c3e50;
|
||||
--accent-green: #27ae60;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #555;
|
||||
--bg-light: #f0f2f5;
|
||||
--bg-card: #ffffff;
|
||||
--border-color: #dde3e8;
|
||||
--shadow-md: 0 0.25rem 1.50rem rgba(0,0,0,0.06);
|
||||
--radius-lg: 0.75rem;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: var(--bg-light); min-height: 100vh; }
|
||||
|
||||
.nav-bar {
|
||||
background: var(--bg-card);
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
padding: 0.88rem 1.25rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 0.06rem 0.19rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.nav-bar .nav-content {
|
||||
max-width: 53.75rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-bar .back-link {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-bar .back-link:hover { gap: 0.62rem; }
|
||||
|
||||
.nav-bar .nav-title {
|
||||
font-size: 1.15em;
|
||||
font-weight: 600;
|
||||
color: var(--secondary-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title i { color: var(--primary-color); }
|
||||
|
||||
.contact-container {
|
||||
max-width: 53.75rem;
|
||||
margin: 2.81rem auto;
|
||||
padding: 0 1.25rem;
|
||||
}
|
||||
|
||||
.contact-card {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.contact-header {
|
||||
background: linear-gradient(135deg, var(--secondary-color), #34495e);
|
||||
padding: 2.81rem 2.50rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.contact-header h1 {
|
||||
font-size: 2em;
|
||||
margin-bottom: 0.50rem;
|
||||
letter-spacing: -0.03rem;
|
||||
}
|
||||
|
||||
.contact-header p {
|
||||
opacity: 0.85;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.contact-body {
|
||||
padding: 2.50rem;
|
||||
}
|
||||
|
||||
.contact-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(17.50rem, 1fr));
|
||||
gap: 1.50rem;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
background: #fafbfc;
|
||||
padding: 1.50rem;
|
||||
border-radius: 0.50rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-item:hover {
|
||||
background: #f4f6f8;
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-0.12rem);
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(52,152,219,0.1);
|
||||
}
|
||||
|
||||
.contact-item .icon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
background: var(--primary-color);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1rem;
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-item:hover .icon {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.contact-item h3 {
|
||||
font-size: 0.95em;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.50rem;
|
||||
}
|
||||
|
||||
.contact-item p {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact-item a {
|
||||
color: var(--primary-color);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
background: #fafbfc;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.social-links a:hover {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-0.12rem);
|
||||
}
|
||||
|
||||
.map-section {
|
||||
margin-top: 1.88rem;
|
||||
background: #fafbfc;
|
||||
border-radius: 0.50rem;
|
||||
padding: 1.25rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.map-section h3 {
|
||||
font-size: 1em;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.94rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.map-section h3 i { color: var(--primary-color); }
|
||||
|
||||
.map-placeholder {
|
||||
height: 12.50rem;
|
||||
background: linear-gradient(135deg, #e8ecf0 0%, #dde3e8 100%);
|
||||
border-radius: 0.38rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.map-placeholder i {
|
||||
font-size: 2em;
|
||||
margin-right: 0.62rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.contact-container { margin: 1.56rem auto; }
|
||||
.contact-header { padding: 1.88rem 1.25rem; }
|
||||
.contact-body { padding: 1.56rem 1.25rem; }
|
||||
.contact-header h1 { font-size: 1.6em; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav-bar">
|
||||
<div class="nav-content">
|
||||
<a href="/" class="back-link">
|
||||
<i class="fas fa-arrow-left"></i> 返回简历
|
||||
</a>
|
||||
<span class="nav-title">
|
||||
<i class="fas fa-envelope"></i> 联系我
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="contact-container">
|
||||
<div class="contact-card">
|
||||
<div class="contact-header">
|
||||
<h1>与我取得联系</h1>
|
||||
<p>期待与您交流,欢迎发送邮件或通过以下方式联系我</p>
|
||||
</div>
|
||||
|
||||
<div class="contact-body">
|
||||
<div class="contact-grid">
|
||||
<div class="contact-item">
|
||||
<div class="icon"><i class="fas fa-envelope"></i></div>
|
||||
<h3>电子邮箱</h3>
|
||||
<p><a href="mailto:{{.BasicInfo.Email}}">{{.BasicInfo.Email}}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="icon"><i class="fas fa-phone"></i></div>
|
||||
<h3>联系电话</h3>
|
||||
<p>{{.BasicInfo.Phone}}</p>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="icon"><i class="fas fa-globe"></i></div>
|
||||
<h3>个人网站</h3>
|
||||
<p><a href="{{.BasicInfo.Website}}" target="_blank">{{.BasicInfo.Website}}</a></p>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="icon"><i class="fas fa-map-marker-alt"></i></div>
|
||||
<h3>所在地</h3>
|
||||
<p>{{.BasicInfo.Location}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="social-links">
|
||||
<a href="#" title="GitHub" target="_blank"><i class="fab fa-github"></i></a>
|
||||
<a href="#" title="LinkedIn" target="_blank"><i class="fab fa-linkedin"></i></a>
|
||||
<a href="#" title="Twitter" target="_blank"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#" title="Stack Overflow" target="_blank"><i class="fab fa-stack-overflow"></i></a>
|
||||
</div>
|
||||
|
||||
{{ if .BasicInfo.Location }}
|
||||
<div class="map-section">
|
||||
<h3><i class="fas fa-map-marker-alt"></i> 工作地点</h3>
|
||||
<div class="map-placeholder">
|
||||
<i class="fas fa-map"></i>
|
||||
<span>{{.BasicInfo.Location}}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,438 @@
|
||||
{{ define "web/elegant" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<meta name="description" content="{{.BasicInfo.Summary}}" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--primary: #1a1a2e;
|
||||
--secondary: #16213e;
|
||||
--accent: #c9a962;
|
||||
--accent-light: #d4bc7e;
|
||||
--text-dark: #1a1a2e;
|
||||
--text-body: #3d3d5c;
|
||||
--text-muted: #7a7a9d;
|
||||
--bg-cream: #faf8f5;
|
||||
--bg-white: #ffffff;
|
||||
--border: #e8e4dc;
|
||||
--shadow: 0 0.12rem 1.25rem rgba(26, 26, 46, 0.06);
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: inherit; }
|
||||
ul { list-style: none; }
|
||||
|
||||
body {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background: var(--bg-cream);
|
||||
color: var(--text-body);
|
||||
line-height: 1.7;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 56.25rem;
|
||||
margin: 3.75rem auto;
|
||||
background: var(--bg-white);
|
||||
box-shadow: var(--shadow);
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.resume-header {
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--secondary) 100%);
|
||||
color: white;
|
||||
padding: 3.75rem 3.75rem 3.12rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.resume-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -10%;
|
||||
width: 25rem;
|
||||
height: 25rem;
|
||||
background: radial-gradient(circle, rgba(201, 169, 98, 0.1) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 6.25rem;
|
||||
height: 6.25rem;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 2.62rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary);
|
||||
flex-shrink: 0;
|
||||
border: 0.19rem solid rgba(201, 169, 98, 0.3);
|
||||
}
|
||||
|
||||
.name-title { flex: 1; }
|
||||
|
||||
.name {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 2.25rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06rem;
|
||||
margin-bottom: 0.50rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1rem;
|
||||
color: var(--accent-light);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.12rem;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.25rem;
|
||||
font-size: 0.81rem;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
.resume-header .contact-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-tag i {
|
||||
color: var(--accent);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-tag a {
|
||||
color: var(--accent-light);
|
||||
}
|
||||
|
||||
.resume-header .meta-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.94rem;
|
||||
margin-top: 0.94rem;
|
||||
font-size: 0.75rem;
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
.resume-header .meta-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.resume-header .meta-tag i {
|
||||
color: var(--accent);
|
||||
font-size: 0.69rem;
|
||||
}
|
||||
|
||||
.resume-body {
|
||||
padding: 3.12rem 3.75rem;
|
||||
}
|
||||
|
||||
.resume-body-inner {
|
||||
display: flex;
|
||||
gap: 2.50rem;
|
||||
}
|
||||
|
||||
.resume-body .sidebar {
|
||||
width: 18.75rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.resume-body .main-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 2.50rem;
|
||||
}
|
||||
|
||||
.section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.50rem;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
width: 2.50rem;
|
||||
height: 2.50rem;
|
||||
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-light) 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
|
||||
.divider {
|
||||
flex: 1;
|
||||
height: 0.06rem;
|
||||
background: linear-gradient(to right, var(--border), transparent);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
background: var(--bg-cream);
|
||||
border-radius: 0.38rem;
|
||||
padding: 1.25rem;
|
||||
border-left: 0.19rem solid var(--accent);
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 1.50rem;
|
||||
padding: 1.25rem;
|
||||
background: var(--bg-white);
|
||||
border-radius: 0.38rem;
|
||||
box-shadow: 0 0.06rem 0.50rem rgba(0,0,0,0.04);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.item:last-child { margin-bottom: 0; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.62rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
font-size: 1rem;
|
||||
}
|
||||
.item-header .period {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.81rem;
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-style: italic;
|
||||
}
|
||||
.item-header a {
|
||||
color: var(--accent);
|
||||
font-size: 0.81rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.position {
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.62rem;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
.degree {
|
||||
color: var(--text-body);
|
||||
margin-bottom: 0.38rem;
|
||||
font-size: 0.81rem;
|
||||
}
|
||||
.gpa {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: var(--text-body);
|
||||
line-height: 1.8;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li {
|
||||
color: var(--text-body);
|
||||
padding: 0.38rem 0;
|
||||
font-size: 0.88rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
.highlights li i {
|
||||
color: var(--accent);
|
||||
font-size: 0.75rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.skill-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.skill-category {
|
||||
background: var(--bg-cream);
|
||||
padding: 1rem;
|
||||
border-radius: 0.38rem;
|
||||
border-left: 0.19rem solid var(--accent);
|
||||
}
|
||||
|
||||
.skill-category-title {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 0.75rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.06rem solid var(--border);
|
||||
}
|
||||
|
||||
.skill-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
font-size: 0.81rem;
|
||||
padding: 0.31rem 0.88rem;
|
||||
background: var(--bg-white);
|
||||
color: var(--text-body);
|
||||
border-radius: 1.25rem;
|
||||
border: 0.06rem solid var(--border);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.skill-tag:hover {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.62rem; }
|
||||
.tech-tag {
|
||||
font-size: 0.69rem;
|
||||
padding: 0.19rem 0.62rem;
|
||||
background: #f0ede8;
|
||||
color: var(--text-muted);
|
||||
border-radius: 0.19rem;
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.50rem; margin-top: 0.62rem; }
|
||||
.platform-tag {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: rgba(201, 169, 98, 0.1);
|
||||
color: var(--accent);
|
||||
border-radius: 0.25rem;
|
||||
border: 0.06rem solid rgba(201, 169, 98, 0.2);
|
||||
}
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: var(--text-body);
|
||||
padding: 0.50rem 0;
|
||||
font-size: 0.88rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.advantage-list li i { color: var(--accent); font-size: 0.88rem; margin-top: 0.12rem; }
|
||||
|
||||
.sidebar-summary {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.9;
|
||||
color: var(--text-body);
|
||||
padding-left: 0.75rem;
|
||||
border-left: 0.19rem solid var(--accent);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.achievements {
|
||||
margin-top: 0.75rem;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 0.06rem dashed var(--border);
|
||||
}
|
||||
|
||||
.achievements h4 {
|
||||
font-family: 'Playfair Display', serif;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 0.50rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.achievements h4 i {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.achievements ul {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.achievements li {
|
||||
font-size: 0.88rem;
|
||||
color: var(--text-body);
|
||||
margin-bottom: 0.31rem;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container { margin: 1.25rem; }
|
||||
.resume-header { padding: 2.50rem 1.88rem; }
|
||||
.header-content { flex-direction: column; text-align: center; }
|
||||
.resume-body { padding: 1.88rem; }
|
||||
.resume-body-inner { flex-direction: column; gap: 1.88rem; }
|
||||
.resume-body .sidebar { width: 100%; }
|
||||
.resume-header .contact-row { justify-content: center; }
|
||||
.resume-header .meta-row { justify-content: center; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,360 @@
|
||||
{{ define "web/fresh" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--bg-primary: #f7f9f7;
|
||||
--bg-secondary: #ffffff;
|
||||
--bg-card: #fefefe;
|
||||
--border-color: #e8ebe8;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #5d6d7e;
|
||||
--text-muted: #95a5a6;
|
||||
--accent-color: #27ae60;
|
||||
--accent-teal: #1abc9c;
|
||||
--accent-blue: #3498db;
|
||||
--accent-orange: #e67e22;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 300 300' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='5' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
||||
opacity: 0.025;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
::selection { background: rgba(39, 174, 96, 0.25); }
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: white;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
padding: 0.44rem 0.88rem;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
box-shadow: 0 0.06rem 0.25rem rgba(0,0,0,0.04);
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(39,174,96,0.2);
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 51.25rem;
|
||||
margin: 2.50rem auto;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 0.12rem 1rem rgba(0,0,0,0.04);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header.resume-header {
|
||||
background: linear-gradient(180deg, #e8f5e9 0%, #f7f9f7 100%);
|
||||
padding: 2.81rem 2.50rem;
|
||||
text-align: center;
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
}
|
||||
header.resume-header .avatar img {
|
||||
width: 8.12rem;
|
||||
height: 8.12rem;
|
||||
border-radius: 50%;
|
||||
border: 0.25rem solid white;
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0.25rem 1rem rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
header.resume-header .basic-info { display: flex; flex-direction: column; gap: 0.88rem; align-items: center; }
|
||||
header.resume-header .basic-info .info-header { text-align: center; }
|
||||
header.resume-header .basic-info .name { font-size: 2em; font-weight: 700; margin-bottom: 0.50rem; color: var(--text-primary); line-height: 1.2; }
|
||||
header.resume-header .basic-info .title { font-size: 0.95em; color: var(--text-secondary); font-weight: 400; }
|
||||
|
||||
.contact-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
.contact-tag {
|
||||
background: white;
|
||||
padding: 0.31rem 0.88rem;
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.84em;
|
||||
color: var(--text-secondary);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.38rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.contact-tag:hover { border-color: var(--accent-color); color: var(--accent-color); }
|
||||
.contact-tag a { color: var(--text-secondary); text-decoration: none; position: relative; }
|
||||
.contact-tag a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: var(--accent-color);
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.contact-tag a:hover::after { width: 100%; }
|
||||
.contact-tag a:hover { color: var(--accent-color); }
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.50rem;
|
||||
font-size: 0.8em;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.meta-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.19rem 0.62rem;
|
||||
background: rgba(255,255,255,0.5);
|
||||
border-radius: 0.12rem;
|
||||
white-space: nowrap;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
.meta-tag i { width: 0.75rem; text-align: center; opacity: 0.5; }
|
||||
|
||||
header.resume-header .basic-info .summary {
|
||||
margin-top: 0.12rem;
|
||||
padding: 0.88rem 1.12rem;
|
||||
background: rgba(255,255,255,0.6);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.88em;
|
||||
line-height: 1.8;
|
||||
color: var(--text-secondary);
|
||||
border-left: 0.19rem solid var(--accent-color);
|
||||
}
|
||||
|
||||
main.resume-body { padding: 2.62rem; }
|
||||
|
||||
section { margin-bottom: 2.50rem; }
|
||||
section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-title {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.25rem;
|
||||
padding-bottom: 0.75rem;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.62rem;
|
||||
letter-spacing: 0.02rem;
|
||||
}
|
||||
.section-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 2.81rem;
|
||||
height: 0.19rem;
|
||||
background: var(--accent-color);
|
||||
border-radius: 0.12rem;
|
||||
}
|
||||
.section-title i { color: var(--accent-color); font-size: 0.9em; }
|
||||
|
||||
.section-content { display: flex; flex-direction: column; gap: 1.12rem; }
|
||||
|
||||
article.item {
|
||||
background: var(--bg-card);
|
||||
padding: 1.38rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
}
|
||||
article.item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 0.19rem;
|
||||
height: 100%;
|
||||
background: var(--accent-color);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
border-radius: 0.12rem 0 0 0.12rem;
|
||||
}
|
||||
article.item:hover {
|
||||
border-color: var(--accent-color);
|
||||
box-shadow: 0 0.12rem 0.75rem rgba(39,174,96,0.06);
|
||||
transform: translateY(-0.12rem);
|
||||
}
|
||||
article.item:hover::before { opacity: 1; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.62rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: var(--text-primary); font-size: 1em; }
|
||||
.item-header .period { color: var(--text-muted); font-size: 0.82em; }
|
||||
.item-header a { color: var(--accent-color); font-size: 0.95em; transition: color 0.3s; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: var(--accent-color);
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
.item-header a:hover { color: #1e8449; }
|
||||
|
||||
.position { color: var(--accent-color); font-weight: 500; margin-bottom: 0.62rem; }
|
||||
.degree { color: var(--text-secondary); margin-bottom: 0.31rem; }
|
||||
.gpa { color: var(--text-muted); font-size: 0.82em; }
|
||||
|
||||
.description { color: var(--text-secondary); line-height: 1.65; margin-bottom: 0.62rem; font-size: 0.92em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: var(--text-secondary); padding: 0.38rem 0; font-size: 0.9em; display: flex; align-items: flex-start; gap: 0.62rem; line-height: 1.6; }
|
||||
.highlights li i { color: var(--accent-teal); font-size: 0.82em; margin-top: 0.19rem; }
|
||||
|
||||
.skill-items { display: flex; flex-wrap: wrap; gap: 0.62rem; }
|
||||
.skill-tag {
|
||||
padding: 0.38rem 0.88rem;
|
||||
background: rgba(39,174,96,0.08);
|
||||
border: 0.06rem solid rgba(39,174,96,0.2);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.88em;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.skill-tag:hover {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border-color: var(--accent-color);
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.50rem; margin-top: 0.62rem; }
|
||||
.tech-tag {
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: rgba(52,152,219,0.1);
|
||||
color: var(--accent-blue);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.82em;
|
||||
border: 0.06rem solid rgba(52,152,219,0.2);
|
||||
}
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.50rem; }
|
||||
.platform-tag {
|
||||
padding: 0.19rem 0.62rem;
|
||||
background: rgba(230,126,34,0.1);
|
||||
color: var(--accent-orange);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.78em;
|
||||
border: 0.06rem solid rgba(230,126,34,0.2);
|
||||
}
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: var(--text-secondary);
|
||||
padding: 0.50rem 0;
|
||||
font-size: 0.92em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.advantage-list li i { color: var(--accent-color); font-size: 0.85em; margin-top: 0.12rem; }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container { margin: 0.94rem; }
|
||||
header.resume-header { padding: 2rem 1.38rem; }
|
||||
main.resume-body { padding: 1.75rem 1.38rem; }
|
||||
.item-header { flex-direction: column; gap: 0.38rem; }
|
||||
.contact-row { gap: 0.62rem; }
|
||||
.contact-item { padding: 0.44rem 0.88rem; font-size: 0.82em; }
|
||||
article.item:hover { transform: translateY(0); }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
body::before { display: none; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border: 0.06rem solid #ddd;
|
||||
}
|
||||
header.resume-header {
|
||||
background: #f5f5f5;
|
||||
border-bottom: 0.12rem solid var(--accent-color);
|
||||
}
|
||||
article.item {
|
||||
background: #fafafa;
|
||||
border: 0.06rem solid #eee;
|
||||
border-left: 0.19rem solid var(--accent-color);
|
||||
}
|
||||
article.item:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.contact-item { background: transparent; border: 0.06rem solid #ddd; }
|
||||
a { color: var(--accent-color); text-decoration: underline; }
|
||||
@page { margin: 15mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,651 @@
|
||||
{{ define "web/home" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{{ .SystemConfig.WebsiteDesc }}" />
|
||||
<meta name="keywords" content="{{ .SystemConfig.WebsiteKeywords }}" />
|
||||
<meta property="og:title" content="{{ .SystemConfig.WebsiteTitle }}" />
|
||||
<meta property="og:description" content="{{ .SystemConfig.WebsiteDesc }}" />
|
||||
<meta property="og:url" content="{{ .SystemConfig.WebsiteDomain }}" />
|
||||
<title>{{ .SystemConfig.WebsiteTitle }}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
:root {
|
||||
--primary: #6D8B94;
|
||||
--secondary: #A8B896;
|
||||
--accent: #E3E5E0;
|
||||
--text: #333333;
|
||||
--text-light: #666666;
|
||||
--bg: #FFFFFF;
|
||||
--border: #E8E8E8;
|
||||
--radius: 0.50rem;
|
||||
}
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; color: var(--text); background: var(--bg); -webkit-font-smoothing: antialiased; }
|
||||
|
||||
.nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
background: rgba(255,255,255,0.95);
|
||||
backdrop-filter: blur(0.50rem);
|
||||
border-bottom: 0.06rem solid var(--border);
|
||||
}
|
||||
.nav-inner {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
padding: 1rem 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
}
|
||||
.logo-icon {
|
||||
width: 2.25rem;
|
||||
height: 2.25rem;
|
||||
border-radius: var(--radius);
|
||||
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.logo-text { font-size: 1.15rem; font-weight: 500; letter-spacing: 0.12rem; }
|
||||
.nav-links { display: flex; gap: 2.25rem; }
|
||||
.nav-links a { text-decoration: none; color: var(--text-light); font-size: 0.9rem; transition: color 0.2s; }
|
||||
.nav-links a:hover { color: var(--primary); }
|
||||
|
||||
.hero {
|
||||
padding: 8.75rem 2rem 7.50rem;
|
||||
text-align: center;
|
||||
background: linear-gradient(180deg, var(--bg) 0%, var(--accent) 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -10%;
|
||||
width: 31.25rem;
|
||||
height: 31.25rem;
|
||||
background: radial-gradient(circle, rgba(109,139,148,0.08) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.hero::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
right: -10%;
|
||||
width: 25rem;
|
||||
height: 25rem;
|
||||
background: radial-gradient(circle, rgba(168,184,150,0.08) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.hero-title {
|
||||
font-size: 3.5rem;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.50rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text);
|
||||
}
|
||||
.hero-subtitle {
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-light);
|
||||
margin-bottom: 0.75rem;
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
.hero-desc {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-light);
|
||||
margin-bottom: 3rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
padding: 0.75rem 2rem;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border-radius: var(--radius);
|
||||
text-decoration: none;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.btn:hover {
|
||||
background: #5A757D;
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.hero-stats {
|
||||
max-width: 37.50rem;
|
||||
margin: 3rem auto 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.hero-stat-item {
|
||||
text-align: center;
|
||||
}
|
||||
.hero-stat-number {
|
||||
font-size: 2rem;
|
||||
font-weight: 300;
|
||||
color: var(--primary);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.hero-stat-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-light);
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
|
||||
.intro {
|
||||
padding: 3.75rem 2rem;
|
||||
background: var(--bg);
|
||||
text-align: center;
|
||||
}
|
||||
.intro-grid {
|
||||
max-width: 56.25rem;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(12.50rem, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
.intro-item {
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.intro-item i {
|
||||
display: block;
|
||||
font-size: 1.5rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.resumes {
|
||||
padding: 5rem 2rem;
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.section-title {
|
||||
text-align: center;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
margin-bottom: 3rem;
|
||||
color: var(--text);
|
||||
}
|
||||
.resume-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(17.50rem, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
.resume-card {
|
||||
background: var(--bg);
|
||||
border: 0.06rem solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.75rem;
|
||||
text-decoration: none;
|
||||
color: var(--text);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.resume-card:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0.50rem 1.50rem rgba(109, 139, 148, 0.12);
|
||||
transform: translateY(-0.12rem);
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.88rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.card-avatar {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.card-name { font-size: 1.1rem; font-weight: 500; margin-bottom: 0.12rem; }
|
||||
.card-title { font-size: 0.85rem; color: var(--text-light); }
|
||||
.card-summary {
|
||||
font-size: 0.88rem;
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.no-resumes {
|
||||
text-align: center;
|
||||
padding: 3.75rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
.no-resumes i { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; }
|
||||
|
||||
.templates-section {
|
||||
padding: 5rem 2rem;
|
||||
background: var(--bg);
|
||||
}
|
||||
.section-subtitle {
|
||||
text-align: center;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-light);
|
||||
margin-bottom: 3rem;
|
||||
margin-top: -2rem;
|
||||
}
|
||||
.templates-grid {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(17.50rem, 1fr));
|
||||
gap: 1.50rem;
|
||||
}
|
||||
.template-card {
|
||||
background: var(--bg);
|
||||
border: 0.06rem solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.template-card:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0.50rem 1.50rem rgba(109, 139, 148, 0.12);
|
||||
transform: translateY(-0.19rem);
|
||||
}
|
||||
.template-preview {
|
||||
height: 11.25rem;
|
||||
background: #fafafa;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.template-name {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
border-top: 0.06rem solid var(--border);
|
||||
}
|
||||
.preview-line {
|
||||
height: 0.38rem;
|
||||
background: #e0e0e0;
|
||||
border-radius: 0.19rem;
|
||||
margin-bottom: 0.50rem;
|
||||
}
|
||||
.preview-line.long { width: 90%; }
|
||||
.preview-line.medium { width: 65%; }
|
||||
.preview-line.short { width: 40%; }
|
||||
.preview-line.dark { background: #4a5568; }
|
||||
|
||||
.modern-preview .preview-header {
|
||||
height: 3.12rem;
|
||||
background: #2c3e50;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.modern-preview .preview-body { padding: 0 1rem; }
|
||||
|
||||
.tech-preview {
|
||||
display: flex;
|
||||
background: #1a1a2e;
|
||||
}
|
||||
.tech-preview .preview-sidebar {
|
||||
width: 5rem;
|
||||
background: #16213e;
|
||||
}
|
||||
.tech-preview .preview-content {
|
||||
flex: 1;
|
||||
padding: 1.25rem 1rem;
|
||||
}
|
||||
|
||||
.sidebar-preview { display: flex; background: #f5f7fa; }
|
||||
.sidebar-preview .preview-left {
|
||||
width: 5.62rem;
|
||||
background: #2c3e50;
|
||||
}
|
||||
.sidebar-preview .preview-right {
|
||||
flex: 1;
|
||||
padding: 1.25rem 1rem;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.elegant-preview .preview-top {
|
||||
height: 3.75rem;
|
||||
background: linear-gradient(135deg, #1a1a2e, #16213e);
|
||||
}
|
||||
.elegant-preview .preview-bottom {
|
||||
padding: 1rem;
|
||||
background: #faf8f5;
|
||||
}
|
||||
|
||||
.classic-preview .preview-classic-header {
|
||||
height: 3.75rem;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 0.12rem solid #333;
|
||||
}
|
||||
.classic-preview .preview-body { padding: 1rem; }
|
||||
|
||||
.fresh-preview .preview-fresh-header {
|
||||
height: 3.12rem;
|
||||
background: linear-gradient(135deg, #a8e6cf, #88d8b0);
|
||||
}
|
||||
.fresh-preview .preview-body { padding: 1rem; }
|
||||
|
||||
.features {
|
||||
padding: 5rem 2rem;
|
||||
background: var(--accent);
|
||||
}
|
||||
.features-grid {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(15.62rem, 1fr));
|
||||
gap: 1.50rem;
|
||||
}
|
||||
.feature-card {
|
||||
background: var(--bg);
|
||||
border: 0.06rem solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.75rem;
|
||||
text-align: center;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
.feature-card:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0.50rem 1.50rem rgba(109, 139, 148, 0.12);
|
||||
transform: translateY(-0.12rem);
|
||||
}
|
||||
.feature-icon {
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
border-radius: var(--radius);
|
||||
background: linear-gradient(135deg, var(--primary), var(--secondary));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.3rem;
|
||||
margin: 0 auto 1rem;
|
||||
}
|
||||
.feature-card h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.50rem;
|
||||
color: var(--text);
|
||||
}
|
||||
.feature-card p {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-light);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 2.50rem 2rem;
|
||||
border-top: 0.06rem solid var(--border);
|
||||
text-align: center;
|
||||
}
|
||||
.footer-brand {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.50rem;
|
||||
letter-spacing: 0.12rem;
|
||||
}
|
||||
.footer-links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 1.50rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
.footer-links a {
|
||||
color: var(--text-light);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.footer-links a:hover { color: var(--primary); }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.nav-links { gap: 1.25rem; }
|
||||
.hero-title { font-size: 2.5rem; letter-spacing: 0.25rem; }
|
||||
.hero-subtitle { font-size: 1rem; }
|
||||
.resume-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="nav-inner">
|
||||
<a href="/" class="logo">
|
||||
<div class="logo-icon">
|
||||
{{ if .SystemConfig.LogoPath }}
|
||||
<img src="{{.SystemConfig.LogoPath}}" alt="Logo" style="width:1.75rem;height:1.75rem;border-radius:0.38rem;object-fit:cover;">
|
||||
{{ else }}
|
||||
<i class="fas fa-file-alt"></i>
|
||||
{{ end }}
|
||||
</div>
|
||||
<span class="logo-text">{{ .SystemConfig.WebsiteTitle }}</span>
|
||||
</a>
|
||||
<div class="nav-links">
|
||||
<a href="/">首页</a>
|
||||
<a href="#resumes">全部简历</a>
|
||||
<a href="#about">关于项目</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<h1 class="hero-title">{{ .SystemConfig.WebsiteTitle }}</h1>
|
||||
<p class="hero-subtitle">{{ .SystemConfig.WebsiteDesc }}</p>
|
||||
<p class="hero-desc">开源、轻量、优雅的简历展示平台</p>
|
||||
<a href="#resumes" class="btn">
|
||||
<i class="fas fa-eye"></i> 浏览简历
|
||||
</a>
|
||||
<div class="hero-stats">
|
||||
<div class="hero-stat-item">
|
||||
<div class="hero-stat-number">{{.Stats.TotalResumes}}</div>
|
||||
<div class="hero-stat-label">份简历</div>
|
||||
</div>
|
||||
<div class="hero-stat-item">
|
||||
<div class="hero-stat-number">{{.Stats.TotalUsers}}</div>
|
||||
<div class="hero-stat-label">位用户</div>
|
||||
</div>
|
||||
<div class="hero-stat-item">
|
||||
<div class="hero-stat-number">8+</div>
|
||||
<div class="hero-stat-label">种模板</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="features">
|
||||
<h2 class="section-title">功能特性</h2>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-file-alt"></i></div>
|
||||
<h3>精美简历模板</h3>
|
||||
<p>多种专业模板,支持自定义主题,打造独特个人主页</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-shield-alt"></i></div>
|
||||
<h3>访问控制</h3>
|
||||
<p>支持密码保护,灵活控制简历可见范围,保护隐私</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-mobile-alt"></i></div>
|
||||
<h3>响应式设计</h3>
|
||||
<p>适配各种设备,随时随地展示你的专业风采</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-search"></i></div>
|
||||
<h3>SEO优化</h3>
|
||||
<p>内置搜索引擎优化,让更多人发现你的简历</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-robot"></i></div>
|
||||
<h3>AI智能辅助</h3>
|
||||
<p>AI润色简历、生成面试题,提升求职成功率</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon"><i class="fas fa-code"></i></div>
|
||||
<h3>开源免费</h3>
|
||||
<p>完全开源,轻量高效,支持二次开发</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="templates-section">
|
||||
<h2 class="section-title">多种精美模板</h2>
|
||||
<p class="section-subtitle">8款风格各异的专业模板,一键切换,打造独特的个人简历</p>
|
||||
<div class="templates-grid">
|
||||
<div class="template-card">
|
||||
<div class="template-preview modern-preview">
|
||||
<div class="preview-header"></div>
|
||||
<div class="preview-body">
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line short"></div>
|
||||
<div class="preview-line medium"></div>
|
||||
<div class="preview-line long"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">现代商务</div>
|
||||
</div>
|
||||
<div class="template-card">
|
||||
<div class="template-preview tech-preview">
|
||||
<div class="preview-sidebar"></div>
|
||||
<div class="preview-content">
|
||||
<div class="preview-line long dark"></div>
|
||||
<div class="preview-line short dark"></div>
|
||||
<div class="preview-line medium dark"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">科技极客</div>
|
||||
</div>
|
||||
<div class="template-card">
|
||||
<div class="template-preview sidebar-preview">
|
||||
<div class="preview-left"></div>
|
||||
<div class="preview-right">
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line medium"></div>
|
||||
<div class="preview-line short"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">经典侧边栏</div>
|
||||
</div>
|
||||
<div class="template-card">
|
||||
<div class="template-preview elegant-preview">
|
||||
<div class="preview-top"></div>
|
||||
<div class="preview-bottom">
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line medium"></div>
|
||||
<div class="preview-line long"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">优雅轻奢</div>
|
||||
</div>
|
||||
<div class="template-card">
|
||||
<div class="template-preview classic-preview">
|
||||
<div class="preview-classic-header"></div>
|
||||
<div class="preview-body">
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line medium"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">传统经典</div>
|
||||
</div>
|
||||
<div class="template-card">
|
||||
<div class="template-preview fresh-preview">
|
||||
<div class="preview-fresh-header"></div>
|
||||
<div class="preview-body">
|
||||
<div class="preview-line long"></div>
|
||||
<div class="preview-line short"></div>
|
||||
<div class="preview-line medium"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="template-name">清新自然</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="resumes" class="resumes">
|
||||
<h2 class="section-title">简历展示</h2>
|
||||
<div class="resume-grid">
|
||||
{{ $visibleResumes := slice }}
|
||||
{{ range .Resumes }}
|
||||
{{ if .ShowInHome }}
|
||||
{{ $visibleResumes = append $visibleResumes . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $limitedResumes := $visibleResumes }}
|
||||
{{ if gt (len $limitedResumes) 9 }}
|
||||
{{ $limitedResumes = first 9 $limitedResumes }}
|
||||
{{ end }}
|
||||
|
||||
{{ range $limitedResumes }}
|
||||
<a href="/{{ .Route }}" class="resume-card">
|
||||
<div class="card-header">
|
||||
<div class="card-avatar">
|
||||
{{ if .BasicInfo.Name }}{{ substr .BasicInfo.Name 0 1 }}{{ else }}简{{ end }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="card-name">{{ .BasicInfo.Name }}</div>
|
||||
<div class="card-title">{{ .BasicInfo.Title }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="card-summary">{{ .BasicInfo.Summary }}</p>
|
||||
</a>
|
||||
{{ else }}
|
||||
<div class="no-resumes" style="grid-column: 1 / -1;">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
<p>暂无公开简历</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ if gt (len $visibleResumes) 9 }}
|
||||
<div style="text-align: center; margin-top: 2rem;">
|
||||
<span class="text-sm text-slate-500">仅展示前9个简历,共{{ len $visibleResumes }}个</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
<section id="about" class="resumes" style="padding-top: 0;">
|
||||
<h2 class="section-title">关于项目</h2>
|
||||
<div style="text-align: center; color: var(--text-light); line-height: 1.8; max-width: 37.50rem; margin: 0 auto;">
|
||||
<p>景笺是一个开源的简历展示平台,致力于提供极简、优雅的简历浏览体验。</p>
|
||||
<p style="margin-top: 0.75rem;">前台仅用于展示简历,无任何登录入口;后台独立私密,仅供管理员使用。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="footer-brand">{{ .SystemConfig.WebsiteTitle }}</div>
|
||||
<div class="footer-links">
|
||||
<span>© 2026 开源简历平台</span>
|
||||
<a href="https://github.com/resume-platform/resume-platform" target="_blank">GitHub</a>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,295 @@
|
||||
{{ define "web/minimalist" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', sans-serif;
|
||||
background: #fafafa;
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: transparent;
|
||||
border: 0.06rem solid #ddd;
|
||||
color: #666;
|
||||
padding: 0.38rem 0.75rem;
|
||||
border-radius: 0.12rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: #111;
|
||||
color: white;
|
||||
border-color: #111;
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 43.75rem;
|
||||
margin: 4.38rem auto;
|
||||
background: white;
|
||||
padding: 3.75rem 3.12rem;
|
||||
border-top: 0.19rem solid #111;
|
||||
box-shadow: 0 0.06rem 0.25rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
header.resume-header { text-align: left; margin-bottom: 3.44rem; padding-bottom: 2.19rem; border-bottom: 0.06rem solid #eee; }
|
||||
header.resume-header .avatar img {
|
||||
width: 5.62rem;
|
||||
height: 5.62rem;
|
||||
border-radius: 0.12rem;
|
||||
object-fit: cover;
|
||||
border: 0.06rem solid #e0e0e0;
|
||||
}
|
||||
header.resume-header .basic-info { display: flex; flex-direction: column; gap: 0.88rem; }
|
||||
header.resume-header .basic-info .info-header { margin-bottom: 0.12rem; }
|
||||
header.resume-header .basic-info .name { font-size: 2.3em; font-weight: 300; margin-bottom: 0.38rem; color: #111; letter-spacing: -0.06rem; line-height: 1.2; }
|
||||
header.resume-header .basic-info .title { font-size: 0.9em; color: #999; letter-spacing: 0.19rem; text-transform: uppercase; font-weight: 400; }
|
||||
|
||||
header.resume-header .basic-info .contact-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.88rem;
|
||||
font-size: 0.84em;
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
color: #555;
|
||||
padding: 0.25rem 0;
|
||||
white-space: nowrap;
|
||||
border-bottom: 0.06rem solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag:hover {
|
||||
color: #111;
|
||||
border-bottom-color: #ccc;
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag i { width: 0.88rem; text-align: center; opacity: 0.5; }
|
||||
header.resume-header .basic-info .contact-tag a { color: #555; text-decoration: none; position: relative; }
|
||||
header.resume-header .basic-info .contact-tag a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #111;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag a:hover::after { width: 100%; }
|
||||
header.resume-header .basic-info .contact-tag a:hover { color: #111; }
|
||||
|
||||
header.resume-header .basic-info .meta-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.8em;
|
||||
color: #777;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag i { width: 0.75rem; text-align: center; opacity: 0.4; }
|
||||
|
||||
main.resume-body { padding-top: 0.50rem; }
|
||||
|
||||
section { margin-bottom: 3.12rem; }
|
||||
section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-title {
|
||||
font-size: 0.82em;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
letter-spacing: 0.12rem;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.56rem;
|
||||
position: relative;
|
||||
}
|
||||
.section-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 5rem;
|
||||
right: 0;
|
||||
height: 0.06rem;
|
||||
background: #eee;
|
||||
}
|
||||
.section-title i { margin-right: 0.62rem; color: #111; font-size: 0.9em; }
|
||||
|
||||
.section-content .item { margin-bottom: 2rem; }
|
||||
.section-content .item:last-child { margin-bottom: 0; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.38rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 500; color: #111; font-size: 1em; }
|
||||
.item-header .period { color: #bbb; font-size: 0.82em; }
|
||||
.item-header a { color: #666; font-size: 0.9em; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #111;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
.item-header a:hover { color: #111; }
|
||||
|
||||
.position { color: #666; font-size: 0.88em; margin-bottom: 0.75rem; font-weight: 400; }
|
||||
.degree { color: #666; font-size: 0.88em; margin-bottom: 0.31rem; }
|
||||
.gpa { color: #bbb; font-size: 0.82em; }
|
||||
|
||||
.description { color: #555; line-height: 1.75; margin-bottom: 0.75rem; font-size: 0.92em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li {
|
||||
color: #555;
|
||||
padding: 0.38rem 0;
|
||||
font-size: 0.9em;
|
||||
position: relative;
|
||||
padding-left: 1.12rem;
|
||||
line-height: 1.65;
|
||||
transition: padding-left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.highlights li:hover { padding-left: 1.38rem; }
|
||||
.highlights li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0.69rem;
|
||||
width: 0.19rem;
|
||||
height: 0.19rem;
|
||||
background: #111;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.highlights li:hover::before { transform: scale(1.5); }
|
||||
|
||||
.skill-items { display: flex; flex-wrap: wrap; gap: 1.12rem; }
|
||||
.skill-tag {
|
||||
padding: 0;
|
||||
font-size: 0.92em;
|
||||
color: #555;
|
||||
border-bottom: 0.06rem solid #ddd;
|
||||
padding-bottom: 0.12rem;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.skill-tag:hover { border-color: #111; color: #111; }
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.88rem; }
|
||||
.tech-tag {
|
||||
padding: 0;
|
||||
font-size: 0.82em;
|
||||
color: #888;
|
||||
border-bottom: 0.06rem solid #eee;
|
||||
padding-bottom: 0.12rem;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.tech-tag:hover { border-color: #111; color: #111; }
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.62rem; margin-top: 0.50rem; }
|
||||
.platform-tag {
|
||||
padding: 0;
|
||||
font-size: 0.78em;
|
||||
color: #999;
|
||||
border-bottom: 0.06rem solid #f0f0f0;
|
||||
padding-bottom: 0.06rem;
|
||||
}
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: #555;
|
||||
padding: 0.50rem 0;
|
||||
font-size: 0.92em;
|
||||
position: relative;
|
||||
padding-left: 1.25rem;
|
||||
line-height: 1.7;
|
||||
transition: padding-left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.advantage-list li:hover { padding-left: 1.50rem; }
|
||||
.advantage-list li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0.81rem;
|
||||
width: 0.25rem;
|
||||
height: 0.25rem;
|
||||
background: #111;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.advantage-list li:hover::before { transform: scale(1.5); }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container { margin: 1.25rem; padding: 2.50rem 1.88rem; }
|
||||
header.resume-header .basic-info .contact { gap: 0.75rem; }
|
||||
.item-header { flex-direction: column; gap: 0.31rem; }
|
||||
.section-title::after { display: none; }
|
||||
header.resume-header { text-align: center; }
|
||||
.highlights li:hover { padding-left: 1.12rem; }
|
||||
.advantage-list li:hover { padding-left: 1.25rem; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-top: 0.19rem solid #111;
|
||||
box-shadow: none;
|
||||
}
|
||||
header.resume-header {
|
||||
padding-bottom: 1.56rem;
|
||||
margin-bottom: 2.50rem;
|
||||
}
|
||||
a { color: #333; text-decoration: underline; }
|
||||
.highlights li:hover { padding-left: 1.12rem; }
|
||||
.advantage-list li:hover { padding-left: 1.25rem; }
|
||||
@page { margin: 20mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,491 @@
|
||||
{{ define "web/modern" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<meta name="description" content="{{.BasicInfo.Summary}}" />
|
||||
<meta name="keywords" content="{{.BasicInfo.Title}}, {{.BasicInfo.Name}}, 简历, 作品集, {{range .Skills}}{{.Name}}, {{end}}" />
|
||||
<meta name="author" content="{{.BasicInfo.Name}}" />
|
||||
<meta property="og:title" content="{{.BasicInfo.Name}} - {{.BasicInfo.Title}}" />
|
||||
<meta property="og:description" content="{{.BasicInfo.Summary}}" />
|
||||
<meta property="og:type" content="profile" />
|
||||
<meta property="og:url" content="https://zhangsan.dev" />
|
||||
<link rel="canonical" href="https://zhangsan.dev" />
|
||||
<link rel="alternate" href="https://zhangsan.dev" hreflang="zh-CN" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--primary-dark: #2980b9;
|
||||
--secondary-color: #2c3e50;
|
||||
--accent-green: #27ae60;
|
||||
--accent-orange: #e67e22;
|
||||
--accent-purple: #9b59b6;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #555;
|
||||
--text-muted: #888;
|
||||
--bg-light: #f0f2f5;
|
||||
--bg-card: #ffffff;
|
||||
--border-color: #dde3e8;
|
||||
--shadow-sm: 0 0.06rem 0.19rem rgba(0,0,0,0.08);
|
||||
--shadow-md: 0 0.25rem 1.50rem rgba(0,0,0,0.06);
|
||||
--shadow-lg: 0 0.50rem 2rem rgba(0,0,0,0.08);
|
||||
--radius-sm: 0.19rem;
|
||||
--radius-md: 0.38rem;
|
||||
--radius-lg: 0.75rem;
|
||||
--transition-fast: 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-normal: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-slow: 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: var(--bg-light);
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
||||
opacity: 0.03;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: white;
|
||||
border: 0.06rem solid #d0d0d0;
|
||||
color: #444;
|
||||
padding: 0.44rem 0.88rem;
|
||||
border-radius: 0.38rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
box-shadow: 0 0.06rem 0.19rem rgba(0,0,0,0.08);
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
border-color: #2c3e50;
|
||||
transform: translateY(-0.06rem);
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 53.75rem;
|
||||
margin: 2.81rem auto;
|
||||
background: white;
|
||||
box-shadow: 0 0.25rem 1.50rem rgba(0,0,0,0.06);
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header.resume-header {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
padding: 2.81rem 2.50rem;
|
||||
color: white;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
header.resume-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -20%;
|
||||
width: 18.75rem;
|
||||
height: 18.75rem;
|
||||
background: radial-gradient(circle, rgba(52, 152, 219, 0.15) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
header.resume-header::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 0.19rem;
|
||||
background: linear-gradient(90deg, #3498db, #9b59b6, #3498db);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
.header-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 100% 1fr;
|
||||
gap: 1.56rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
header.resume-header .avatar img {
|
||||
width: 7.50rem;
|
||||
height: 7.50rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 0.19rem solid rgba(255,255,255,0.2);
|
||||
object-fit: cover;
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
header.resume-header .basic-info { display: flex; flex-direction: column; gap: 0.75rem; }
|
||||
header.resume-header .basic-info .info-header { margin-bottom: 0.12rem; }
|
||||
header.resume-header .basic-info .name { font-size: 2em; font-weight: 700; margin-bottom: 0.38rem; letter-spacing: -0.03rem; line-height: 1.2; }
|
||||
header.resume-header .basic-info .title { font-size: 1em; opacity: 0.85; }
|
||||
|
||||
header.resume-header .basic-info .contact-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.86em;
|
||||
opacity: 0.92;
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
padding: 0.31rem 0.75rem;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border-radius: 0.19rem;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s ease;
|
||||
border: 0.06rem solid rgba(255,255,255,0.08);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag:hover {
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-color: rgba(255,255,255,0.15);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag i { width: 0.88rem; text-align: center; opacity: 0.75; }
|
||||
header.resume-header .basic-info .contact-tag a { color: white; text-decoration: none; position: relative; }
|
||||
header.resume-header .basic-info .contact-tag a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: white;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
header.resume-header .basic-info .contact-tag a:hover::after { width: 100%; }
|
||||
|
||||
header.resume-header .basic-info .meta-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.62rem;
|
||||
font-size: 0.82em;
|
||||
opacity: 0.78;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.31rem;
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 0.12rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
header.resume-header .basic-info .meta-tag i { width: 0.75rem; text-align: center; opacity: 0.5; }
|
||||
|
||||
header.resume-header .basic-info .summary {
|
||||
margin-top: 0.12rem;
|
||||
padding: 0.88rem 1.12rem;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.88em;
|
||||
line-height: 1.8;
|
||||
border-left: 0.19rem solid #3498db;
|
||||
}
|
||||
|
||||
main.resume-body { padding: 2.38rem 2.50rem; }
|
||||
|
||||
section {
|
||||
margin-bottom: 2.25rem;
|
||||
opacity: 0;
|
||||
transform: translateY(1.25rem);
|
||||
animation: slideUp 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
section:nth-child(1) { animation-delay: 0.1s; }
|
||||
section:nth-child(2) { animation-delay: 0.2s; }
|
||||
section:nth-child(3) { animation-delay: 0.3s; }
|
||||
section:nth-child(4) { animation-delay: 0.4s; }
|
||||
section:nth-child(5) { animation-delay: 0.5s; }
|
||||
section:nth-child(6) { animation-delay: 0.6s; }
|
||||
|
||||
section:last-child { margin-bottom: 0; }
|
||||
|
||||
@keyframes slideUp {
|
||||
from { opacity: 0; transform: translateY(1.25rem); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1.12rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.12rem solid #3498db;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.62rem;
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
.section-title i { color: #3498db; font-size: 0.9em; }
|
||||
|
||||
.section-content .item {
|
||||
margin-bottom: 1.50rem;
|
||||
padding: 1.25rem;
|
||||
background: #fafbfc;
|
||||
border-radius: 0.25rem;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border-left: 0.19rem solid transparent;
|
||||
}
|
||||
.section-content .item:hover {
|
||||
background: #f4f6f8;
|
||||
border-left-color: #3498db;
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(0,0,0,0.04);
|
||||
transform: translateX(0.25rem);
|
||||
}
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.50rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: #2c3e50; font-size: 1em; }
|
||||
.item-header .period { color: #888; font-size: 0.85em; }
|
||||
.item-header a { color: #3498db; font-size: 1em; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #3498db;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
|
||||
.position { color: #3498db; font-weight: 500; margin-bottom: 0.62rem; }
|
||||
.degree { color: #555; margin-bottom: 0.31rem; }
|
||||
.gpa { color: #888; font-size: 0.9em; }
|
||||
|
||||
.description { color: #555; line-height: 1.6; margin-bottom: 0.62rem; font-size: 0.95em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: #444; padding: 0.31rem 0; font-size: 0.95em; display: flex; align-items: flex-start; gap: 0.50rem; }
|
||||
.highlights li i { color: #27ae60; font-size: 0.85em; margin-top: 0.25rem; }
|
||||
|
||||
.skill-section {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(17.50rem, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.skill-category {
|
||||
background: #fafbfc;
|
||||
padding: 1.12rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.skill-category-title {
|
||||
font-size: 0.85em;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03rem;
|
||||
margin-bottom: 0.88rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.skill-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
padding: 0.38rem 0.88rem;
|
||||
background: #e8ecf0;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 0.88em;
|
||||
color: #4a5568;
|
||||
transition: var(--transition-normal);
|
||||
border: 0.06rem solid var(--border-color);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skill-tag::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 0.12rem;
|
||||
background: var(--primary-color);
|
||||
transition: var(--transition-normal);
|
||||
}
|
||||
|
||||
.skill-tag:hover {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
border-color: var(--primary-color);
|
||||
transform: translateY(-0.06rem);
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(52,152,219,0.3);
|
||||
}
|
||||
|
||||
.skill-tag:hover::before {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.skill-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skill-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.skill-progress-item {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.skill-progress-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.skill-progress-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 0.38rem;
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
.skill-progress-name {
|
||||
color: var(--text-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.skill-progress-level {
|
||||
color: var(--primary-color);
|
||||
font-weight: 600;
|
||||
font-size: 0.82em;
|
||||
}
|
||||
|
||||
.skill-progress-bar {
|
||||
height: 0.38rem;
|
||||
background: #e8ecf0;
|
||||
border-radius: 0.19rem;
|
||||
overflow: hidden;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.skill-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-green));
|
||||
border-radius: 0.12rem;
|
||||
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.62rem; }
|
||||
.tech-tag { padding: 0.25rem 0.62rem; background: #2c3e50; color: white; border-radius: 0.19rem; font-size: 0.82em; }
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.50rem; }
|
||||
.platform-tag { padding: 0.19rem 0.62rem; background: rgba(52,152,219,0.1); color: #3498db; border-radius: 0.19rem; font-size: 0.8em; border: 0.06rem solid rgba(52,152,219,0.2); }
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: #444;
|
||||
padding: 0.50rem 0;
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.advantage-list li i { color: #3498db; font-size: 0.9em; margin-top: 0.12rem; }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container { margin: 0.94rem; }
|
||||
header.resume-header { padding: 1.88rem 1.25rem; }
|
||||
main.resume-body { padding: 1.56rem 1.25rem; }
|
||||
.item-header { flex-direction: column; gap: 0.31rem; }
|
||||
header.resume-header .basic-info .contact { gap: 0.62rem; }
|
||||
.header-grid { grid-template-columns: 1fr; text-align: center; }
|
||||
header.resume-header .avatar img { margin: 0 auto; }
|
||||
.section-content .item:hover { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
body::before { display: none; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
border: 0.06rem solid #ddd;
|
||||
}
|
||||
header.resume-header {
|
||||
background: #2c3e50;
|
||||
color: white;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
.section-content .item {
|
||||
background: #fafafa;
|
||||
border-left: 0.19rem solid #3498db;
|
||||
}
|
||||
.section-content .item:hover {
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
a { color: #3498db; text-decoration: underline; }
|
||||
@page { margin: 15mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
<div class="header-grid">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</div>
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,261 @@
|
||||
{{ define "web/password" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ .SystemConfig.WebsiteTitle }} - 访问密码</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
:root {
|
||||
--primary: #7B8EA2;
|
||||
--primary-dark: #6B7E92;
|
||||
--text-primary: #2F343A;
|
||||
--text-secondary: #6C737C;
|
||||
--text-muted: #9CA3AF;
|
||||
--bg: #F8F7F4;
|
||||
--bg-card: #FFFFFF;
|
||||
--border-color: #E5E3DF;
|
||||
--shadow-sm: 0 0.06rem 0.12rem rgba(47, 52, 58, 0.04);
|
||||
--shadow-md: 0 0.25rem 0.75rem rgba(47, 52, 58, 0.06);
|
||||
--shadow-lg: 0 0.50rem 1.50rem rgba(47, 52, 58, 0.08);
|
||||
--radius-sm: 0.38rem;
|
||||
--radius-md: 0.62rem;
|
||||
--radius-lg: 0.88rem;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Noto Sans SC', sans-serif;
|
||||
background-color: var(--bg);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.password-container {
|
||||
background: var(--bg-card);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 3rem;
|
||||
box-shadow: var(--shadow-lg);
|
||||
width: 100%;
|
||||
max-width: 26.25rem;
|
||||
text-align: center;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
.password-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.password-logo {
|
||||
width: 3.50rem;
|
||||
height: 3.50rem;
|
||||
margin: 0 auto 1.25rem;
|
||||
background: var(--primary);
|
||||
border-radius: var(--radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
.password-logo img { width: 2.50rem; height: 2.50rem; border-radius: var(--radius-sm); object-cover; }
|
||||
.password-title {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.password-subtitle {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.password-icon {
|
||||
width: 4.50rem;
|
||||
height: 4.50rem;
|
||||
background: rgba(123, 142, 162, 0.08);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 1.50rem;
|
||||
font-size: 2rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.6rem;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.50rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
p {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 2rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
text-align: left;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.50rem;
|
||||
}
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 0.75rem 0.88rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 1rem;
|
||||
transition: all 0.2s;
|
||||
outline: none;
|
||||
background: var(--bg);
|
||||
}
|
||||
.form-group input:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 0.19rem rgba(123, 142, 162, 0.1);
|
||||
background: white;
|
||||
}
|
||||
.form-group input.error {
|
||||
border-color: #ef4444;
|
||||
}
|
||||
.error-message {
|
||||
color: #ef4444;
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.38rem;
|
||||
display: none;
|
||||
}
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 0.81rem;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn:hover {
|
||||
background: var(--primary-dark);
|
||||
transform: translateY(-0.06rem);
|
||||
box-shadow: 0 0.25rem 0.75rem rgba(123, 142, 162, 0.2);
|
||||
}
|
||||
.btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 1.12rem;
|
||||
height: 1.12rem;
|
||||
border: 0.12rem solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 0.8s linear infinite;
|
||||
margin-right: 0.50rem;
|
||||
}
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="password-container">
|
||||
<div class="password-header">
|
||||
<div class="password-logo">
|
||||
{{ if .SystemConfig.LogoPath }}
|
||||
<img src="{{.SystemConfig.LogoPath}}" alt="Logo">
|
||||
{{ else }}
|
||||
<i class="fas fa-file-alt"></i>
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="password-title">{{ .SystemConfig.WebsiteTitle }}</div>
|
||||
<div class="password-subtitle">{{ .SystemConfig.WebsiteDesc }}</div>
|
||||
</div>
|
||||
|
||||
<div class="password-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<h1>简历已加密</h1>
|
||||
<p>请输入访问密码以查看这份简历</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">访问密码</label>
|
||||
<input type="password" id="password" placeholder="请输入密码" onkeypress="handleKeyPress(event)">
|
||||
<div class="error-message" id="errorMessage">密码错误,请重试</div>
|
||||
</div>
|
||||
|
||||
<button class="btn" id="submitBtn" onclick="submitPassword()">
|
||||
<span id="btnText">验证访问</span>
|
||||
<span class="loading-spinner" id="loadingSpinner" style="display: none;"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const passwordInput = document.getElementById('password');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const btnText = document.getElementById('btnText');
|
||||
const loadingSpinner = document.getElementById('loadingSpinner');
|
||||
const errorMessage = document.getElementById('errorMessage');
|
||||
|
||||
function handleKeyPress(event) {
|
||||
if (event.key === 'Enter') {
|
||||
submitPassword();
|
||||
}
|
||||
}
|
||||
|
||||
async function submitPassword() {
|
||||
const password = passwordInput.value.trim();
|
||||
if (!password) {
|
||||
return;
|
||||
}
|
||||
|
||||
submitBtn.disabled = true;
|
||||
btnText.textContent = '验证中...';
|
||||
loadingSpinner.style.display = 'inline-block';
|
||||
errorMessage.style.display = 'none';
|
||||
passwordInput.classList.remove('error');
|
||||
|
||||
try {
|
||||
const response = await fetch(window.location.pathname + '/verify-password', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'password=' + encodeURIComponent(password),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
errorMessage.textContent = result.message;
|
||||
errorMessage.style.display = 'block';
|
||||
passwordInput.classList.add('error');
|
||||
passwordInput.value = '';
|
||||
passwordInput.focus();
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.textContent = '验证失败,请重试';
|
||||
errorMessage.style.display = 'block';
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
btnText.textContent = '验证访问';
|
||||
loadingSpinner.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
passwordInput.focus();
|
||||
</script>
|
||||
</body>
|
||||
</html>{{ end }}
|
||||
@@ -0,0 +1,847 @@
|
||||
{{ define "web/portfolio" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>项目作品集 - {{.Resume.BasicInfo.Name}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/github.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
background: white;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
padding: 0.88rem 1.25rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 0.06rem 0.19rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.nav-bar .nav-content {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-bar .back-link {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-bar .back-link:hover {
|
||||
color: #2980b9;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title {
|
||||
font-size: 1.15em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title i {
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
background: white;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
padding: 0.75rem 1.25rem;
|
||||
position: sticky;
|
||||
top: 3.75rem;
|
||||
z-index: 999;
|
||||
box-shadow: 0 0.06rem 0.19rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.search-bar .search-content {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
gap: 0.94rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-input-wrapper {
|
||||
flex: 1;
|
||||
min-width: 15.62rem;
|
||||
max-width: 25rem;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-input-wrapper i {
|
||||
position: absolute;
|
||||
left: 0.75rem;
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.search-input-wrapper input {
|
||||
width: 100%;
|
||||
padding: 0.62rem 2.50rem 0.62rem 2.19rem;
|
||||
border: 0.06rem solid #e8ecef;
|
||||
border-radius: 0.38rem;
|
||||
font-size: 0.95em;
|
||||
transition: all 0.2s;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.search-input-wrapper input:focus {
|
||||
outline: none;
|
||||
border-color: #3498db;
|
||||
box-shadow: 0 0 0 0.19rem rgba(52,152,219,0.1);
|
||||
}
|
||||
|
||||
.clear-btn {
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
padding: 0.25rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.clear-btn:hover {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.filter-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.filter-tag {
|
||||
padding: 0.38rem 0.75rem;
|
||||
background: #f0f4f8;
|
||||
color: #555;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.85em;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 0.06rem solid #e8ecef;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.filter-tag:hover {
|
||||
background: #e8f4fd;
|
||||
color: #3498db;
|
||||
border-color: #d5eaf9;
|
||||
}
|
||||
|
||||
.filter-tag.active {
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border-color: #3498db;
|
||||
}
|
||||
|
||||
.portfolio-container {
|
||||
max-width: 75rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
min-height: calc(100vh - 3.75rem);
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
width: 17.50rem;
|
||||
background: white;
|
||||
border-right: 0.06rem solid #e8ecef;
|
||||
padding: 1.50rem 0;
|
||||
position: sticky;
|
||||
top: 3.75rem;
|
||||
height: calc(100vh - 3.75rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.project-sidebar .sidebar-header {
|
||||
padding: 0 1.25rem 1rem;
|
||||
border-bottom: 0.06rem solid #f0f2f5;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.project-sidebar .sidebar-header h3 {
|
||||
font-size: 0.88em;
|
||||
font-weight: 600;
|
||||
color: #888;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03rem;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.75rem 1.25rem;
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
transition: all 0.25s ease;
|
||||
border-left: 0.19rem solid transparent;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a:hover {
|
||||
background: #f8fbfe;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a.active {
|
||||
background: #e8f4fd;
|
||||
color: #3498db;
|
||||
border-left-color: #3498db;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a i {
|
||||
font-size: 0.85em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a .project-name {
|
||||
font-size: 0.92em;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a .project-date {
|
||||
font-size: 0.78em;
|
||||
color: #999;
|
||||
margin-top: 0.12rem;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
flex: 1;
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.project-main .project-header {
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.25rem;
|
||||
border-bottom: 0.12rem solid #3498db;
|
||||
}
|
||||
|
||||
.project-main .project-header h1 {
|
||||
font-size: 1.7em;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 0.50rem;
|
||||
}
|
||||
|
||||
.project-main .project-header .project-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
font-size: 0.92em;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.project-main .project-header .project-meta span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.project-main .project-header .project-meta i {
|
||||
color: #3498db;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.project-main .project-header .tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.38rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.project-main .project-header .tech-tag {
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: #e8f4fd;
|
||||
color: #3498db;
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.82em;
|
||||
border: 0.06rem solid #d5eaf9;
|
||||
}
|
||||
|
||||
.project-main .doc-content {
|
||||
color: #333;
|
||||
font-size: 0.98em;
|
||||
}
|
||||
|
||||
.doc-content h2 {
|
||||
font-size: 1.4em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin: 1.75rem 0 1rem;
|
||||
padding-bottom: 0.62rem;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
color: #34495e;
|
||||
margin: 1.38rem 0 0.75rem;
|
||||
}
|
||||
|
||||
.doc-content h4 {
|
||||
font-size: 1.05em;
|
||||
font-weight: 600;
|
||||
color: #4a5568;
|
||||
margin: 1.12rem 0 0.62rem;
|
||||
}
|
||||
|
||||
.doc-content p {
|
||||
margin-bottom: 0.88rem;
|
||||
line-height: 1.75;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.doc-content ul, .doc-content ol {
|
||||
margin-bottom: 0.88rem;
|
||||
padding-left: 1.50rem;
|
||||
}
|
||||
|
||||
.doc-content li {
|
||||
margin-bottom: 0.38rem;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.doc-content blockquote {
|
||||
border-left: 0.19rem solid #3498db;
|
||||
padding: 0.88rem 1.25rem;
|
||||
margin: 1.12rem 0;
|
||||
background: #f8fbfe;
|
||||
color: #555;
|
||||
border-radius: 0 0.25rem 0.25rem 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.doc-content pre {
|
||||
background: #2d2d2d;
|
||||
color: #ccc;
|
||||
padding: 1.12rem 1.38rem;
|
||||
border-radius: 0.38rem;
|
||||
overflow-x: auto;
|
||||
margin: 1.12rem 0;
|
||||
font-size: 0.88em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.doc-content pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.doc-content code {
|
||||
background: #f4f6f8;
|
||||
padding: 0.12rem 0.38rem;
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.9em;
|
||||
color: #e74c3c;
|
||||
font-family: 'Fira Code', 'Monaco', monospace;
|
||||
}
|
||||
|
||||
.doc-content table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1.12rem 0;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
.doc-content th, .doc-content td {
|
||||
border: 0.06rem solid #e8ecef;
|
||||
padding: 0.75rem 1rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.doc-content th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.doc-content tr:nth-child(even) {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.doc-content img {
|
||||
max-width: 100%;
|
||||
border-radius: 0.38rem;
|
||||
margin: 0.88rem 0;
|
||||
box-shadow: 0 0.12rem 0.62rem rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.doc-content a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.doc-content a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #3498db;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.doc-content a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.doc-content hr {
|
||||
border: none;
|
||||
border-top: 0.06rem solid #e8ecef;
|
||||
margin: 1.75rem 0;
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
margin: 1.12rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mermaid svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.no-doc {
|
||||
text-align: center;
|
||||
padding: 5rem 1.25rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.no-doc i {
|
||||
font-size: 4em;
|
||||
margin-bottom: 1.25rem;
|
||||
opacity: 0.4;
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.no-doc h3 {
|
||||
font-size: 1.2em;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.50rem;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.no-doc p {
|
||||
font-size: 0.95em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
@media (max-width: 56.25rem) {
|
||||
.portfolio-container {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
width: 100%;
|
||||
border-right: none;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
position: relative;
|
||||
top: auto;
|
||||
height: auto;
|
||||
max-height: 18.75rem;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
padding: 1.50rem 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.nav-bar {
|
||||
padding: 0.75rem 0.94rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
padding: 1.12rem 0;
|
||||
}
|
||||
|
||||
.project-sidebar .sidebar-header {
|
||||
padding: 0 0.94rem 0.75rem;
|
||||
}
|
||||
|
||||
.project-sidebar .project-list li a {
|
||||
padding: 0.62rem 0.94rem;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
padding: 1.25rem 0.94rem;
|
||||
}
|
||||
|
||||
.project-main .project-header h1 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.project-main .project-header .project-meta {
|
||||
gap: 0.62rem;
|
||||
font-size: 0.88em;
|
||||
}
|
||||
|
||||
.doc-content h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.doc-content pre {
|
||||
padding: 0.75rem;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
.nav-bar { display: none; }
|
||||
.portfolio-container {
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
.project-sidebar {
|
||||
display: none;
|
||||
}
|
||||
.project-main {
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
a { color: #3498db; text-decoration: underline; }
|
||||
@page { margin: 15mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav-bar">
|
||||
<div class="nav-content">
|
||||
<a href="/" class="back-link">
|
||||
<i class="fas fa-arrow-left"></i> 返回简历
|
||||
</a>
|
||||
<span class="nav-title">
|
||||
<i class="fas fa-folder-open"></i> 项目作品集
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="search-bar">
|
||||
<div class="search-content">
|
||||
<div class="search-input-wrapper">
|
||||
<i class="fas fa-search"></i>
|
||||
<input type="text" id="searchInput" placeholder="搜索项目名称、描述或技术栈..." oninput="handleSearch()">
|
||||
<button class="clear-btn" id="clearSearch" onclick="clearSearch()" style="display:none;">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="filter-tags" id="filterTags">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="portfolio-container">
|
||||
<aside class="project-sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h3>项目列表</h3>
|
||||
</div>
|
||||
<ul class="project-list" id="projectList">
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<main class="project-main" id="projectContent">
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/lib/highlight.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
flowchart: { htmlLabels: false },
|
||||
graph: { htmlLabels: false }
|
||||
});
|
||||
|
||||
let allProjects = [];
|
||||
let currentFilter = '';
|
||||
let currentSearch = '';
|
||||
|
||||
function handleSearch() {
|
||||
const input = document.getElementById('searchInput');
|
||||
const clearBtn = document.getElementById('clearSearch');
|
||||
currentSearch = input.value.toLowerCase().trim();
|
||||
|
||||
if (currentSearch) {
|
||||
clearBtn.style.display = 'block';
|
||||
} else {
|
||||
clearBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
filterProjects();
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
const input = document.getElementById('searchInput');
|
||||
input.value = '';
|
||||
currentSearch = '';
|
||||
document.getElementById('clearSearch').style.display = 'none';
|
||||
filterProjects();
|
||||
}
|
||||
|
||||
function toggleFilter(tag) {
|
||||
const filterTags = document.querySelectorAll('.filter-tag');
|
||||
filterTags.forEach(el => {
|
||||
if (el.dataset.tag === tag) {
|
||||
el.classList.toggle('active');
|
||||
if (el.classList.contains('active')) {
|
||||
currentFilter = tag;
|
||||
} else {
|
||||
currentFilter = '';
|
||||
}
|
||||
} else {
|
||||
el.classList.remove('active');
|
||||
}
|
||||
});
|
||||
filterProjects();
|
||||
}
|
||||
|
||||
function filterProjects() {
|
||||
if (allProjects.length === 0) return;
|
||||
|
||||
let filtered = allProjects;
|
||||
|
||||
if (currentSearch) {
|
||||
filtered = filtered.filter(p => {
|
||||
const name = (p.name || '').toLowerCase();
|
||||
const desc = (p.description || '').toLowerCase();
|
||||
const tech = (p.tech_stack || []).join(' ').toLowerCase();
|
||||
return name.includes(currentSearch) || desc.includes(currentSearch) || tech.includes(currentSearch);
|
||||
});
|
||||
}
|
||||
|
||||
if (currentFilter) {
|
||||
filtered = filtered.filter(p => {
|
||||
const tech = (p.tech_stack || []);
|
||||
return tech.includes(currentFilter);
|
||||
});
|
||||
}
|
||||
|
||||
renderProjectList(filtered);
|
||||
|
||||
if (filtered.length > 0) {
|
||||
renderProject(filtered[0], 0);
|
||||
} else {
|
||||
document.getElementById('projectContent').innerHTML = '<div class="no-doc"><i class="fas fa-search"></i><h3>未找到匹配的项目</h3><p>尝试调整搜索关键词或筛选条件</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderFilterTags() {
|
||||
const tagContainer = document.getElementById('filterTags');
|
||||
if (!tagContainer) return;
|
||||
|
||||
const techSet = new Set();
|
||||
allProjects.forEach(p => {
|
||||
(p.tech_stack || []).forEach(t => techSet.add(t));
|
||||
});
|
||||
|
||||
const tags = Array.from(techSet).slice(0, 6);
|
||||
|
||||
if (tags.length === 0) {
|
||||
tagContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
tagContainer.innerHTML = tags.map(tag =>
|
||||
`<span class="filter-tag" data-tag="${tag}" onclick="toggleFilter('${tag}')">
|
||||
<i class="fas fa-tag"></i> ${tag}
|
||||
</span>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
async function renderMarkdown(markdown) {
|
||||
const mermaidBlocks = [];
|
||||
const processed = markdown.replace(/```mermaid\s*([\s\S]*?)```/g, function(match, content) {
|
||||
const id = 'mermaid-' + Date.now() + '-' + mermaidBlocks.length;
|
||||
mermaidBlocks.push({ id: id, content: content.trim() });
|
||||
return '<div id="' + id + '" class="mermaid"></div>';
|
||||
});
|
||||
|
||||
const html = DOMPurify.sanitize(marked.parse(processed), {
|
||||
ADD_TAGS: ['iframe'],
|
||||
ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling']
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
|
||||
if (typeof hljs !== 'undefined') {
|
||||
container.querySelectorAll('pre code').forEach(function(block) {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(mermaidBlocks.map(async function(block) {
|
||||
const el = container.querySelector('#' + block.id);
|
||||
if (el) {
|
||||
try {
|
||||
const result = await mermaid.render(block.id, block.content);
|
||||
el.innerHTML = result.svg;
|
||||
} catch (e) {
|
||||
el.innerHTML = '<div style="color: #e74c3c; padding: 0.62rem; background: #fef5f5; border-radius: 0.25rem;">' + e.message + '</div>';
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return container.innerHTML;
|
||||
}
|
||||
|
||||
async function loadProjects() {
|
||||
const response = await fetch('/api/portfolio');
|
||||
const projects = await response.json();
|
||||
allProjects = projects;
|
||||
|
||||
renderFilterTags();
|
||||
renderProjectList(projects);
|
||||
|
||||
if (projects.length > 0) {
|
||||
renderProject(projects[0], 0);
|
||||
} else {
|
||||
document.getElementById('projectContent').innerHTML = '<div class="no-doc"><i class="fas fa-folder-open"></i><h3>暂无项目</h3><p>还没有添加任何作品集项目</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
function renderProjectList(projects) {
|
||||
const list = document.getElementById('projectList');
|
||||
list.innerHTML = '';
|
||||
|
||||
projects.forEach(function(project, index) {
|
||||
const li = document.createElement('li');
|
||||
const a = document.createElement('a');
|
||||
a.href = '#';
|
||||
a.onclick = function(e) {
|
||||
e.preventDefault();
|
||||
selectProject(index);
|
||||
};
|
||||
|
||||
const icon = document.createElement('i');
|
||||
icon.className = 'fas fa-file-code';
|
||||
|
||||
const info = document.createElement('div');
|
||||
info.innerHTML = '<div class="project-name">' + (project.name || '未命名项目') + '</div>' + (project.start_date ? '<div class="project-date">' + project.start_date + ' - ' + (project.end_date || '至今') + '</div>' : '');
|
||||
|
||||
a.appendChild(icon);
|
||||
a.appendChild(info);
|
||||
li.appendChild(a);
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
|
||||
function selectProject(index) {
|
||||
const links = document.querySelectorAll('.project-list li a');
|
||||
links.forEach(function(link, i) {
|
||||
if (i === index) {
|
||||
link.classList.add('active');
|
||||
} else {
|
||||
link.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
let projects = allProjects;
|
||||
if (currentSearch || currentFilter) {
|
||||
projects = projects.filter(p => {
|
||||
const matchSearch = !currentSearch ||
|
||||
(p.name || '').toLowerCase().includes(currentSearch) ||
|
||||
(p.description || '').toLowerCase().includes(currentSearch) ||
|
||||
(p.tech_stack || []).join(' ').toLowerCase().includes(currentSearch);
|
||||
const matchFilter = !currentFilter || (p.tech_stack || []).includes(currentFilter);
|
||||
return matchSearch && matchFilter;
|
||||
});
|
||||
}
|
||||
|
||||
if (projects[index]) {
|
||||
renderProject(projects[index], index);
|
||||
}
|
||||
}
|
||||
|
||||
async function renderProject(project, index) {
|
||||
const content = document.getElementById('projectContent');
|
||||
|
||||
content.innerHTML = '<div style="text-align:center;padding:2.50rem;"><i class="fas fa-spinner fa-spin fa-2x"></i></div>';
|
||||
|
||||
const header = '<div class="project-header">' +
|
||||
'<h1>' + (project.name || '未命名项目') + '</h1>' +
|
||||
'<div class="project-meta">' +
|
||||
(project.start_date ? '<span><i class="fas fa-calendar"></i> ' + project.start_date + ' - ' + (project.end_date || '至今') + '</span>' : '') +
|
||||
(project.url ? '<span><i class="fas fa-external-link-alt"></i> <a href="' + project.url + '" target="_blank">项目链接</a></span>' : '') +
|
||||
'</div>' +
|
||||
(project.tech_stack && project.tech_stack.length ? '<div class="tech-stack">' + project.tech_stack.map(function(t) { return '<span class="tech-tag">' + t + '</span>'; }).join('') + '</div>' : '') +
|
||||
'</div>';
|
||||
|
||||
var docHtml = '';
|
||||
if (project.doc && project.doc.trim()) {
|
||||
docHtml = '<div class="doc-content" id="doc-' + index + '"></div>';
|
||||
} else {
|
||||
docHtml = '<div class="no-doc"><i class="fas fa-file"></i><h3>该项目暂无详细文档</h3><p>点击左侧项目切换,或在后台添加项目文档</p></div>';
|
||||
}
|
||||
|
||||
content.innerHTML = header + docHtml;
|
||||
|
||||
if (project.doc && project.doc.trim()) {
|
||||
const docDiv = document.getElementById('doc-' + index);
|
||||
if (docDiv) {
|
||||
docDiv.innerHTML = await renderMarkdown(project.doc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', loadProjects);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,321 @@
|
||||
{{ define "web/professional" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--bg-primary: #f2f2f2;
|
||||
--bg-secondary: #ffffff;
|
||||
--bg-card: #ffffff;
|
||||
--bg-accent: #1a252f;
|
||||
--bg-accent-light: #243447;
|
||||
--border-color: #d8d8d8;
|
||||
--text-primary: #1a1a1a;
|
||||
--text-secondary: #4a4a4a;
|
||||
--text-muted: #808080;
|
||||
--accent-color: #2c3e50;
|
||||
--accent-gold: #c9a227;
|
||||
--accent-blue: #3498db;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: 'Georgia', 'Times New Roman', 'SimSun', serif;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 150 150' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.05' numOctaves='4'/%3E%3CfeDiffuseLighting in='noise' lighting-color='%23ffffff' surfaceScale='2'%3E%3CfeDistantLight azimuth='45' elevation='60'/%3E%3C/feDiffuseLighting%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
||||
opacity: 0.03;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
::selection { background: rgba(44, 62, 80, 0.2); }
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: white;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
padding: 0.44rem 0.88rem;
|
||||
border-radius: 0.19rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border-color: var(--accent-color);
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 48.75rem;
|
||||
margin: 3.12rem auto;
|
||||
background: var(--bg-secondary);
|
||||
box-shadow: 0 0.12rem 0.75rem rgba(0,0,0,0.08);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header.resume-header {
|
||||
background: var(--bg-accent);
|
||||
padding: 2.50rem;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
header.resume-header .avatar img {
|
||||
width: 7.81rem;
|
||||
height: 7.81rem;
|
||||
border-radius: 50%;
|
||||
border: 0.25rem solid rgba(255,255,255,0.25);
|
||||
object-fit: cover;
|
||||
}
|
||||
header.resume-header .basic-info { display: flex; flex-direction: column; gap: 0.88rem; align-items: center; }
|
||||
header.resume-header .basic-info .info-header { text-align: center; }
|
||||
header.resume-header .basic-info .name { font-size: 2.2em; font-weight: 700; margin-bottom: 0.50rem; letter-spacing: 0.12rem; line-height: 1.2; }
|
||||
header.resume-header .basic-info .title { font-size: 1em; opacity: 0.88; font-weight: 300; letter-spacing: 0.06rem; }
|
||||
|
||||
.contact-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.62rem;
|
||||
font-size: 0.84em;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.contact-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.31rem;
|
||||
padding: 0.31rem 0.88rem;
|
||||
background: rgba(255,255,255,0.08);
|
||||
border-radius: 0.19rem;
|
||||
white-space: nowrap;
|
||||
transition: all 0.3s ease;
|
||||
border: 0.06rem solid rgba(255,255,255,0.08);
|
||||
}
|
||||
.contact-tag:hover {
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-color: rgba(255,255,255,0.15);
|
||||
}
|
||||
.contact-tag i { width: 0.81rem; text-align: center; opacity: 0.7; }
|
||||
.contact-tag a { color: white; text-decoration: none; position: relative; }
|
||||
.contact-tag a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: white;
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.contact-tag a:hover::after { width: 100%; }
|
||||
.contact-tag a:hover { opacity: 0.8; }
|
||||
|
||||
.meta-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
gap: 0.50rem;
|
||||
font-size: 0.8em;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.meta-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border-radius: 0.12rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.meta-tag i { width: 0.75rem; text-align: center; opacity: 0.5; }
|
||||
|
||||
main.resume-body { padding: 2.62rem; }
|
||||
|
||||
.resume-body-inner { display: flex; gap: 2.62rem; }
|
||||
|
||||
aside.sidebar { width: 35%; }
|
||||
section.main-content { width: 65%; }
|
||||
|
||||
section { margin-bottom: 2.19rem; }
|
||||
section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-title {
|
||||
font-size: 0.95em;
|
||||
font-weight: 600;
|
||||
color: var(--bg-accent);
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.12rem solid var(--bg-accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
.section-title i { font-size: 0.85em; }
|
||||
|
||||
aside.sidebar .section-title { color: var(--accent-color); border-color: var(--accent-color); }
|
||||
|
||||
.section-content { display: flex; flex-direction: column; gap: 1rem; }
|
||||
|
||||
aside.sidebar .section-content { gap: 0.88rem; }
|
||||
|
||||
article.item { padding-bottom: 1rem; border-bottom: 0.06rem solid var(--border-color); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
article.item:last-child { border-bottom: none; padding-bottom: 0; }
|
||||
article.item:hover { background: #fafafa; padding-left: 0.50rem; margin-left: -0.50rem; margin-right: -0.50rem; padding-right: 0.50rem; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.31rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: var(--text-primary); font-size: 0.98em; }
|
||||
.item-header .period { color: var(--text-muted); font-size: 0.82em; }
|
||||
.item-header a { color: var(--accent-blue); font-size: 0.95em; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: var(--accent-blue);
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
|
||||
.position { color: var(--accent-color); font-weight: 500; margin-bottom: 0.50rem; }
|
||||
.degree { color: var(--text-secondary); margin-bottom: 0.31rem; }
|
||||
.gpa { color: var(--text-muted); font-size: 0.82em; }
|
||||
|
||||
.description { color: var(--text-secondary); line-height: 1.6; margin-bottom: 0.62rem; font-size: 0.88em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: var(--text-secondary); padding: 0.25rem 0; font-size: 0.88em; display: flex; align-items: flex-start; gap: 0.50rem; line-height: 1.6; }
|
||||
.highlights li i { color: var(--accent-gold); font-size: 0.78em; margin-top: 0.25rem; }
|
||||
|
||||
.skill-items { display: flex; flex-wrap: wrap; gap: 0.44rem; }
|
||||
.skill-tag {
|
||||
padding: 0.31rem 0.62rem;
|
||||
background: rgba(44,62,80,0.06);
|
||||
border: 0.06rem solid rgba(44,62,80,0.15);
|
||||
border-radius: 0.12rem;
|
||||
font-size: 0.82em;
|
||||
color: var(--accent-color);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.skill-tag:hover {
|
||||
background: var(--accent-color);
|
||||
color: white;
|
||||
border-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.31rem; margin-top: 0.62rem; }
|
||||
.tech-tag { padding: 0.19rem 0.56rem; background: var(--bg-accent); color: white; border-radius: 0.12rem; font-size: 0.78em; }
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.31rem; margin-top: 0.50rem; }
|
||||
.platform-tag { padding: 0.12rem 0.50rem; background: rgba(52,152,219,0.1); color: var(--accent-blue); border-radius: 0.12rem; font-size: 0.76em; border: 0.06rem solid rgba(52,152,219,0.2); }
|
||||
|
||||
.sidebar-summary { color: var(--text-secondary); font-size: 0.88em; line-height: 1.65; }
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: var(--text-secondary);
|
||||
padding: 0.38rem 0;
|
||||
font-size: 0.88em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.50rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.advantage-list li i { color: var(--accent-color); font-size: 0.82em; margin-top: 0.12rem; }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-body-inner { flex-direction: column; gap: 1.88rem; }
|
||||
aside.sidebar { width: 100%; }
|
||||
section.main-content { width: 100%; }
|
||||
.resume-container { margin: 1.25rem; }
|
||||
header.resume-header { padding: 1.88rem 1.38rem; }
|
||||
main.resume-body { padding: 1.88rem 1.38rem; }
|
||||
.contact-line { gap: 0.75rem; }
|
||||
.item-header { flex-direction: column; gap: 0.31rem; }
|
||||
article.item:hover { margin: 0; padding: 0; padding-bottom: 1rem; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
body::before { display: none; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
header.resume-header {
|
||||
background: #1a252f;
|
||||
color: white;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
aside.sidebar {
|
||||
background: #f7f7f7;
|
||||
padding-right: 1.25rem;
|
||||
border-right: 0.06rem solid #ddd;
|
||||
}
|
||||
article.item:hover {
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
a { color: var(--accent-blue); text-decoration: underline; }
|
||||
@page { margin: 12mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
<div class="resume-body-inner">
|
||||
<aside class="sidebar">
|
||||
{{ template "section-summary" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-education" . }}
|
||||
</aside>
|
||||
<section class="main-content">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,545 @@
|
||||
{{ define "web/project" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.Project.Name}} - {{.Resume.BasicInfo.Name}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/styles/github.min.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: unset; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
background: white;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
padding: 0.88rem 1.25rem;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 0.06rem 0.19rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.nav-bar .nav-content {
|
||||
max-width: 62.50rem;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.nav-bar .back-link {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
font-size: 0.95em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nav-bar .back-link:hover {
|
||||
color: #2980b9;
|
||||
gap: 0.62rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.project-container {
|
||||
max-width: 62.50rem;
|
||||
margin: 2.19rem auto;
|
||||
padding: 0 1.25rem;
|
||||
display: grid;
|
||||
grid-template-columns: 17.50rem 1fr;
|
||||
gap: 1.75rem;
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
background: white;
|
||||
border-radius: 0.38rem;
|
||||
padding: 1.38rem;
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(0,0,0,0.04);
|
||||
position: sticky;
|
||||
top: 5rem;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.project-sidebar .sidebar-title {
|
||||
font-size: 0.88em;
|
||||
font-weight: 600;
|
||||
color: #888;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03rem;
|
||||
margin-bottom: 0.88rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.06rem solid #f0f2f5;
|
||||
}
|
||||
|
||||
.project-sidebar .project-name {
|
||||
font-size: 1.2em;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 0.75rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.project-sidebar .meta-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.50rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.92em;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.project-sidebar .meta-item i {
|
||||
color: #3498db;
|
||||
font-size: 0.85em;
|
||||
margin-top: 0.12rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.project-sidebar .meta-item .meta-label {
|
||||
color: #888;
|
||||
font-size: 0.85em;
|
||||
margin-right: 0.38rem;
|
||||
}
|
||||
|
||||
.project-sidebar .tech-stack {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.38rem;
|
||||
margin-top: 0.50rem;
|
||||
}
|
||||
|
||||
.project-sidebar .tech-tag {
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: #e8f4fd;
|
||||
color: #3498db;
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.82em;
|
||||
border: 0.06rem solid #d5eaf9;
|
||||
}
|
||||
|
||||
.project-sidebar .links {
|
||||
margin-top: 1.12rem;
|
||||
padding-top: 1.12rem;
|
||||
border-top: 0.06rem solid #f0f2f5;
|
||||
}
|
||||
|
||||
.project-sidebar .links a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
font-size: 0.92em;
|
||||
padding: 0.50rem 0;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 0.25rem;
|
||||
padding-left: 0.62rem;
|
||||
}
|
||||
|
||||
.project-sidebar .links a:hover {
|
||||
background: #f0f7ff;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
background: white;
|
||||
border-radius: 0.38rem;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.project-main .doc-content {
|
||||
color: #333;
|
||||
font-size: 0.98em;
|
||||
}
|
||||
|
||||
.doc-content h1 {
|
||||
font-size: 1.7em;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1.12rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 0.12rem solid #3498db;
|
||||
}
|
||||
|
||||
.doc-content h2 {
|
||||
font-size: 1.4em;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
margin: 1.50rem 0 0.88rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.06rem solid #e8ecef;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1.15em;
|
||||
font-weight: 600;
|
||||
color: #34495e;
|
||||
margin: 1.25rem 0 0.75rem;
|
||||
}
|
||||
|
||||
.doc-content p {
|
||||
margin-bottom: 0.88rem;
|
||||
line-height: 1.75;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.doc-content ul, .doc-content ol {
|
||||
margin-bottom: 0.88rem;
|
||||
padding-left: 1.50rem;
|
||||
}
|
||||
|
||||
.doc-content li {
|
||||
margin-bottom: 0.38rem;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.doc-content blockquote {
|
||||
border-left: 0.19rem solid #3498db;
|
||||
padding: 0.75rem 1.12rem;
|
||||
margin: 1rem 0;
|
||||
background: #f8fbfe;
|
||||
color: #555;
|
||||
border-radius: 0 0.25rem 0.25rem 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.doc-content pre {
|
||||
background: #2d2d2d;
|
||||
color: #ccc;
|
||||
padding: 1rem 1.25rem;
|
||||
border-radius: 0.38rem;
|
||||
overflow-x: auto;
|
||||
margin: 1rem 0;
|
||||
font-size: 0.88em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.doc-content pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.doc-content code {
|
||||
background: #f4f6f8;
|
||||
padding: 0.12rem 0.38rem;
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.9em;
|
||||
color: #e74c3c;
|
||||
font-family: 'Fira Code', 'Monaco', monospace;
|
||||
}
|
||||
|
||||
.doc-content table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
.doc-content th, .doc-content td {
|
||||
border: 0.06rem solid #e8ecef;
|
||||
padding: 0.62rem 0.88rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.doc-content th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.doc-content tr:nth-child(even) {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.doc-content img {
|
||||
max-width: 100%;
|
||||
border-radius: 0.38rem;
|
||||
margin: 0.75rem 0;
|
||||
box-shadow: 0 0.12rem 0.50rem rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.doc-content a {
|
||||
color: #3498db;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.doc-content a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.06rem;
|
||||
left: 0;
|
||||
background: #3498db;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.doc-content a:hover::after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.doc-content hr {
|
||||
border: none;
|
||||
border-top: 0.06rem solid #e8ecef;
|
||||
margin: 1.50rem 0;
|
||||
}
|
||||
|
||||
.mermaid-placeholder {
|
||||
background: #fafbfc;
|
||||
border: 0.06rem dashed #dde3e8;
|
||||
border-radius: 0.38rem;
|
||||
padding: 1.25rem;
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.mermaid {
|
||||
margin: 1rem 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mermaid svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.no-doc {
|
||||
text-align: center;
|
||||
padding: 3.75rem 1.25rem;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.no-doc i {
|
||||
font-size: 3em;
|
||||
margin-bottom: 1rem;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.no-doc p {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
@media (max-width: 56.25rem) {
|
||||
.project-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
position: static;
|
||||
order: 2;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.nav-bar {
|
||||
padding: 0.75rem 0.94rem;
|
||||
}
|
||||
|
||||
.nav-bar .nav-title {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.project-container {
|
||||
margin: 1.25rem auto;
|
||||
padding: 0 0.94rem;
|
||||
}
|
||||
|
||||
.project-sidebar {
|
||||
padding: 1.12rem;
|
||||
}
|
||||
|
||||
.project-main {
|
||||
padding: 1.38rem;
|
||||
}
|
||||
|
||||
.doc-content h1 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.doc-content h2 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.doc-content pre {
|
||||
padding: 0.75rem;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; }
|
||||
.nav-bar { display: none; }
|
||||
.project-container {
|
||||
grid-template-columns: 1fr;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.project-sidebar {
|
||||
position: static;
|
||||
box-shadow: none;
|
||||
border: 0.06rem solid #ddd;
|
||||
}
|
||||
.project-main {
|
||||
box-shadow: none;
|
||||
border: 0.06rem solid #ddd;
|
||||
border-top: none;
|
||||
}
|
||||
a { color: #3498db; text-decoration: underline; }
|
||||
@page { margin: 15mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav-bar">
|
||||
<div class="nav-content">
|
||||
<a href="/" class="back-link">
|
||||
<i class="fas fa-arrow-left"></i> 返回简历
|
||||
</a>
|
||||
<span class="nav-title">{{.Project.Name}}</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="project-container">
|
||||
<aside class="project-sidebar">
|
||||
<div class="project-name">{{.Project.Name}}</div>
|
||||
<div class="sidebar-title">项目信息</div>
|
||||
|
||||
<div class="meta-item">
|
||||
<i class="fas fa-calendar"></i>
|
||||
<span>{{.Project.StartDate}} - {{.Project.EndDate}}</span>
|
||||
</div>
|
||||
|
||||
<div class="meta-item">
|
||||
<i class="fas fa-tags"></i>
|
||||
<div class="tech-stack">
|
||||
{{ range .Project.TechStack }}
|
||||
<span class="tech-tag">{{.}}</span>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ if .Project.Description }}
|
||||
<div class="meta-item">
|
||||
<i class="fas fa-file-alt"></i>
|
||||
<span>{{.Project.Description}}</span>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<div class="links">
|
||||
{{ if .Project.URL }}
|
||||
<a href="{{.Project.URL}}" target="_blank">
|
||||
<i class="fas fa-external-link-alt"></i> 项目链接
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ if .Project.Doc }}
|
||||
<a href="#doc-content">
|
||||
<i class="fas fa-scroll"></i> 项目文档
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="project-main">
|
||||
<div id="doc-content" class="doc-content">
|
||||
{{ if .Project.Doc }}
|
||||
<div id="markdown-output" data-markdown="{{ .Project.Doc }}"></div>
|
||||
{{ else }}
|
||||
<div class="no-doc">
|
||||
<i class="fas fa-file"></i>
|
||||
<p>该项目暂无详细文档</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/highlight.js@11.8.0/lib/highlight.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
||||
|
||||
<script>
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'default',
|
||||
flowchart: { htmlLabels: false },
|
||||
graph: { htmlLabels: false }
|
||||
});
|
||||
|
||||
async function renderMarkdown(markdown) {
|
||||
const mermaidBlocks = [];
|
||||
const processed = markdown.replace(/```mermaid\s*([\s\S]*?)```/g, (match, content) => {
|
||||
const id = 'mermaid-' + Date.now() + '-' + mermaidBlocks.length;
|
||||
mermaidBlocks.push({ id, content: content.trim() });
|
||||
return `<div id="${id}" class="mermaid"></div>`;
|
||||
});
|
||||
|
||||
const html = DOMPurify.sanitize(marked.parse(processed), {
|
||||
ADD_TAGS: ['iframe'],
|
||||
ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling']
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
|
||||
if (typeof hljs !== 'undefined') {
|
||||
container.querySelectorAll('pre code').forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
}
|
||||
|
||||
await Promise.all(mermaidBlocks.map(async (block) => {
|
||||
const el = container.querySelector('#' + block.id);
|
||||
if (el) {
|
||||
try {
|
||||
const { svg } = await mermaid.render(block.id, block.content);
|
||||
el.innerHTML = svg;
|
||||
} catch (e) {
|
||||
el.innerHTML = '<div style="color: #e74c3c; padding: 0.62rem; background: #fef5f5; border-radius: 0.25rem;">' + e.message + '</div>';
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return container.innerHTML;
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const output = document.getElementById('markdown-output');
|
||||
if (output) {
|
||||
const markdown = output.getAttribute('data-markdown');
|
||||
if (markdown && markdown.trim()) {
|
||||
output.innerHTML = await renderMarkdown(markdown);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,345 @@
|
||||
{{ define "web/sidebar" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<meta name="description" content="{{.BasicInfo.Summary}}" />
|
||||
<meta name="keywords" content="{{.BasicInfo.Title}}, {{.BasicInfo.Name}}, 简历, 作品集" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--sidebar-bg: #2c3e50;
|
||||
--sidebar-text: #ecf0f1;
|
||||
--sidebar-muted: #95a5a6;
|
||||
--accent: #3498db;
|
||||
--accent-light: #5dade2;
|
||||
--text-primary: #2c3e50;
|
||||
--text-secondary: #555;
|
||||
--text-muted: #888;
|
||||
--bg-main: #f5f7fa;
|
||||
--bg-card: #ffffff;
|
||||
--border-color: #e1e8ed;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
a { text-decoration: none; color: inherit; }
|
||||
ul { list-style: none; }
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
background: var(--bg-main);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 68.75rem;
|
||||
margin: 2.50rem auto;
|
||||
display: grid;
|
||||
grid-template-columns: 18.75rem 1fr;
|
||||
box-shadow: 0 0.25rem 2rem rgba(0,0,0,0.08);
|
||||
border-radius: 0.75rem;
|
||||
overflow: hidden;
|
||||
min-height: calc(100vh - 5rem);
|
||||
}
|
||||
|
||||
.resume-header {
|
||||
background: var(--sidebar-bg);
|
||||
color: var(--sidebar-text);
|
||||
padding: 2.50rem 1.75rem;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.75rem;
|
||||
border-bottom: 0.06rem solid rgba(255,255,255,0.1);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 7.50rem;
|
||||
height: 7.50rem;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-light));
|
||||
margin: 0 auto 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2.62rem;
|
||||
color: white;
|
||||
font-weight: 300;
|
||||
border: 0.25rem solid rgba(255,255,255,0.15);
|
||||
}
|
||||
|
||||
.sidebar-name {
|
||||
font-size: 1.38rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.sidebar-title {
|
||||
font-size: 0.88rem;
|
||||
color: var(--sidebar-muted);
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.62rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.62rem;
|
||||
font-size: 0.81rem;
|
||||
color: var(--sidebar-text);
|
||||
}
|
||||
|
||||
.resume-header .contact-tag i {
|
||||
width: 1rem;
|
||||
text-align: center;
|
||||
color: var(--accent-light);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.resume-header .contact-tag a {
|
||||
color: var(--accent-light);
|
||||
}
|
||||
|
||||
.resume-header .meta-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.50rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.resume-header .meta-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--sidebar-muted);
|
||||
}
|
||||
|
||||
.resume-header .summary {
|
||||
margin-top: 1.25rem;
|
||||
padding: 1rem;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 0.38rem;
|
||||
font-size: 0.81rem;
|
||||
line-height: 1.7;
|
||||
color: var(--sidebar-text);
|
||||
}
|
||||
|
||||
.resume-body {
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.resume-body-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.resume-body .sidebar {
|
||||
padding: 2rem 1.75rem;
|
||||
background: var(--bg-main);
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.resume-body .main-content {
|
||||
padding: 2.50rem 3rem;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.50rem;
|
||||
border-bottom: 0.12rem solid var(--accent);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
color: var(--accent);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
background: var(--bg-card);
|
||||
border-radius: 0.38rem;
|
||||
padding: 1rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-bottom: 1.25rem;
|
||||
padding: 1rem;
|
||||
background: var(--bg-card);
|
||||
border-radius: 0.38rem;
|
||||
border-left: 0.19rem solid var(--accent);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.item:last-child { margin-bottom: 0; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.50rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: var(--text-primary); font-size: 0.88rem; }
|
||||
.item-header .period { color: var(--text-muted); font-size: 0.75rem; }
|
||||
.item-header a { color: var(--accent); font-size: 0.81rem; }
|
||||
|
||||
.position { color: var(--accent); font-weight: 500; margin-bottom: 0.50rem; font-size: 0.81rem; }
|
||||
.degree { color: var(--text-secondary); margin-bottom: 0.31rem; font-size: 0.75rem; }
|
||||
.gpa { color: var(--text-muted); font-size: 0.69rem; }
|
||||
|
||||
.description { color: var(--text-secondary); line-height: 1.6; margin-bottom: 0.62rem; font-size: 0.81rem; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: var(--text-secondary); padding: 0.31rem 0; font-size: 0.81rem; display: flex; align-items: flex-start; gap: 0.50rem; }
|
||||
.highlights li i { color: #27ae60; font-size: 0.75rem; margin-top: 0.19rem; }
|
||||
|
||||
.skill-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.50rem;
|
||||
}
|
||||
|
||||
.skill-category {
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.skill-category:last-child { margin-bottom: 0; }
|
||||
|
||||
.skill-category-title {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.03rem;
|
||||
margin-bottom: 0.62rem;
|
||||
padding-bottom: 0.38rem;
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
}
|
||||
|
||||
.skill-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.skill-tag {
|
||||
padding: 0.25rem 0.62rem;
|
||||
background: rgba(52, 152, 219, 0.1);
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--accent);
|
||||
border: 0.06rem solid rgba(52, 152, 219, 0.2);
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.62rem; }
|
||||
.tech-tag { padding: 0.19rem 0.50rem; background: var(--bg-main); color: var(--text-secondary); border-radius: 0.25rem; font-size: 0.69rem; border: 0.06rem solid var(--border-color); }
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.50rem; }
|
||||
.platform-tag { padding: 0.19rem 0.50rem; background: rgba(52, 152, 219, 0.1); color: var(--accent); border-radius: 0.19rem; font-size: 0.69rem; border: 0.06rem solid rgba(52, 152, 219, 0.2); }
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: var(--text-secondary);
|
||||
padding: 0.50rem 0;
|
||||
font-size: 0.81rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.advantage-list li i { color: var(--accent); font-size: 0.81rem; margin-top: 0.12rem; }
|
||||
|
||||
.sidebar-summary {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.81rem;
|
||||
line-height: 1.7;
|
||||
padding: 0.75rem;
|
||||
background: var(--bg-card);
|
||||
border-radius: 0.38rem;
|
||||
border-left: 0.19rem solid var(--accent);
|
||||
}
|
||||
|
||||
.achievements {
|
||||
margin-top: 0.62rem;
|
||||
padding-top: 0.62rem;
|
||||
border-top: 0.06rem dashed var(--border-color);
|
||||
}
|
||||
|
||||
.achievements h4 {
|
||||
font-size: 0.81rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0.38rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.38rem;
|
||||
}
|
||||
|
||||
.achievements h4 i {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.achievements ul {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.achievements li {
|
||||
font-size: 0.81rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container {
|
||||
grid-template-columns: 1fr;
|
||||
margin: 1.25rem;
|
||||
min-height: auto;
|
||||
}
|
||||
.resume-header { padding: 2rem 1.50rem; }
|
||||
.resume-body .sidebar { padding: 1.50rem; }
|
||||
.resume-body .main-content { padding: 1.50rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
{{ template "component-basic-info" .BasicInfo }}
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,359 @@
|
||||
{{ define "web/tech" }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{.BasicInfo.Name}} - {{.BasicInfo.Title}}</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<style>
|
||||
:root {
|
||||
--bg-primary: #0d1117;
|
||||
--bg-secondary: #161b22;
|
||||
--bg-tertiary: #21262d;
|
||||
--bg-card: #1c2128;
|
||||
--border-color: #30363d;
|
||||
--text-primary: #e6edf3;
|
||||
--text-secondary: #8b949e;
|
||||
--text-muted: #6e7681;
|
||||
--accent-color: #58a6ff;
|
||||
--accent-green: #3fb950;
|
||||
--accent-orange: #d29922;
|
||||
--accent-red: #f85149;
|
||||
--accent-purple: #c792ea;
|
||||
}
|
||||
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
|
||||
opacity: 0.02;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
::selection { background: rgba(88, 166, 255, 0.3); }
|
||||
|
||||
.theme-switch { position: fixed; top: 1.25rem; right: 1.25rem; z-index: 1000; }
|
||||
.theme-switch button {
|
||||
background: var(--bg-card);
|
||||
border: 0.06rem solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
padding: 0.44rem 0.88rem;
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.50rem;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.theme-switch button:hover {
|
||||
background: var(--bg-tertiary);
|
||||
border-color: var(--accent-color);
|
||||
color: var(--accent-color);
|
||||
transform: translateY(-0.06rem);
|
||||
}
|
||||
|
||||
.resume-container {
|
||||
max-width: 55rem;
|
||||
margin: 2.50rem auto;
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 0.38rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
header.resume-header {
|
||||
background: linear-gradient(180deg, #1a1f2e 0%, #161b22 100%);
|
||||
padding: 2.50rem;
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
}
|
||||
.header-inner { display: flex; align-items: flex-start; gap: 1.56rem; }
|
||||
header.resume-header .avatar img {
|
||||
width: 7.50rem;
|
||||
height: 7.50rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 0.12rem solid var(--border-color);
|
||||
object-fit: cover;
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.code-block {
|
||||
background: var(--bg-primary);
|
||||
padding: 1.25rem;
|
||||
border-radius: 0.25rem;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
font-family: inherit;
|
||||
font-size: 0.82em;
|
||||
line-height: 1.7;
|
||||
flex: 1;
|
||||
}
|
||||
.code-line { display: flex; gap: 0.88rem; margin-bottom: 0.25rem; }
|
||||
.code-line:last-child { margin-bottom: 0; }
|
||||
.code-number { color: var(--text-muted); user-select: none; min-width: 1.75rem; text-align: right; }
|
||||
.code-keyword { color: var(--accent-purple); }
|
||||
.code-string { color: var(--accent-green); }
|
||||
.code-function { color: var(--accent-orange); }
|
||||
.code-variable { color: var(--accent-color); }
|
||||
.code-comment { color: var(--text-muted); font-style: italic; }
|
||||
|
||||
.typewriter {
|
||||
overflow: hidden;
|
||||
border-right: 0.12rem solid var(--accent-green);
|
||||
animation: typing 2.5s steps(40, end), blink-caret 0.75s step-end infinite;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@keyframes typing {
|
||||
from { width: 0; }
|
||||
to { width: 100%; }
|
||||
}
|
||||
|
||||
@keyframes blink-caret {
|
||||
from, to { border-color: transparent; }
|
||||
50% { border-color: var(--accent-green); }
|
||||
}
|
||||
|
||||
main.resume-body { padding: 2.50rem; }
|
||||
|
||||
section { margin-bottom: 2.38rem; }
|
||||
section:last-child { margin-bottom: 0; }
|
||||
|
||||
.section-title {
|
||||
font-size: 0.95em;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 1.12rem;
|
||||
padding-bottom: 0.62rem;
|
||||
border-bottom: 0.06rem solid var(--border-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.62rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06rem;
|
||||
}
|
||||
.section-title i { color: var(--accent-color); font-size: 0.9em; }
|
||||
|
||||
.section-content { background: var(--bg-primary); border-radius: 0.25rem; border: 0.06rem solid var(--border-color); padding: 1.25rem; }
|
||||
|
||||
article.item { padding: 0.88rem 0; border-bottom: 0.06rem dashed var(--border-color); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
article.item:last-child { border-bottom: none; }
|
||||
article.item:hover { background: rgba(88, 166, 255, 0.04); margin: 0 -1.25rem; padding-left: 1.25rem; padding-right: 1.25rem; border-radius: 0.19rem; }
|
||||
|
||||
.item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.50rem; }
|
||||
.item-header .company, .item-header .school, .item-header .project-name { font-weight: 600; color: var(--text-primary); font-size: 0.95em; }
|
||||
.item-header .period { color: var(--text-muted); font-size: 0.82em; }
|
||||
.item-header a { color: var(--accent-color); font-size: 0.9em; transition: color 0.3s; position: relative; }
|
||||
.item-header a::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0.06rem;
|
||||
bottom: -0.12rem;
|
||||
left: 0;
|
||||
background: var(--accent-color);
|
||||
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.item-header a:hover::after { width: 100%; }
|
||||
.item-header a:hover { color: #79c0ff; }
|
||||
|
||||
.position { color: var(--accent-green); font-weight: 500; margin-bottom: 0.50rem; font-size: 0.9em; }
|
||||
.degree { color: var(--text-secondary); margin-bottom: 0.31rem; font-size: 0.88em; }
|
||||
.gpa { color: var(--text-muted); font-size: 0.82em; }
|
||||
|
||||
.description { color: var(--text-secondary); line-height: 1.6; margin-bottom: 0.62rem; font-size: 0.88em; }
|
||||
|
||||
.highlights { list-style: none; padding-left: 0; }
|
||||
.highlights li { color: var(--text-secondary); padding: 0.31rem 0; font-size: 0.88em; display: flex; align-items: flex-start; gap: 0.62rem; line-height: 1.6; }
|
||||
.highlights li i { color: var(--accent-green); font-size: 0.78em; margin-top: 0.25rem; }
|
||||
|
||||
.skill-items { display: flex; flex-wrap: wrap; gap: 0.50rem; }
|
||||
.skill-tag {
|
||||
padding: 0.31rem 0.75rem;
|
||||
background: var(--bg-tertiary);
|
||||
border: 0.06rem solid var(--border-color);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.82em;
|
||||
color: var(--text-primary);
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.skill-tag:hover {
|
||||
background: var(--accent-color);
|
||||
border-color: var(--accent-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tech-stack { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.62rem; }
|
||||
.tech-tag {
|
||||
padding: 0.19rem 0.56rem;
|
||||
background: rgba(88, 166, 255, 0.12);
|
||||
color: var(--accent-color);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.78em;
|
||||
border: 0.06rem solid rgba(88, 166, 255, 0.25);
|
||||
}
|
||||
|
||||
.platforms { display: flex; flex-wrap: wrap; gap: 0.38rem; margin-top: 0.50rem; }
|
||||
.platform-tag {
|
||||
padding: 0.12rem 0.50rem;
|
||||
background: rgba(63, 185, 80, 0.1);
|
||||
color: var(--accent-green);
|
||||
border-radius: 0.19rem;
|
||||
font-size: 0.76em;
|
||||
border: 0.06rem solid rgba(63, 185, 80, 0.2);
|
||||
}
|
||||
|
||||
.advantage-list { list-style: none; padding-left: 0; }
|
||||
.advantage-list li {
|
||||
color: var(--text-secondary);
|
||||
padding: 0.44rem 0;
|
||||
font-size: 0.88em;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.62rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.advantage-list li i { color: var(--accent-color); font-size: 0.82em; margin-top: 0.12rem; }
|
||||
|
||||
.skill-progress { margin-bottom: 0.88rem; }
|
||||
.skill-progress:last-child { margin-bottom: 0; }
|
||||
.skill-progress-label { display: flex; justify-content: space-between; font-size: 0.82em; margin-bottom: 0.25rem; }
|
||||
.skill-progress-label span:first-child { color: var(--text-primary); }
|
||||
.skill-progress-label span:last-child { color: var(--accent-color); }
|
||||
.skill-progress-bar {
|
||||
height: 0.38rem;
|
||||
background: var(--bg-tertiary);
|
||||
border-radius: 0.19rem;
|
||||
overflow: hidden;
|
||||
border: 0.06rem solid var(--border-color);
|
||||
}
|
||||
.skill-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--accent-color), var(--accent-green));
|
||||
border-radius: 0.12rem;
|
||||
transition: width 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(0.50rem); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
section { animation: fadeIn 0.5s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
.resume-container { margin: 0.94rem; }
|
||||
header.resume-header { padding: 1.56rem; }
|
||||
main.resume-body { padding: 1.56rem; }
|
||||
.header-inner { flex-direction: column; text-align: center; }
|
||||
.item-header { flex-direction: column; gap: 0.31rem; }
|
||||
.code-block { font-size: 0.72em; }
|
||||
}
|
||||
|
||||
@media print {
|
||||
body { background: white; color: #000; }
|
||||
body::before { display: none; }
|
||||
.theme-switch { display: none; }
|
||||
.resume-container {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: white;
|
||||
}
|
||||
header.resume-header {
|
||||
background: #1a1a1a;
|
||||
color: white;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
.section-content { background: #f5f5f5; border: 0.06rem solid #ddd; }
|
||||
article.item:hover {
|
||||
background: transparent;
|
||||
margin: 0;
|
||||
padding: 0.88rem 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
a { color: #333; text-decoration: underline; }
|
||||
.typewriter { border-right: none; }
|
||||
@page { margin: 15mm; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{ template "theme-switch" . }}
|
||||
|
||||
<div class="resume-container">
|
||||
<header class="resume-header">
|
||||
<div class="header-inner">
|
||||
{{ template "component-avatar" .BasicInfo }}
|
||||
<div class="code-block">
|
||||
<div class="code-line">
|
||||
<span class="code-number">1</span>
|
||||
<span><span class="code-keyword">package</span> <span class="code-function">resume</span></span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">2</span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">3</span>
|
||||
<span><span class="code-keyword">type</span> <span class="code-function">{{.BasicInfo.Name}}</span> <span class="code-keyword">struct</span> {</span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">4</span>
|
||||
<span> Name <span class="code-keyword">string</span> = <span class="code-string">"{{.BasicInfo.Name}}"</span></span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">5</span>
|
||||
<span> Title <span class="code-keyword">string</span> = <span class="typewriter code-string">"{{.BasicInfo.Title}}"</span></span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">6</span>
|
||||
<span> Email <span class="code-keyword">string</span> = <span class="code-string">"{{.BasicInfo.Email}}"</span></span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">7</span>
|
||||
<span> Location <span class="code-keyword">string</span> = <span class="code-string">"{{.BasicInfo.Location}}"</span></span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">8</span>
|
||||
<span>}</span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">9</span>
|
||||
</div>
|
||||
<div class="code-line">
|
||||
<span class="code-number">10</span>
|
||||
<span class="code-comment">// {{.BasicInfo.Summary}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main class="resume-body">
|
||||
{{ template "section-core-advantages" . }}
|
||||
{{ template "section-experience" . }}
|
||||
{{ template "section-education" . }}
|
||||
{{ template "section-skills-progress" . }}
|
||||
{{ template "section-projects" . }}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ template "theme-script" . }}
|
||||
<script src="/static/js/security.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
Reference in New Issue
Block a user