个人资料和岗位列表联调,简历优化部分页面

This commit is contained in:
2026-04-01 11:41:51 +08:00
parent 0468339d23
commit 821a950df2
42 changed files with 5935 additions and 748 deletions
+602
View File
@@ -0,0 +1,602 @@
<template>
<!-- 岗位专属简历定制弹窗步骤1居中弹窗步骤2+右侧抽屉 -->
<div v-if="modelValue" class="job-resume-custom-dialog" :class="{ 'job-resume-custom-dialog--drawer': currentStep >= 2 }">
<div class="job-resume-custom-dialog__overlay" @click="handleClose"></div>
<!-- ===== 步骤一居中弹窗 ===== -->
<div v-if="currentStep === 1" class="job-resume-custom-dialog__panel">
<div class="job-resume-custom-dialog__header">
<h2 class="job-resume-custom-dialog__title">10s快速定制岗位专属简历</h2>
<button class="job-resume-custom-dialog__close-btn" @click="handleClose" aria-label="关闭">
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__close-icon"><path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</button>
</div>
<div class="job-resume-custom-dialog__tip-bar"><span>你与该岗位的匹配度较低简历可能无法通过机筛</span></div>
<div class="job-resume-custom-dialog__job-card">
<div class="job-resume-custom-dialog__job-left">
<div class="job-resume-custom-dialog__company-icon">
<img v-if="jobInfo.companyLogoUrl" :src="jobInfo.companyLogoUrl" :alt="jobInfo.company" class="job-resume-custom-dialog__company-logo-img" />
<svg v-else viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__company-svg"><rect x="3" y="7" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/><path d="M7 7V5a2 2 0 012-2h6a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5"/><path d="M3 13h18" stroke="currentColor" stroke-width="1.5"/></svg>
</div>
<div class="job-resume-custom-dialog__job-info">
<span class="job-resume-custom-dialog__job-title">{{ jobInfo.title }}</span>
<span class="job-resume-custom-dialog__job-sub">{{ jobInfo.location }} · {{ jobInfo.company }}</span>
</div>
</div>
<div class="job-resume-custom-dialog__match-area">
<div class="job-resume-custom-dialog__match-ring">
<svg viewBox="0 0 60 60" class="job-resume-custom-dialog__ring-svg"><circle cx="30" cy="30" r="24" stroke-width="4" stroke="#E8E8E8" fill="none" opacity="0.3"/><circle cx="30" cy="30" r="24" stroke-width="4" fill="none" stroke="#4FC2C9" stroke-linecap="round" :stroke-dasharray="2*Math.PI*24" :stroke-dashoffset="2*Math.PI*24*(1-jobInfo.matchScore/10)" transform="rotate(-90 30 30)"/></svg>
<span class="job-resume-custom-dialog__match-score">{{ jobInfo.matchScore }}</span>
</div>
<span class="job-resume-custom-dialog__match-label">{{ matchLevelText }}</span>
</div>
</div>
<div class="job-resume-custom-dialog__skills-section">
<p class="job-resume-custom-dialog__skills-title">缺少{{ missingSkills.length }}项技能</p>
<div class="job-resume-custom-dialog__skills-list">
<span v-for="skill in missingSkills" :key="skill" class="job-resume-custom-dialog__skill-tag">{{ skill }}</span>
</div>
</div>
<div class="job-resume-custom-dialog__footer">
<button class="job-resume-custom-dialog__primary-btn" @click="goToStep(2)">立即定制简历</button>
<span class="job-resume-custom-dialog__skip-link" @click="handleSkip">不优化直接投递</span>
</div>
</div>
<!-- ===== 步骤2+右侧抽屉 ===== -->
<div v-if="currentStep >= 2" class="job-resume-custom-dialog__drawer">
<!-- 抽屉头部 -->
<div class="job-resume-custom-dialog__drawer-header">
<button class="job-resume-custom-dialog__close-btn" @click="handleClose" aria-label="关闭">
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__close-icon"><path d="M12 4L4 12M4 4l8 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>
</button>
<h2 class="job-resume-custom-dialog__drawer-title">生成你的岗位专属简历</h2>
</div>
<!-- 返回按钮步骤四显示 -->
<button v-if="currentStep === 4" class="job-resume-custom-dialog__back-btn" @click="goToStep(3)">返回</button>
<!-- 步骤指示器 -->
<div class="job-resume-custom-dialog__steps">
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 2 }">
<span class="job-resume-custom-dialog__step-num">1</span><span>差距分析</span>
</div>
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 3 }">
<span class="job-resume-custom-dialog__step-num">2</span><span>定制简历</span>
</div>
<div class="job-resume-custom-dialog__step" :class="{ 'job-resume-custom-dialog__step--active': currentStep === 4 }">
<span class="job-resume-custom-dialog__step-num">3</span><span>预览</span>
</div>
</div>
<!-- 抽屉内容区可滚动 -->
<div class="job-resume-custom-dialog__drawer-body">
<!-- 步骤二差距分析 -->
<template v-if="currentStep === 2">
<div class="job-resume-custom-dialog__gap-header">
<div class="job-resume-custom-dialog__gap-left">
<h3 class="job-resume-custom-dialog__gap-title">你的简历与该岗位的匹配度较低</h3>
<div class="job-resume-custom-dialog__gap-warn">
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__warn-icon"><circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/><path d="M8 5v3M8 10.5v.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
<span>匹配度低于 6.0 分的简历在筛选环节可能会被优先淘汰我们会帮你快速优化提升</span>
</div>
</div>
<div class="job-resume-custom-dialog__gap-score-area">
<div class="job-resume-custom-dialog__match-ring">
<svg viewBox="0 0 60 60" class="job-resume-custom-dialog__ring-svg"><circle cx="30" cy="30" r="24" stroke-width="4" stroke="#E8E8E8" fill="none" opacity="0.3"/><circle cx="30" cy="30" r="24" stroke-width="4" fill="none" stroke="#4FC2C9" stroke-linecap="round" :stroke-dasharray="2*Math.PI*24" :stroke-dashoffset="2*Math.PI*24*(1-jobInfo.matchScore/10)" transform="rotate(-90 30 30)"/></svg>
<span class="job-resume-custom-dialog__match-score">{{ jobInfo.matchScore }}</span>
</div>
<span class="job-resume-custom-dialog__match-label">{{ matchLevelText }}</span>
</div>
</div>
<!-- 对比卡片表格 -->
<div class="job-resume-custom-dialog__gap-table">
<!-- 第一行概览 -->
<div class="job-resume-custom-dialog__gap-row">
<!-- 标签列 -->
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
<span class="job-resume-custom-dialog__gap-cell-title">概览</span>
</div>
<!-- 岗位信息列 -->
<div class="job-resume-custom-dialog__gap-cell">
<div class="job-resume-custom-dialog__gap-job-info">
<div class="job-resume-custom-dialog__gap-company-icon">
<img v-if="jobInfo.companyLogoUrl" :src="jobInfo.companyLogoUrl" :alt="jobInfo.company" class="job-resume-custom-dialog__gap-company-logo" />
<svg v-else viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__gap-company-svg"><rect x="3" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="14" y="3" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="3" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/><rect x="14" y="14" width="7" height="7" rx="1" stroke="currentColor" stroke-width="1.2"/></svg>
</div>
<div class="job-resume-custom-dialog__gap-job-text">
<span class="job-resume-custom-dialog__gap-job-title">{{ jobInfo.title }}</span>
<span class="job-resume-custom-dialog__gap-job-sub">{{ jobInfo.location }} · {{ jobInfo.company }}&nbsp;&nbsp;独角兽</span>
</div>
</div>
</div>
<!-- 简历选择列 -->
<div class="job-resume-custom-dialog__gap-cell">
<div class="job-resume-custom-dialog__resume-selector">
<div class="job-resume-custom-dialog__resume-info">
<svg viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__resume-file-icon"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M14 2v6h6" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
<div class="job-resume-custom-dialog__resume-text">
<span class="job-resume-custom-dialog__resume-label">你的简历</span>
<span class="job-resume-custom-dialog__resume-name">{{ selectedResume.name }}</span>
</div>
</div>
<button class="job-resume-custom-dialog__resume-select-btn" @click="toggleResumeDropdown">选择 <svg viewBox="0 0 12 12" fill="none" class="job-resume-custom-dialog__dropdown-arrow"><path d="M3 5l3 3 3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<div v-if="showResumeDropdown" class="job-resume-custom-dialog__resume-dropdown">
<div v-for="r in resumeList" :key="r.id" class="job-resume-custom-dialog__resume-option" :class="{'job-resume-custom-dialog__resume-option--active': r.id === selectedResume.id}" @click="selectResume(r)">{{ r.name }}</div>
</div>
</div>
</div>
</div>
<!-- 第二行岗位名称 -->
<div class="job-resume-custom-dialog__gap-row">
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
<span>岗位名称</span>
</div>
<div class="job-resume-custom-dialog__gap-cell">
<span class="job-resume-custom-dialog__gap-value">{{ jobInfo.title }}</span>
</div>
<div class="job-resume-custom-dialog__gap-cell">
<span class="job-resume-custom-dialog__gap-value">{{ selectedResume.targetJob || '—' }}</span>
</div>
</div>
<!-- 第三行岗位关键词 -->
<div class="job-resume-custom-dialog__gap-row">
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--label">
<span>岗位关键词</span>
</div>
<div class="job-resume-custom-dialog__gap-cell job-resume-custom-dialog__gap-cell--keywords">
<div class="job-resume-custom-dialog__gap-keywords">
<span v-for="kw in jobInfo.keywords" :key="kw" class="job-resume-custom-dialog__gap-kw-tag">{{ kw }}</span>
</div>
</div>
</div>
</div>
</template>
<!-- 步骤三定制简历 -->
<template v-if="currentStep === 3">
<div class="job-resume-custom-dialog__custom">
<!-- 左侧选择要优化的部分 -->
<div class="job-resume-custom-dialog__custom-panel">
<h3 class="job-resume-custom-dialog__custom-panel-title">1.选择你要优化的部分</h3>
<div class="job-resume-custom-dialog__custom-options">
<label v-for="item in optimizeSections" :key="item.key" class="job-resume-custom-dialog__custom-checkbox">
<input type="checkbox" v-model="item.checked" class="job-resume-custom-dialog__custom-input" />
<span class="job-resume-custom-dialog__custom-checkmark"></span>
<span class="job-resume-custom-dialog__custom-label">{{ item.label }}</span>
<!-- 技能和工作经验的问号提示使用 el-tooltip -->
<el-tooltip
v-if="item.tooltip"
:content="item.tooltip"
placement="right"
:show-arrow="true"
:popper-options="{ strategy: 'fixed' }"
effect="dark"
>
<span class="job-resume-custom-dialog__custom-tooltip-trigger">
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__custom-tooltip-icon">
<circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/>
<path d="M6.5 6.5a1.5 1.5 0 112.12 1.37c-.42.18-.62.5-.62.88V9.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
<circle cx="8" cy="11.5" r="0.6" fill="currentColor"/>
</svg>
</span>
</el-tooltip>
</label>
</div>
</div>
<!-- 右侧选择要新增的技能关键词 -->
<div class="job-resume-custom-dialog__custom-panel">
<h3 class="job-resume-custom-dialog__custom-panel-title">2.选择你要新增的技能关键词</h3>
<div class="job-resume-custom-dialog__custom-options">
<label v-for="skill in newSkillOptions" :key="skill.name" class="job-resume-custom-dialog__custom-checkbox">
<input type="checkbox" v-model="skill.checked" class="job-resume-custom-dialog__custom-input" />
<span class="job-resume-custom-dialog__custom-checkmark"></span>
<span class="job-resume-custom-dialog__custom-label">{{ skill.name }}</span>
</label>
</div>
</div>
</div>
</template>
<!-- 步骤四预览 -->
<template v-if="currentStep === 4">
<div class="job-resume-custom-dialog__preview">
<!-- 左侧简历模板预览 -->
<div class="job-resume-custom-dialog__preview-left">
<JobResumeTemplate :resumeData="resumeTemplateData" ref="resumeTemplateRef" />
</div>
<!-- 右侧AI帮写 / 编辑 tab -->
<div class="job-resume-custom-dialog__preview-right">
<!-- Tab 切换 -->
<div class="job-resume-custom-dialog__preview-tabs">
<button
class="job-resume-custom-dialog__preview-tab"
:class="{ 'job-resume-custom-dialog__preview-tab--active': previewTab === 'ai' }"
@click="previewTab = 'ai'"
>AI帮写</button>
<button
class="job-resume-custom-dialog__preview-tab"
:class="{ 'job-resume-custom-dialog__preview-tab--active': previewTab === 'edit' }"
@click="previewTab = 'edit'"
>编辑</button>
</div>
<!-- AI帮写内容 -->
<div v-if="previewTab === 'ai'" class="job-resume-custom-dialog__preview-ai">
<!-- 匹配度提升提示 -->
<div class="job-resume-custom-dialog__ai-result">
<div class="job-resume-custom-dialog__ai-result-text">
<p class="job-resume-custom-dialog__ai-result-title">恭喜你的简历匹配值从<br/>{{ jobInfo.matchScore }}分提升到了10分</p>
<div class="job-resume-custom-dialog__ai-result-detail">
<p class="job-resume-custom-dialog__ai-result-subtitle">做了哪些优化</p>
<ul class="job-resume-custom-dialog__ai-result-list">
<li v-for="(item, i) in aiOptimizeResults" :key="i">·{{ item }}</li>
</ul>
</div>
</div>
<div class="job-resume-custom-dialog__ai-result-score">
<div class="job-resume-custom-dialog__match-ring job-resume-custom-dialog__match-ring--large">
<svg viewBox="0 0 60 60" class="job-resume-custom-dialog__ring-svg">
<circle cx="30" cy="30" r="24" stroke-width="4" stroke="#E8E8E8" fill="none" opacity="0.3"/>
<circle cx="30" cy="30" r="24" stroke-width="4" fill="none" stroke="#4FC2C9" stroke-linecap="round" :stroke-dasharray="2*Math.PI*24" :stroke-dashoffset="0" transform="rotate(-90 30 30)"/>
</svg>
<span class="job-resume-custom-dialog__match-score">10.0</span>
</div>
<span class="job-resume-custom-dialog__match-label job-resume-custom-dialog__match-label--high">非常匹配</span>
</div>
</div>
<!-- 快捷操作按钮 -->
<div class="job-resume-custom-dialog__ai-quick-actions">
<button
v-for="(action, i) in aiQuickActions"
:key="i"
class="job-resume-custom-dialog__ai-quick-btn"
@click="sendAiMessage(action)"
>{{ action }}</button>
</div>
<!-- AI聊天消息区域 -->
<div class="job-resume-custom-dialog__ai-messages" ref="aiMessagesRef">
<div
v-for="(msg, i) in aiMessages"
:key="i"
class="job-resume-custom-dialog__ai-msg"
:class="msg.role === 'ai' ? 'job-resume-custom-dialog__ai-msg--ai' : 'job-resume-custom-dialog__ai-msg--user'"
>
<div class="job-resume-custom-dialog__ai-msg-bubble">{{ msg.content }}</div>
</div>
</div>
<!-- AI输入框 -->
<div class="job-resume-custom-dialog__ai-input-area">
<input
v-model="aiInputText"
class="job-resume-custom-dialog__ai-input"
placeholder="你要怎么优化"
@keyup.enter="sendAiMessage(aiInputText)"
/>
<button class="job-resume-custom-dialog__ai-send-btn" @click="sendAiMessage(aiInputText)">
<svg viewBox="0 0 24 24" fill="none" class="job-resume-custom-dialog__ai-send-icon">
<path d="M5 12h14M12 5l7 7-7 7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
<!-- 编辑内容占位 -->
<div v-if="previewTab === 'edit'" class="job-resume-custom-dialog__preview-edit">
<div class="job-resume-custom-dialog__placeholder">编辑功能待开发</div>
</div>
</div>
</div>
</template>
</div>
<!-- 抽屉底部按钮 -->
<div v-if="currentStep < 4" class="job-resume-custom-dialog__drawer-footer">
<button class="job-resume-custom-dialog__primary-btn" @click="handleDrawerNext">立即定制简历</button>
</div>
<!-- 步骤四专属底部下载简历 + 立即去投递 -->
<div v-if="currentStep === 4" class="job-resume-custom-dialog__preview-footer">
<!-- 左侧下载简历按钮带下拉 -->
<div class="job-resume-custom-dialog__download-wrap">
<button class="job-resume-custom-dialog__download-btn" @click="toggleDownloadMenu">下载简历</button>
<div v-if="showDownloadMenu" class="job-resume-custom-dialog__download-menu">
<button class="job-resume-custom-dialog__download-option" @click="handleDownload('pdf')">下载PDF</button>
<button class="job-resume-custom-dialog__download-option" @click="handleDownload('word')">下载Word</button>
</div>
</div>
<!-- 右侧立即去投递按钮 -->
<button class="job-resume-custom-dialog__submit-btn" @click="handleSubmit">立即去投递</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
import { fetchProfile, fetchEducation, fetchWork, fetchInternship, fetchProject, fetchCompetition } from '@/api/profile'
// ==================== 类型定义 ====================
/** 简历选项 */
interface ResumeOption {
id: string
name: string
targetJob: string
}
/** 岗位信息 */
interface JobInfo {
title: string
company: string
companyLogoUrl: string
location: string
matchScore: number
missingSkills: string[]
keywords: string[]
sourceUrl: string
}
/** AI聊天消息 */
interface AiChatMsg {
role: 'ai' | 'user'
content: string
}
// ==================== Props & Emits ====================
const props = defineProps<{
/** 控制弹窗显隐 */
modelValue: boolean
/** 岗位信息 */
jobInfo: JobInfo
}>()
const emit = defineEmits<{
(e: 'update:modelValue', val: boolean): void
(e: 'skip'): void
(e: 'submit'): void
}>()
// ==================== 步骤控制 ====================
/** 当前步骤:1-确认入口(居中弹窗) 2-差距分析(右侧抽屉) 3-定制简历 4-预览 */
const currentStep = ref(1)
/** 缺少的技能列表 */
const missingSkills = computed(() => props.jobInfo.missingSkills || [])
/** 匹配度等级文案 */
const matchLevelText = computed(() => {
const score = props.jobInfo.matchScore
if (score >= 8) return '高匹配度'
if (score >= 5) return '中匹配度'
return '低匹配度'
})
/** 跳转到指定步骤 */
function goToStep(step: number) {
if (step === 3) initSkillOptions()
if (step === 4) loadResumeData()
currentStep.value = step
}
/** 抽屉模式下一步 */
function handleDrawerNext() {
if (currentStep.value < 4) {
if (currentStep.value === 2) initSkillOptions()
if (currentStep.value === 3) loadResumeData()
currentStep.value++
}
}
/** 关闭弹窗并重置步骤 */
function handleClose() {
currentStep.value = 1
showResumeDropdown.value = false
showDownloadMenu.value = false
emit('update:modelValue', false)
}
/** 不优化,直接投递 */
function handleSkip() {
handleClose()
emit('skip')
}
// ==================== 定制简历选项(步骤三) ====================
/** 优化部分选项 */
interface OptimizeSection {
key: string
label: string
checked: boolean
tooltip?: string
}
/** 技能关键词选项 */
interface SkillOption {
name: string
checked: boolean
}
/** 左侧:可优化的简历部分 */
const optimizeSections = ref<OptimizeSection[]>([
{ key: 'summary', label: '个人概述', checked: false },
{ key: 'skills', label: '技能', checked: false, tooltip: '我们将把您勾选的技能补充进简历的技能模块。这对于简历能否通过ATS关键词筛选至关重要。' },
{ key: 'experience', label: '工作经验', checked: false, tooltip: '我们将把您选择的技能融入工作经历中,并对关键词进行润色,最大程度提升简历与岗位的匹配度。' },
])
/** 右侧:可新增的技能关键词 */
const newSkillOptions = ref<SkillOption[]>([])
/** 根据缺失技能初始化技能选项 */
function initSkillOptions() {
newSkillOptions.value = missingSkills.value.map(skill => ({
name: skill,
checked: false,
}))
}
// ==================== 简历选择(步骤二) ====================
/** 简历列表(模拟数据,后续对接接口) */
const resumeList = ref<ResumeOption[]>([
{ id: '1', name: '李华_产品经理', targetJob: '电商产品经理' },
{ id: '2', name: '李华_数据分析', targetJob: '数据分析师' },
])
/** 当前选中的简历 */
const selectedResume = ref<ResumeOption>(resumeList.value[0])
/** 简历下拉是否展开 */
const showResumeDropdown = ref(false)
/** 切换简历下拉 */
function toggleResumeDropdown() {
showResumeDropdown.value = !showResumeDropdown.value
}
/** 选择简历 */
function selectResume(r: ResumeOption) {
selectedResume.value = r
showResumeDropdown.value = false
}
// ==================== 步骤四:预览相关 ====================
/** 简历模板组件引用 */
const resumeTemplateRef = ref()
/** 简历模板数据 */
const resumeTemplateData = ref<ResumeTemplateData>({
name: '',
email: '',
mobileNumber: '',
wechatNumber: '',
summary: '',
educations: [],
workExperiences: [],
internships: [],
projects: [],
competitions: [],
skills: [],
certificates: [],
})
/** 从接口加载个人资料并组装简历数据 */
async function loadResumeData() {
try {
// 并行请求所有个人资料数据
const [profileRes, eduRes, workRes, internRes, projRes, compRes] = await Promise.all([
fetchProfile(),
fetchEducation(),
fetchWork(),
fetchInternship(),
fetchProject(),
fetchCompetition(),
])
// 组装简历模板数据
const profile = profileRes.code === '0' ? profileRes.data : null
resumeTemplateData.value = {
name: profile?.name || '未填写姓名',
email: profile?.email || '',
mobileNumber: profile?.mobileNumber || '',
wechatNumber: profile?.wechatNumber || '',
summary: '', // 个人概述字段,后续由AI生成或用户编辑
educations: eduRes.code === '0' && eduRes.data ? eduRes.data.map(e => ({
school: e.school || '',
major: e.major || '',
degree: e.degree || 2,
startDate: e.startDate || '',
endDate: e.endDate || '',
description: e.description,
})) : [],
workExperiences: workRes.code === '0' && workRes.data ? workRes.data.map(w => ({
companyName: w.companyName || '',
position: w.position || '',
startDate: w.startDate || '',
endDate: w.endDate || '',
description: w.description,
})) : [],
internships: internRes.code === '0' && internRes.data ? internRes.data.map(i => ({
companyName: i.companyName || '',
position: i.position || '',
startDate: i.startDate || '',
endDate: i.endDate || '',
description: i.description,
})) : [],
projects: projRes.code === '0' && projRes.data ? projRes.data.map(p => ({
projectName: p.projectName || '',
companyName: p.companyName || '',
role: p.role || '',
startDate: p.startDate || '',
endDate: p.endDate || '',
description: p.description,
})) : [],
competitions: compRes.code === '0' && compRes.data ? compRes.data.map(c => ({
competitionName: c.competitionName || '',
award: c.award || '',
awardDate: c.awardDate || '',
description: c.description,
})) : [],
skills: profile?.skills || [],
certificates: profile?.certificates || [],
}
} catch (err) {
console.error('[JobResumeCustomDialog] 加载简历数据失败', err)
}
}
/** 当前预览右侧tabai-AI帮写 / edit-编辑 */
const previewTab = ref<'ai' | 'edit'>('ai')
/** AI优化结果列表(模拟数据) */
const aiOptimizeResults = ref<string[]>([
'增加了个人概述',
'优化了5段经历描述',
])
/** AI快捷操作按钮 */
const aiQuickActions = ref<string[]>([
'精简一下第一段工作经历',
'帮我强化一下简历里面的量化成果',
'删掉和这个岗位不相关的技能',
])
/** AI聊天消息列表 */
const aiMessages = ref<AiChatMsg[]>([])
/** AI输入框内容 */
const aiInputText = ref('')
/** AI消息区域DOM引用 */
const aiMessagesRef = ref<HTMLElement>()
/** 发送AI消息 */
function sendAiMessage(text: string) {
if (!text.trim()) return
aiMessages.value.push({ role: 'user', content: text.trim() })
aiInputText.value = ''
// TODO: 接入AI聊天接口,获取AI回复
nextTick(() => {
if (aiMessagesRef.value) {
aiMessagesRef.value.scrollTop = aiMessagesRef.value.scrollHeight
}
})
}
/** 下载菜单是否展开 */
const showDownloadMenu = ref(false)
/** 切换下载菜单 */
function toggleDownloadMenu() {
showDownloadMenu.value = !showDownloadMenu.value
}
/** 处理下载(PDF/Word */
function handleDownload(type: 'pdf' | 'word') {
showDownloadMenu.value = false
// TODO: 实现简历HTML转PDF/Word下载
console.log(`[下载简历] 格式: ${type}`)
}
/** 立即去投递 */
function handleSubmit() {
handleClose()
emit('submit')
}
</script>