Files
resume/templates/web/base.html
T

424 lines
18 KiB
HTML

{{ 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 }}