生成定制简历部分默认优化选项全选

This commit is contained in:
2026-07-06 18:58:16 +08:00
parent 6024ea27e9
commit 78bf7fbebc
8 changed files with 655 additions and 409 deletions
+49 -7
View File
@@ -22,18 +22,16 @@
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ref, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { fetchFavoriteCount, fetchApplyCount } from '@/api/jobs'
// ==================== Props & Emits ====================
/** activeTab: 当前激活的 Tab key,由父组件通过 v-model 传入 */
const props = defineProps<{
activeTab: string
/** 收藏总数,由父组件传入 */
favoriteCount?: number
/** 投递总数,由父组件传入 */
applyCount?: number
}>()
/** 双向绑定事件,在 Jobs 页面内切换 Tab 时触发 */
@@ -45,14 +43,58 @@ const emit = defineEmits<{
const router = useRouter()
const route = useRoute()
const store = useStore()
/** 登录状态 */
const isAuthenticated = computed(() => store.state.isAuthenticated)
// ==================== 收藏 & 投递统计 ====================
/** 收藏总数 */
const favoriteTotal = ref(0)
/** 投递总数 */
const applyTotal = ref(0)
/** 加载收藏统计 */
async function loadFavoriteCount() {
try {
const res = await fetchFavoriteCount()
if (res.code === '0' && res.data) {
favoriteTotal.value = res.data.totalCount
}
} catch (e) {
console.error('加载收藏统计失败', e)
}
}
/** 加载投递统计 */
async function loadApplyCount() {
try {
const res = await fetchApplyCount()
if (res.code === '0' && res.data) {
applyTotal.value = res.data.totalCount
}
} catch (e) {
console.error('加载投递统计失败', e)
}
}
/** 组件挂载时请求收藏和投递统计 */
onMounted(() => {
if (isAuthenticated.value) {
loadFavoriteCount()
loadApplyCount()
}
})
// ==================== 常量数据 ====================
/** Tab 选项列表,count 用于显示圆形徽章计数 */
const tabs = computed(() => [
{ key: 'recommend', label: '推荐', count: undefined },
{ key: 'collected', label: '收藏', count: props.favoriteCount ?? 0 },
{ key: 'applied', label: '已投递', count: props.applyCount ?? 0 },
{ key: 'collected', label: '收藏', count: favoriteTotal.value },
{ key: 'applied', label: '已投递', count: applyTotal.value },
])
// ==================== 事件处理 ====================
+169 -124
View File
@@ -12,148 +12,168 @@
</button>
<h2 class="job-resume-custom-dialog__drawer-title">生成你的岗位专属简历</h2>
</div>
<!-- 返回按钮步骤显示 -->
<button v-if="currentStep === 4" class="job-resume-custom-dialog__back-btn mt10" @click="goToStep(3)">返回</button>
<!-- 下载简历按钮步骤显示位于步骤指示器上方 -->
<div v-if="currentStep === 4" class="job-resume-custom-dialog__top-download mt10">
<button class="job-resume-custom-dialog__top-download-btn" @click="toggleDownloadMenu">
<svg viewBox="0 0 16 16" fill="none" class="job-resume-custom-dialog__top-download-icon">
<path d="M8 2v8M8 10l-3-3M8 10l3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M3 12h10" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
</svg>
下载简历
</button>
<!-- 下载下拉菜单 -->
<div v-if="showDownloadMenu" class="job-resume-custom-dialog__top-download-menu">
<button class="job-resume-custom-dialog__top-download-option" @click="handleDownload('pdf')">下载PDF</button>
<button class="job-resume-custom-dialog__top-download-option" @click="handleDownload('word')">下载Word</button>
</div>
</div>
<!-- 步骤指示器 -->
<div class="job-resume-custom-dialog__steps pt10">
<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-line"></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-line"></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">你的简历与该岗位的匹配度{{ isLowMatch ? '较低' : '较高' }}</h3>
<div v-if="isLowMatch" 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>
<h3 class="job-resume-custom-dialog__gap-title">你的简历与该岗位的<span class="job-resume-custom-dialog__gap-title-highlight">匹配度{{ isLowMatch ? '较低' : '较高' }}</span></h3>
<p class="job-resume-custom-dialog__gap-subtitle">匹配度低于 6.0 分的简历在筛选环节可能会被优先淘汰我们会帮你快速优化提升</p>
</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 class="job-resume-custom-dialog__score-circle">
<svg viewBox="0 0 72 72" class="job-resume-custom-dialog__score-svg">
<circle cx="36" cy="36" r="32" stroke-width="5" stroke="rgba(82,202,209,0.2)" fill="none"/>
<circle cx="36" cy="36" r="32" stroke-width="5" fill="none" stroke="#52CAD1" stroke-linecap="round" :stroke-dasharray="2*Math.PI*32" :stroke-dashoffset="2*Math.PI*32*(1-jobInfo.matchScore/10)" transform="rotate(-90 36 36)"/>
</svg>
<span class="job-resume-custom-dialog__score-num">{{ jobInfo.matchScore }}</span>
</div>
<span class="job-resume-custom-dialog__match-label">{{ matchLevelText }}</span>
<span class="job-resume-custom-dialog__score-tag">{{ 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 class="job-resume-custom-dialog__section">
<h4 class="job-resume-custom-dialog__section-title">简历选择</h4>
<div class="job-resume-custom-dialog__resume-select-box" @click="toggleResumeDropdown">
<span class="job-resume-custom-dialog__resume-select-name">{{ selectedResume.name }}</span>
<svg viewBox="0 0 14 14" fill="none" class="job-resume-custom-dialog__resume-select-arrow"><path d="M4 6l3 3 3-3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></svg>
<!-- 简历下拉 -->
<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.stop="selectResume(r)">{{ r.name }}</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__section">
<h4 class="job-resume-custom-dialog__section-title">岗位概览</h4>
<div class="job-resume-custom-dialog__overview-card">
<div class="job-resume-custom-dialog__overview-title">{{ jobInfo.company }} · {{ jobInfo.title }}</div>
<div class="job-resume-custom-dialog__overview-sub">{{ jobInfo.location }} · {{ jobInfo.company }}</div>
<div class="job-resume-custom-dialog__overview-tags">
<span v-for="kw in jobInfo.keywords" :key="kw" class="job-resume-custom-dialog__overview-tag">{{ kw }}</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 class="job-resume-custom-dialog__section">
<h4 class="job-resume-custom-dialog__section-title">差距分析</h4>
<div class="job-resume-custom-dialog__gap-list">
<div v-for="(gap, idx) in gapItems" :key="idx" class="job-resume-custom-dialog__gap-item">
<div class="job-resume-custom-dialog__gap-item-num">
<span>{{ idx + 1 }}</span>
</div>
<div class="job-resume-custom-dialog__gap-item-content">
<div class="job-resume-custom-dialog__gap-item-title">{{ gap.title }}</div>
<div class="job-resume-custom-dialog__gap-item-desc">{{ gap.description }}</div>
</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>
<div class="job-resume-custom-dialog__custom-v2">
<!-- 第一部分选择要优化的部分 -->
<div class="job-resume-custom-dialog__custom-section">
<div class="job-resume-custom-dialog__custom-section-header">
<h3 class="job-resume-custom-dialog__custom-section-title">选择你要优化的部分</h3>
<p class="job-resume-custom-dialog__custom-section-desc">选择后系统会基于岗位要求自动重写对应模块提升简历通过初筛的概率</p>
</div>
<div class="job-resume-custom-dialog__custom-cards">
<label
v-for="item in optimizeSections"
:key="item.key"
class="job-resume-custom-dialog__custom-card"
:class="{ 'job-resume-custom-dialog__custom-card--checked': item.checked }"
>
<input type="checkbox" v-model="item.checked" class="job-resume-custom-dialog__custom-card-input" />
<!-- 自定义复选框圆形 -->
<span class="job-resume-custom-dialog__custom-card-check">
<svg v-if="item.checked" viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
<circle cx="10" cy="10" r="10" fill="#52CAD1"/>
<path d="M6 10.5l2.5 2.5 5.5-5.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg v-else viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
<circle cx="10" cy="10" r="9.33" stroke="#6A7282" stroke-width="1.33"/>
</svg>
</span>
<!-- 文字内容 -->
<span class="job-resume-custom-dialog__custom-card-text">
<span class="job-resume-custom-dialog__custom-card-label">{{ item.label }}</span>
<span v-if="item.tooltip" class="job-resume-custom-dialog__custom-card-tip">{{ item.tooltip }}</span>
</span>
</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>
<!-- 第二部分选择要新增的技能关键词 -->
<div class="job-resume-custom-dialog__custom-section">
<div class="job-resume-custom-dialog__custom-section-header">
<h3 class="job-resume-custom-dialog__custom-section-title">选择你要新增的技能关键词</h3>
<p class="job-resume-custom-dialog__custom-section-desc">关键词会被自然融入经历描述中避免生硬堆砌</p>
</div>
<div class="job-resume-custom-dialog__custom-cards">
<label
v-for="skill in newSkillOptions"
:key="skill.name"
class="job-resume-custom-dialog__custom-card"
:class="{ 'job-resume-custom-dialog__custom-card--checked': skill.checked }"
>
<input type="checkbox" v-model="skill.checked" class="job-resume-custom-dialog__custom-card-input" />
<!-- 自定义复选框圆形 -->
<span class="job-resume-custom-dialog__custom-card-check">
<svg v-if="skill.checked" viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
<circle cx="10" cy="10" r="10" fill="#52CAD1"/>
<path d="M6 10.5l2.5 2.5 5.5-5.5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg v-else viewBox="0 0 20 20" fill="none" class="job-resume-custom-dialog__custom-card-check-svg">
<circle cx="10" cy="10" r="9.33" stroke="#6A7282" stroke-width="1.33"/>
</svg>
</span>
<!-- 文字内容 -->
<span class="job-resume-custom-dialog__custom-card-text">
<span class="job-resume-custom-dialog__custom-card-label">{{ skill.name }}</span>
</span>
</label>
</div>
</div>
</div>
</template>
<!-- 步骤预览 -->
<!-- 步骤简历预览 -->
<template v-if="currentStep === 4">
<div class="job-resume-custom-dialog__preview">
<!-- 左侧简历模板预览支持差异对比模式 -->
@@ -280,18 +300,22 @@
</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 mt10 pt30">
<!-- 左侧下载简历按钮带下拉 -->
<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>
<!-- 步骤二定制简历上一步 + 下一步 -->
<template v-if="currentStep === 3">
<div class="job-resume-custom-dialog__footer-btns">
<button class="job-resume-custom-dialog__prev-btn" @click="goToStep(2)">上一步</button>
<button class="job-resume-custom-dialog__next-btn" @click="handleDrawerNext">下一步</button>
</div>
</div>
</template>
<!-- 步骤一差距分析立即定制简历 -->
<template v-else>
<button class="job-resume-custom-dialog__primary-btn" @click="handleDrawerNext">立即定制简历</button>
</template>
</div>
<!-- 步骤三专属底部下载简历 + 立即去投递 -->
<div v-if="currentStep === 4" class="job-resume-custom-dialog__preview-footer mt10 pt30">
<!-- 左侧上一步按钮 -->
<button class="job-resume-custom-dialog__footer-prev-btn" @click="goToStep(3)">上一步</button>
<!-- 右侧立即去投递按钮 -->
<button class="job-resume-custom-dialog__submit-btn" @click="handleSubmit">立即去投递</button>
</div>
@@ -318,7 +342,7 @@ import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
import JobResumeCustomEditPanel from '@/components/JobResumeCustomEditPanel.vue'
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
import { exportResumePdf, exportResumeWord } from '@/utils/resumeExport'
import { fetchResumeList } from '@/api/resume'
import { fetchResumeList, setDefaultResume } from '@/api/resume'
import type { ResumeListItem } from '@/api/resume'
import { fetchCustomizeResume, generateCustomizeResume, aiEditResume, rollbackCustomizeResume } from '@/api/jobs'
import type { CustomizeResumeData, AiEditChatMessage } from '@/api/jobs'
@@ -334,6 +358,13 @@ interface ResumeOption {
targetJob: string
}
/** 差距分析项 */
interface GapItem {
keyword: string
title: string
description: string
}
/** 岗位信息 */
interface JobInfo {
title: string
@@ -344,6 +375,8 @@ interface JobInfo {
missingSkills: string[]
keywords: string[]
sourceUrl: string
/** 差距分析列表(来自 skill-gap 接口) */
gaps: GapItem[]
/** 默认简历信息(来自 skill-gap 接口) */
defaultResume: { resumeId: string; resumeName: string; targetPosition: string } | null
}
@@ -400,6 +433,9 @@ const matchLevelText = computed(() => {
/** 是否为低匹配度(低于6分) */
const isLowMatch = computed(() => props.jobInfo.matchScore < 6)
/** 差距分析列表(来自 jobInfo.gaps */
const gapItems = computed(() => props.jobInfo.gaps || [])
/** 跳转到指定步骤 */
function goToStep(step: number) {
// 从步骤4返回步骤3时,清理步骤4的状态数据(避免下次进入被旧数据影响)
@@ -440,6 +476,11 @@ async function handleDrawerNext() {
initSkillOptions()
currentStep.value++
} else if (currentStep.value === 3) {
// 校验:至少勾选一个优化部分
if (selectedSectionKeys.value.length === 0) {
ElMessage.warning('请选择你要优化的部分')
return
}
// 步骤3 → 步骤4:先调用定制简历接口
await fetchAndLoadCustomResume()
} else {
@@ -576,7 +617,7 @@ function handleClose() {
// ==================== 定制简历选项(步骤 ====================
// ==================== 定制简历选项(步骤 ====================
/** 优化部分选项 */
interface OptimizeSection {
@@ -594,19 +635,19 @@ interface SkillOption {
/** 左侧:可优化的简历部分 */
const optimizeSections = ref<OptimizeSection[]>([
{ key: 'summary', label: '个人概述', checked: false },
{ key: 'skills', label: '技能', checked: false, tooltip: '我们将把您勾选的技能补充进简历的技能模块。这对于简历能否通过ATS关键词筛选至关重要。' },
{ key: 'experience', label: '工作经验', checked: false, tooltip: '我们将把您选择的技能融入工作经历中,并对关键词进行润色,最大程度提升简历与岗位的匹配度。' },
{ key: 'summary', label: '个人概述', checked: true },
{ key: 'skills', label: '技能', checked: true, tooltip: '我们将把您勾选的技能补充进简历的技能模块。这对于简历能否通过ATS关键词筛选至关重要。' },
{ key: 'experience', label: '工作经验', checked: true, tooltip: '我们将把您选择的技能融入工作经历中,并对关键词进行润色,最大程度提升简历与岗位的匹配度。' },
])
/** 右侧:可新增的技能关键词 */
const newSkillOptions = ref<SkillOption[]>([])
/** 根据缺失技能初始化技能选项 */
/** 根据缺失技能初始化技能选项(默认全选) */
function initSkillOptions() {
newSkillOptions.value = missingSkills.value.map(skill => ({
name: skill,
checked: false,
checked: true,
}))
}
@@ -620,7 +661,7 @@ const selectedNewSkills = computed(() =>
newSkillOptions.value.filter(s => s.checked).map(s => s.name)
)
// ==================== 简历选择(步骤 ====================
// ==================== 简历选择(步骤 ====================
/** 简历列表(从接口获取) */
const resumeList = ref<ResumeOption[]>([])
@@ -680,13 +721,17 @@ function toggleResumeDropdown() {
showResumeDropdown.value = !showResumeDropdown.value
}
/** 选择简历 */
/** 选择简历并设为默认 */
function selectResume(r: ResumeOption) {
selectedResume.value = r
showResumeDropdown.value = false
// 调用设为默认简历接口
setDefaultResume(r.id).catch(e => {
console.error('[JobResumeCustomDialog] 设为默认简历失败', e)
})
}
// ==================== 步骤:预览相关 ====================
// ==================== 步骤:预览相关 ====================
/** 简历模板组件引用 */
const resumeTemplateRef = ref()
@@ -715,7 +760,7 @@ const customResumeRawData = ref<CustomizeResumeData>({
/** 当前预览右侧tabai-AI帮写 / edit-编辑 */
const previewTab = ref<'ai' | 'edit'>('ai')
/** AI优化结果列表(根据步骤勾选的优化项动态生成) */
/** AI优化结果列表(根据步骤勾选的优化项动态生成) */
const aiOptimizeResults = computed<string[]>(() => {
const results: string[] = []