Files

1130 lines
66 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
<div class="border-b border-slate-200 px-6 py-4 bg-slate-50">
<div>
<h3 class="font-semibold text-slate-800 flex items-center gap-2">
<i class="fas fa-file-alt text-purple-500"></i> {{ if .IsNew }}新建简历{{ else }}编辑简历 - {{.Resume.BasicInfo.Name}}{{ end }}
</h3>
<p class="text-sm text-slate-500 mt-1">{{.User.Name}} 的简历管理</p>
</div>
</div>
<div class="flex">
<div class="w-64 border-r border-slate-200 bg-slate-50/50">
<nav class="p-4 space-y-1">
<button onclick="switchTab('basic')" class="tab-btn w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left text-sm font-medium transition-all bg-blue-600 text-white shadow-md shadow-blue-500/30" data-tab="basic">
<i class="fas fa-user text-lg"></i> 基本信息
</button>
<button onclick="switchTab('experience')" class="tab-btn w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left text-sm font-medium transition-all text-slate-600 hover:bg-slate-100 hover:text-slate-800" data-tab="experience">
<i class="fas fa-briefcase text-lg"></i> 工作经历
</button>
<button onclick="switchTab('education')" class="tab-btn w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left text-sm font-medium transition-all text-slate-600 hover:bg-slate-100 hover:text-slate-800" data-tab="education">
<i class="fas fa-graduation-cap text-lg"></i> 教育背景
</button>
<button onclick="switchTab('skills')" class="tab-btn w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left text-sm font-medium transition-all text-slate-600 hover:bg-slate-100 hover:text-slate-800" data-tab="skills">
<i class="fas fa-code text-lg"></i> 技能特长
</button>
<button onclick="switchTab('projects')" class="tab-btn w-full flex items-center gap-3 px-4 py-3 rounded-xl text-left text-sm font-medium transition-all text-slate-600 hover:bg-slate-100 hover:text-slate-800" data-tab="projects">
<i class="fas fa-folder-open text-lg"></i> 项目经验
</button>
</nav>
</div>
<div class="flex-1 p-6">
<div id="tab-basic" class="tab-content">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">访问路由</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-link"></i>
</div>
<input type="text" id="basic-route" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.Route}}" placeholder="如 zhangsan">
</div>
<p class="text-xs text-slate-500 mt-1">访问地址: /[路由]</p>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">简历模板</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-palette"></i>
</div>
<select id="basic-template" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all appearance-none bg-white">
<option value="modern" {{ if eq .Resume.Template "modern" }}selected{{ end }}>现代商务</option>
<option value="classic" {{ if eq .Resume.Template "classic" }}selected{{ end }}>传统经典</option>
<option value="tech" {{ if eq .Resume.Template "tech" }}selected{{ end }}>科技极客</option>
<option value="sidebar" {{ if eq .Resume.Template "sidebar" }}selected{{ end }}>经典侧边栏</option>
<option value="elegant" {{ if eq .Resume.Template "elegant" }}selected{{ end }}>优雅轻奢</option>
<option value="fresh" {{ if eq .Resume.Template "fresh" }}selected{{ end }}>清新自然</option>
<option value="minimalist" {{ if eq .Resume.Template "minimalist" }}selected{{ end }}>极简风格</option>
<option value="professional" {{ if eq .Resume.Template "professional" }}selected{{ end }}>专业商务</option>
</select>
</div>
</div>
<div class="flex items-end">
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox" id="basic-showInHome" class="w-4 h-4 text-blue-600 rounded focus:ring-blue-500" {{ if .Resume.ShowInHome }}checked{{ end }}>
<span class="text-sm font-medium text-slate-700">在首页展示</span>
</label>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">姓名</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-user"></i>
</div>
<input type="text" id="basic-name" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Name}}" required>
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">职位</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-briefcase"></i>
</div>
<input type="text" id="basic-title" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Title}}" placeholder="如 高级后端工程师">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">邮箱</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-envelope"></i>
</div>
<input type="email" id="basic-email" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Email}}">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">手机号</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-phone"></i>
</div>
<input type="tel" id="basic-phone" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Phone}}">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">所在地</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-map-marker-alt"></i>
</div>
<input type="text" id="basic-location" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Location}}" placeholder="如 北京市">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">个人网站</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-globe"></i>
</div>
<input type="url" id="basic-website" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Website}}" placeholder="https://">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">工作年限</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-calendar"></i>
</div>
<input type="text" id="basic-experienceYears" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.ExperienceYears}}" placeholder="如 10年">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">求职目标</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-target"></i>
</div>
<input type="text" id="basic-jobTarget" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.JobTarget}}" placeholder="如 高级后端工程师">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">所属行业</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-building"></i>
</div>
<input type="text" id="basic-industry" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Industry}}" placeholder="如 互联网、医疗">
</div>
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1.5">头像URL</label>
<div class="relative">
<div class="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400">
<i class="fas fa-image"></i>
</div>
<input type="url" id="basic-avatar" class="w-full pl-10 pr-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" value="{{.Resume.BasicInfo.Avatar}}">
</div>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-slate-700 mb-1.5">个人简介</label>
<textarea id="basic-summary" rows="6" class="w-full px-4 py-3 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all resize-none" placeholder="简要介绍自己的核心竞争力和职业发展方向...">{{.Resume.BasicInfo.Summary}}</textarea>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-medium text-slate-700 mb-1.5">核心优势</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" id="coreAdvantageInput" class="flex-1 px-4 py-2.5 border border-slate-200 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" placeholder="输入核心优势,按回车添加">
<button onclick="addCoreAdvantage()" class="btn btn-primary">
<i class="fas fa-plus"></i> 添加
</button>
</div>
<div id="coreAdvantagesList" class="flex flex-wrap gap-2">
{{ range .Resume.BasicInfo.CoreAdvantages }}
<span class="inline-flex items-center gap-1 px-3 py-1.5 bg-blue-50 text-blue-600 rounded-full text-sm">
{{.}}
<button onclick="removeCoreAdvantage(this)" class="hover:text-blue-800"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
</div>
</div>
<div id="tab-experience" class="tab-content hidden relative">
<div class="flex items-center justify-between mb-6">
<h4 class="font-semibold text-slate-800">工作经历列表</h4>
<button onclick="addExperience()" class="btn btn-primary">
<i class="fas fa-plus"></i> 添加工作经历
</button>
</div>
<div id="experienceList" class="space-y-4">
{{ range .Resume.Experience }}
<div class="experience-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move" draggable="true" ondragstart="dragStart(event, 'experience')">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium exp-company">{{.Company}}</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium exp-position">{{.Position}}</span>
</div>
<button onclick="removeExperience(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">公司名称</label>
<input type="text" class="exp-company-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Company}}" placeholder="输入公司名称" onchange="updateExpCompany(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">职位</label>
<input type="text" class="exp-position-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Position}}" placeholder="输入职位" onchange="updateExpPosition(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">开始日期</label>
<input type="text" class="exp-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.StartDate}}" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">结束日期</label>
<input type="text" class="exp-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.EndDate}}" placeholder="YYYY-MM 或 至今">
</div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-1">工作描述</label>
<textarea class="exp-description w-full px-3 py-2 border border-slate-200 rounded-lg text-sm resize-none" rows="3" placeholder="描述你的工作职责和主要内容...">{{.Description}}</textarea>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">工作亮点</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="exp-highlight-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入亮点,按回车添加">
<button onclick="addExpHighlight(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="exp-highlights-list flex flex-wrap gap-2">
{{ range .Highlights }}
<span class="inline-flex items-center gap-1 px-2 py-1 bg-amber-50 text-amber-700 rounded-full text-xs">
{{.}}
<button onclick="removeExpHighlight(this)" class="hover:text-amber-900"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-2">负责平台</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="exp-platform-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入平台名称,按回车添加">
<button onclick="addExpPlatform(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="exp-platforms-list flex flex-wrap gap-2">
{{ range .Platforms }}
<span class="inline-flex items-center gap-1 px-2 py-1 bg-green-50 text-green-700 rounded-full text-xs">
{{.}}
<button onclick="removeExpPlatform(this)" class="hover:text-green-900"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
</div>
{{ else }}
<div id="experienceEmpty" class="text-center py-12 text-slate-500">
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-100 flex items-center justify-center">
<i class="fas fa-briefcase text-slate-400 text-2xl"></i>
</div>
<p>暂无工作经历</p>
</div>
{{ end }}
</div>
</div>
<div id="tab-education" class="tab-content hidden relative">
<div class="flex items-center justify-between mb-6">
<h4 class="font-semibold text-slate-800">教育背景列表</h4>
<button onclick="addEducation()" class="btn btn-primary">
<i class="fas fa-plus"></i> 添加教育背景
</button>
</div>
<div id="educationList" class="space-y-4">
{{ range .Resume.Education }}
<div class="education-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move" draggable="true" ondragstart="dragStart(event, 'education')">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium edu-school">{{.School}}</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium edu-degree">{{.Degree}}</span>
<span class="px-3 py-1 bg-blue-50 text-blue-700 rounded-full text-xs font-medium edu-major">{{.Major}}</span>
</div>
<button onclick="removeEducation(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">学校名称</label>
<input type="text" class="edu-school-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.School}}" placeholder="输入学校名称" onchange="updateEduSchool(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">学位</label>
<input type="text" class="edu-degree-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Degree}}" placeholder="如 本科、硕士" onchange="updateEduDegree(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">专业</label>
<input type="text" class="edu-major-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Major}}" placeholder="输入专业" onchange="updateEduMajor(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">入学日期</label>
<input type="text" class="edu-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.StartDate}}" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">毕业日期</label>
<input type="text" class="edu-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.EndDate}}" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">GPA</label>
<input type="text" class="edu-gpa w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.GPA}}" placeholder="如 3.8/4.0">
</div>
</div>
</div>
{{ else }}
<div id="educationEmpty" class="text-center py-12 text-slate-500">
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-100 flex items-center justify-center">
<i class="fas fa-graduation-cap text-slate-400 text-2xl"></i>
</div>
<p>暂无教育背景</p>
</div>
{{ end }}
</div>
</div>
<div id="tab-skills" class="tab-content hidden relative">
<div class="flex items-center justify-between mb-6">
<h4 class="font-semibold text-slate-800">技能特长列表</h4>
<button onclick="addSkill()" class="btn btn-primary">
<i class="fas fa-plus"></i> 添加技能
</button>
</div>
<div id="skillsList" class="space-y-4">
{{ range .Resume.Skills }}
<div class="skill-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move" draggable="true" ondragstart="dragStart(event, 'skills')">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium">{{.Name}}</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium">{{.Level}}</span>
<span class="px-3 py-1 bg-orange-50 text-orange-700 rounded-full text-xs font-medium">{{.Category}}</span>
</div>
<button onclick="removeSkill(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">技能名称</label>
<input type="text" class="skill-name w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Name}}" placeholder="如 Go、React">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">熟练程度</label>
<select class="skill-level w-full px-3 py-2 border border-slate-200 rounded-lg text-sm">
<option value="beginner" {{ if eq .Level "beginner" }}selected{{ end }}>入门 (Beginner)</option>
<option value="intermediate" {{ if eq .Level "intermediate" }}selected{{ end }}>进阶 (Intermediate)</option>
<option value="advanced" {{ if eq .Level "advanced" }}selected{{ end }}>高级 (Advanced)</option>
<option value="expert" {{ if eq .Level "expert" }}selected{{ end }}>专家 (Expert)</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">技能分类</label>
<input type="text" class="skill-category w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Category}}" placeholder="如 后端、前端、数据库">
</div>
</div>
</div>
{{ else }}
<div id="skillsEmpty" class="text-center py-12 text-slate-500">
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-100 flex items-center justify-center">
<i class="fas fa-code text-slate-400 text-2xl"></i>
</div>
<p>暂无技能特长</p>
</div>
{{ end }}
</div>
</div>
<div id="tab-projects" class="tab-content hidden relative">
<div class="flex items-center justify-between mb-6">
<h4 class="font-semibold text-slate-800">项目经验列表</h4>
<button onclick="addProject()" class="btn btn-primary">
<i class="fas fa-plus"></i> 添加项目
</button>
</div>
<div id="projectsList" class="space-y-4">
{{ range .Resume.Projects }}
<div class="project-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move" draggable="true" ondragstart="dragStart(event, 'projects')">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-indigo-100 text-indigo-700 rounded-full text-xs font-medium proj-name">{{.Name}}</span>
<a href="{{.URL}}" target="_blank" class="px-3 py-1 bg-blue-50 text-blue-700 rounded-full text-xs font-medium hover:bg-blue-100 transition-colors proj-url">
<i class="fas fa-external-link-alt"></i> 链接
</a>
</div>
<button onclick="removeProject(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">项目名称</label>
<input type="text" class="proj-name-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.Name}}" placeholder="输入项目名称" onchange="updateProjName(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">项目链接</label>
<input type="url" class="proj-url-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.URL}}" placeholder="https://" onchange="updateProjUrl(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">开始日期</label>
<input type="text" class="proj-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.StartDate}}" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">结束日期</label>
<input type="text" class="proj-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" value="{{.EndDate}}" placeholder="YYYY-MM 或 至今">
</div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-1">项目描述</label>
<textarea class="proj-description w-full px-3 py-2 border border-slate-200 rounded-lg text-sm resize-none" rows="3" placeholder="描述项目的背景、目标和你的角色...">{{.Description}}</textarea>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">技术栈</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-tech-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入技术栈,按回车添加">
<button onclick="addProjTech(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-tech-list flex flex-wrap gap-2">
{{ range .TechStack }}
<span class="inline-flex items-center gap-1 px-2 py-1 bg-cyan-50 text-cyan-700 rounded-full text-xs">
{{.}}
<button onclick="removeProjTech(this)" class="hover:text-cyan-900"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">项目亮点</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-highlight-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入亮点,按回车添加">
<button onclick="addProjHighlight(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-highlights-list flex flex-wrap gap-2">
{{ range .Highlights }}
<span class="inline-flex items-center gap-1 px-2 py-1 bg-amber-50 text-amber-700 rounded-full text-xs">
{{.}}
<button onclick="removeProjHighlight(this)" class="hover:text-amber-900"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-2">项目成果</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-achievement-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入成果,按回车添加">
<button onclick="addProjAchievement(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-achievements-list flex flex-wrap gap-2">
{{ range .Achievements }}
<span class="inline-flex items-center gap-1 px-2 py-1 bg-green-50 text-green-700 rounded-full text-xs">
{{.}}
<button onclick="removeProjAchievement(this)" class="hover:text-green-900"><i class="fas fa-times"></i></button>
</span>
{{ end }}
</div>
</div>
</div>
{{ else }}
<div id="projectsEmpty" class="text-center py-12 text-slate-500">
<div class="w-16 h-16 mx-auto mb-4 rounded-full bg-slate-100 flex items-center justify-center">
<i class="fas fa-folder-open text-slate-400 text-2xl"></i>
</div>
<p>暂无项目经验</p>
</div>
{{ end }}
</div>
</div>
</div>
</div>
</div>
<input type="hidden" id="resume-id" value="{{.Resume.ID}}">
<input type="hidden" id="user-id" value="{{.User.ID}}">
<div class="fixed bottom-0 left-0 right-0 bg-white border-t border-slate-200 px-6 py-4 shadow-lg z-50" style="padding-left: 17.50rem;">
<div class="flex items-center justify-center gap-3">
<button onclick="goBack()" class="btn btn-secondary">
<i class="fas fa-arrow-left"></i> 返回
</button>
<button onclick="previewResume()" class="btn btn-info">
<i class="fas fa-eye"></i> 预览
</button>
<button onclick="saveResume()" class="btn btn-primary">
<i class="fas fa-save"></i> 保存简历
</button>
</div>
</div>
<div style="height: 5rem;"></div>
<script>
function switchTab(tabName) {
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
document.querySelectorAll('.tab-btn').forEach(el => {
el.classList.remove('bg-blue-600', 'text-white', 'shadow-md', 'shadow-blue-500/30');
el.classList.add('text-slate-600', 'hover:bg-slate-100', 'hover:text-slate-800');
});
document.getElementById('tab-' + tabName).classList.remove('hidden');
const activeBtn = document.querySelector('[data-tab="' + tabName + '"]');
activeBtn.classList.remove('text-slate-600', 'hover:bg-slate-100', 'hover:text-slate-800');
activeBtn.classList.add('bg-blue-600', 'text-white', 'shadow-md', 'shadow-blue-500/30');
}
function getCoreAdvantages() {
const list = document.getElementById('coreAdvantagesList');
const items = list.querySelectorAll('span');
return Array.from(items).map(item => item.textContent.trim().replace(/×$/, ''));
}
function addCoreAdvantage() {
const input = document.getElementById('coreAdvantageInput');
const value = input.value.trim();
if (!value) return;
const list = document.getElementById('coreAdvantagesList');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-3 py-1.5 bg-blue-50 text-blue-600 rounded-full text-sm';
span.innerHTML = value + ' <button onclick="removeCoreAdvantage(this)" class="hover:text-blue-800"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeCoreAdvantage(el) {
el.parentElement.remove();
}
document.getElementById('coreAdvantageInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addCoreAdvantage();
}
});
function addExperience() {
const list = document.getElementById('experienceList');
const empty = document.getElementById('experienceEmpty');
if (empty) empty.classList.add('hidden');
const item = document.createElement('div');
item.className = 'experience-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move';
item.draggable = true;
item.ondragstart = function(e) { dragStart(e, 'experience'); };
item.ondragover = function(e) { dragOver(e); };
item.ondrop = function(e) { dragDrop(e, 'experience'); };
item.innerHTML = `
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-blue-100 text-blue-700 rounded-full text-xs font-medium exp-company">公司名称</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium exp-position">职位</span>
</div>
<button onclick="removeExperience(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">公司名称</label>
<input type="text" class="exp-company-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入公司名称" onchange="updateExpCompany(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">职位</label>
<input type="text" class="exp-position-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入职位" onchange="updateExpPosition(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">开始日期</label>
<input type="text" class="exp-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">结束日期</label>
<input type="text" class="exp-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM 或 至今">
</div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-1">工作描述</label>
<textarea class="exp-description w-full px-3 py-2 border border-slate-200 rounded-lg text-sm resize-none" rows="3" placeholder="描述你的工作职责和主要内容..."></textarea>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">工作亮点</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="exp-highlight-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入亮点,按回车添加" onkeypress="handleEnterKey(event, addExpHighlight)">
<button onclick="addExpHighlight(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="exp-highlights-list flex flex-wrap gap-2"></div>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-2">负责平台</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="exp-platform-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入平台名称,按回车添加" onkeypress="handleEnterKey(event, addExpPlatform)">
<button onclick="addExpPlatform(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="exp-platforms-list flex flex-wrap gap-2"></div>
</div>
`;
list.appendChild(item);
}
function removeExperience(el) {
el.closest('.experience-item').remove();
checkEmptyState('experience');
}
function updateExpCompany(el) {
const item = el.closest('.experience-item');
item.querySelector('.exp-company').textContent = el.value || '公司名称';
}
function updateExpPosition(el) {
const item = el.closest('.experience-item');
item.querySelector('.exp-position').textContent = el.value || '职位';
}
function addExpHighlight(el) {
const input = el.previousElementSibling;
const value = input.value.trim();
if (!value) return;
const list = el.closest('.experience-item').querySelector('.exp-highlights-list');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-2 py-1 bg-amber-50 text-amber-700 rounded-full text-xs';
span.innerHTML = value + ' <button onclick="removeExpHighlight(this)" class="hover:text-amber-900"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeExpHighlight(el) {
el.parentElement.remove();
}
function addExpPlatform(el) {
const input = el.previousElementSibling;
const value = input.value.trim();
if (!value) return;
const list = el.closest('.experience-item').querySelector('.exp-platforms-list');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-2 py-1 bg-green-50 text-green-700 rounded-full text-xs';
span.innerHTML = value + ' <button onclick="removeExpPlatform(this)" class="hover:text-green-900"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeExpPlatform(el) {
el.parentElement.remove();
}
function addEducation() {
const list = document.getElementById('educationList');
const empty = document.getElementById('educationEmpty');
if (empty) empty.classList.add('hidden');
const item = document.createElement('div');
item.className = 'education-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move';
item.draggable = true;
item.ondragstart = function(e) { dragStart(e, 'education'); };
item.ondragover = function(e) { dragOver(e); };
item.ondrop = function(e) { dragDrop(e, 'education'); };
item.innerHTML = `
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-green-100 text-green-700 rounded-full text-xs font-medium edu-school">学校名称</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium edu-degree">学位</span>
<span class="px-3 py-1 bg-blue-50 text-blue-700 rounded-full text-xs font-medium edu-major">专业</span>
</div>
<button onclick="removeEducation(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">学校名称</label>
<input type="text" class="edu-school-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入学校名称" onchange="updateEduSchool(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">学位</label>
<input type="text" class="edu-degree-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="如 本科、硕士" onchange="updateEduDegree(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">专业</label>
<input type="text" class="edu-major-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入专业" onchange="updateEduMajor(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">入学日期</label>
<input type="text" class="edu-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">毕业日期</label>
<input type="text" class="edu-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">GPA</label>
<input type="text" class="edu-gpa w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="如 3.8/4.0">
</div>
</div>
`;
list.appendChild(item);
}
function removeEducation(el) {
el.closest('.education-item').remove();
checkEmptyState('education');
}
function updateEduSchool(el) {
const item = el.closest('.education-item');
item.querySelector('.edu-school').textContent = el.value || '学校名称';
}
function updateEduDegree(el) {
const item = el.closest('.education-item');
item.querySelector('.edu-degree').textContent = el.value || '学位';
}
function updateEduMajor(el) {
const item = el.closest('.education-item');
item.querySelector('.edu-major').textContent = el.value || '专业';
}
function addSkill() {
const list = document.getElementById('skillsList');
const empty = document.getElementById('skillsEmpty');
if (empty) empty.classList.add('hidden');
const item = document.createElement('div');
item.className = 'skill-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move';
item.draggable = true;
item.ondragstart = function(e) { dragStart(e, 'skills'); };
item.ondragover = function(e) { dragOver(e); };
item.ondrop = function(e) { dragDrop(e, 'skills'); };
item.innerHTML = `
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-purple-100 text-purple-700 rounded-full text-xs font-medium skill-name-label">技能名称</span>
<span class="px-3 py-1 bg-slate-200 text-slate-700 rounded-full text-xs font-medium skill-level-label">入门</span>
<span class="px-3 py-1 bg-orange-50 text-orange-700 rounded-full text-xs font-medium skill-category-label">分类</span>
</div>
<button onclick="removeSkill(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">技能名称</label>
<input type="text" class="skill-name w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="如 Go、React" onchange="updateSkillName(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">熟练程度</label>
<select class="skill-level w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" onchange="updateSkillLevel(this)">
<option value="beginner">入门 (Beginner)</option>
<option value="intermediate">进阶 (Intermediate)</option>
<option value="advanced">高级 (Advanced)</option>
<option value="expert">专家 (Expert)</option>
</select>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">技能分类</label>
<input type="text" class="skill-category w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="如 后端、前端、数据库" onchange="updateSkillCategory(this)">
</div>
</div>
`;
list.appendChild(item);
}
function removeSkill(el) {
el.closest('.skill-item').remove();
checkEmptyState('skills');
}
function updateSkillName(el) {
const item = el.closest('.skill-item');
item.querySelector('.skill-name-label').textContent = el.value || '技能名称';
}
function updateSkillLevel(el) {
const item = el.closest('.skill-item');
const map = {
'beginner': '入门',
'intermediate': '进阶',
'advanced': '高级',
'expert': '专家'
};
item.querySelector('.skill-level-label').textContent = map[el.value] || el.value;
}
function updateSkillCategory(el) {
const item = el.closest('.skill-item');
item.querySelector('.skill-category-label').textContent = el.value || '分类';
}
function addProject() {
const list = document.getElementById('projectsList');
const empty = document.getElementById('projectsEmpty');
if (empty) empty.classList.add('hidden');
const item = document.createElement('div');
item.className = 'project-item bg-slate-50 rounded-xl p-5 border border-slate-200 cursor-move';
item.draggable = true;
item.ondragstart = function(e) { dragStart(e, 'projects'); };
item.ondragover = function(e) { dragOver(e); };
item.ondrop = function(e) { dragDrop(e, 'projects'); };
item.innerHTML = `
<div class="flex items-start justify-between mb-4">
<div class="flex items-center gap-2">
<span class="cursor-grab text-slate-400 hover:text-slate-600"><i class="fas fa-grip-vertical"></i></span>
<span class="px-3 py-1 bg-indigo-100 text-indigo-700 rounded-full text-xs font-medium proj-name">项目名称</span>
<a href="#" target="_blank" class="px-3 py-1 bg-blue-50 text-blue-700 rounded-full text-xs font-medium hover:bg-blue-100 transition-colors proj-url">
<i class="fas fa-external-link-alt"></i> 链接
</a>
</div>
<button onclick="removeProject(this)" class="text-slate-400 hover:text-red-500 transition-colors">
<i class="fas fa-trash"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">项目名称</label>
<input type="text" class="proj-name-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入项目名称" onchange="updateProjName(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">项目链接</label>
<input type="url" class="proj-url-input w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="https://" onchange="updateProjUrl(this)">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">开始日期</label>
<input type="text" class="proj-start-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM">
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-1">结束日期</label>
<input type="text" class="proj-end-date w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="YYYY-MM 或 至今">
</div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-1">项目描述</label>
<textarea class="proj-description w-full px-3 py-2 border border-slate-200 rounded-lg text-sm resize-none" rows="3" placeholder="描述项目的背景、目标和你的角色..."></textarea>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">技术栈</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-tech-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入技术栈,按回车添加" onkeypress="handleEnterKey(event, addProjTech)">
<button onclick="addProjTech(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-tech-list flex flex-wrap gap-2"></div>
</div>
<div class="mb-4">
<label class="block text-xs font-medium text-slate-500 mb-2">项目亮点</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-highlight-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入亮点,按回车添加" onkeypress="handleEnterKey(event, addProjHighlight)">
<button onclick="addProjHighlight(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-highlights-list flex flex-wrap gap-2"></div>
</div>
<div>
<label class="block text-xs font-medium text-slate-500 mb-2">项目成果</label>
<div class="flex items-center gap-2 mb-2">
<input type="text" class="proj-achievement-input flex-1 px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="输入成果,按回车添加" onkeypress="handleEnterKey(event, addProjAchievement)">
<button onclick="addProjAchievement(this)" class="btn btn-secondary btn-xs">添加</button>
</div>
<div class="proj-achievements-list flex flex-wrap gap-2"></div>
</div>
`;
list.appendChild(item);
}
function removeProject(el) {
el.closest('.project-item').remove();
checkEmptyState('projects');
}
function updateProjName(el) {
const item = el.closest('.project-item');
item.querySelector('.proj-name').textContent = el.value || '项目名称';
}
function updateProjUrl(el) {
const item = el.closest('.project-item');
const urlEl = item.querySelector('.proj-url');
urlEl.href = el.value || '#';
}
function addProjTech(el) {
const input = el.previousElementSibling;
const value = input.value.trim();
if (!value) return;
const list = el.closest('.project-item').querySelector('.proj-tech-list');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-2 py-1 bg-cyan-50 text-cyan-700 rounded-full text-xs';
span.innerHTML = value + ' <button onclick="removeProjTech(this)" class="hover:text-cyan-900"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeProjTech(el) {
el.parentElement.remove();
}
function addProjHighlight(el) {
const input = el.previousElementSibling;
const value = input.value.trim();
if (!value) return;
const list = el.closest('.project-item').querySelector('.proj-highlights-list');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-2 py-1 bg-amber-50 text-amber-700 rounded-full text-xs';
span.innerHTML = value + ' <button onclick="removeProjHighlight(this)" class="hover:text-amber-900"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeProjHighlight(el) {
el.parentElement.remove();
}
function addProjAchievement(el) {
const input = el.previousElementSibling;
const value = input.value.trim();
if (!value) return;
const list = el.closest('.project-item').querySelector('.proj-achievements-list');
const span = document.createElement('span');
span.className = 'inline-flex items-center gap-1 px-2 py-1 bg-green-50 text-green-700 rounded-full text-xs';
span.innerHTML = value + ' <button onclick="removeProjAchievement(this)" class="hover:text-green-900"><i class="fas fa-times"></i></button>';
list.appendChild(span);
input.value = '';
}
function removeProjAchievement(el) {
el.parentElement.remove();
}
function collectExperience() {
const items = document.querySelectorAll('.experience-item');
return Array.from(items).map(item => ({
company: item.querySelector('.exp-company-input')?.value || item.querySelector('.exp-company')?.textContent || '',
position: item.querySelector('.exp-position-input')?.value || item.querySelector('.exp-position')?.textContent || '',
start_date: item.querySelector('.exp-start-date')?.value || '',
end_date: item.querySelector('.exp-end-date')?.value || '',
description: item.querySelector('.exp-description')?.value || '',
highlights: Array.from(item.querySelectorAll('.exp-highlights-list span')).map(s => s.textContent.trim().replace(/×$/, '')),
platforms: Array.from(item.querySelectorAll('.exp-platforms-list span')).map(s => s.textContent.trim().replace(/×$/, ''))
}));
}
function collectEducation() {
const items = document.querySelectorAll('.education-item');
return Array.from(items).map(item => ({
school: item.querySelector('.edu-school-input')?.value || item.querySelector('.edu-school')?.textContent || '',
degree: item.querySelector('.edu-degree-input')?.value || item.querySelector('.edu-degree')?.textContent || '',
major: item.querySelector('.edu-major-input')?.value || item.querySelector('.edu-major')?.textContent || '',
start_date: item.querySelector('.edu-start-date')?.value || '',
end_date: item.querySelector('.edu-end-date')?.value || '',
gpa: item.querySelector('.edu-gpa')?.value || ''
}));
}
function collectSkills() {
const items = document.querySelectorAll('.skill-item');
return Array.from(items).map(item => ({
name: item.querySelector('.skill-name')?.value || item.querySelector('.skill-name-label')?.textContent || '',
level: item.querySelector('.skill-level')?.value || 'beginner',
category: item.querySelector('.skill-category')?.value || item.querySelector('.skill-category-label')?.textContent || ''
}));
}
function collectProjects() {
const items = document.querySelectorAll('.project-item');
return Array.from(items).map(item => ({
name: item.querySelector('.proj-name-input')?.value || item.querySelector('.proj-name')?.textContent || '',
description: item.querySelector('.proj-description')?.value || '',
tech_stack: Array.from(item.querySelectorAll('.proj-tech-list span')).map(s => s.textContent.trim().replace(/×$/, '')),
url: item.querySelector('.proj-url-input')?.value || '',
highlights: Array.from(item.querySelectorAll('.proj-highlights-list span')).map(s => s.textContent.trim().replace(/×$/, '')),
achievements: Array.from(item.querySelectorAll('.proj-achievements-list span')).map(s => s.textContent.trim().replace(/×$/, '')),
start_date: item.querySelector('.proj-start-date')?.value || '',
end_date: item.querySelector('.proj-end-date')?.value || '',
show_in_resume: true
}));
}
function saveResume() {
const resumeId = document.getElementById('resume-id').value;
const userId = document.getElementById('user-id').value;
const data = {
user_id: userId,
route: document.getElementById('basic-route').value,
template: document.getElementById('basic-template').value,
show_in_home: document.getElementById('basic-showInHome').checked,
basic_info: {
name: document.getElementById('basic-name').value,
title: document.getElementById('basic-title').value,
email: document.getElementById('basic-email').value,
phone: document.getElementById('basic-phone').value,
location: document.getElementById('basic-location').value,
website: document.getElementById('basic-website').value,
summary: document.getElementById('basic-summary').value,
avatar: document.getElementById('basic-avatar').value,
experience_years: document.getElementById('basic-experienceYears').value,
job_target: document.getElementById('basic-jobTarget').value,
industry: document.getElementById('basic-industry').value,
core_advantages: getCoreAdvantages()
},
experience: collectExperience(),
education: collectEducation(),
skills: collectSkills(),
projects: collectProjects()
};
let url, method;
if (resumeId) {
url = '/dashboard/api/resume/' + resumeId;
method = 'PUT';
} else {
url = '/dashboard/api/resume/create';
method = 'POST';
}
fetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => {
if (result.success) {
showCustomAlert('success', '成功', '简历保存成功');
setTimeout(() => {
window.location.href = '/dashboard/user/' + userId + '/resume';
}, 1500);
} else {
showCustomAlert('error', '错误', '保存失败:' + (result.message || '未知错误'));
}
})
.catch(error => {
console.error('Error:', error);
showCustomAlert('error', '错误', '保存失败');
});
}
function previewResume() {
const route = document.getElementById('basic-route').value;
if (!route) {
showCustomAlert('info', '提示', '请先填写访问路由');
return;
}
window.open('/' + route, '_blank');
}
function goBack() {
const userId = document.getElementById('user-id').value;
window.location.href = '/dashboard/user/' + userId + '/resume';
}
function handleEnterKey(event, callback) {
if (event.key === 'Enter') {
event.preventDefault();
callback(event.target.nextElementSibling);
}
}
function checkEmptyState(type) {
const list = document.getElementById(type + 'List');
const empty = document.getElementById(type + 'Empty');
if (empty && list && list.querySelectorAll('.' + type + '-item').length === 0) {
empty.classList.remove('hidden');
}
}
let draggedItem = null;
let draggedType = '';
function dragStart(event, type) {
draggedItem = event.target;
draggedType = type;
event.target.style.opacity = '0.5';
event.dataTransfer.effectAllowed = 'move';
}
function dragOver(event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'move';
const target = event.target.closest('.' + draggedType + '-item');
if (target && target !== draggedItem) {
target.style.borderColor = '#3b82f6';
target.style.borderWidth = '0.12rem';
}
}
function dragDrop(event, type) {
event.preventDefault();
const target = event.target.closest('.' + type + '-item');
if (target && target !== draggedItem) {
const list = document.getElementById(type + 'List');
const children = Array.from(list.children);
const fromIndex = children.indexOf(draggedItem);
const toIndex = children.indexOf(target);
if (fromIndex < toIndex) {
list.insertBefore(draggedItem, target.nextSibling);
} else {
list.insertBefore(draggedItem, target);
}
}
draggedItem.style.opacity = '1';
document.querySelectorAll('.' + type + '-item').forEach(item => {
item.style.borderColor = '';
item.style.borderWidth = '';
});
draggedItem = null;
draggedType = '';
}
document.addEventListener('DOMContentLoaded', function() {
switchTab('basic');
});
</script>