全项目加载动效重做和简历经历区域的时间和编辑按钮同位置显示切换

This commit is contained in:
2026-06-30 17:09:09 +08:00
parent 0aebde7bd3
commit 8773c65b28
24 changed files with 610 additions and 489 deletions
+12 -5
View File
@@ -281,6 +281,8 @@
<MemberAccessDialog v-model="showMemberAccessDialog" @open-member="showMemberDialog = true" />
<!-- 会员购买弹窗 -->
<MemberDialog v-model="showMemberDialog" />
<!-- 全屏加载遮罩 -->
<FullscreenLoading :visible="fullLoading" :text="fullLoadingText" title="Nova 正在帮您努力处理~" />
</div>
</template>
@@ -296,6 +298,7 @@ import JobDislikeDialog from '@/components/JobDislikeDialog.vue'
import JobFeedbackDialog from '@/components/JobFeedbackDialog.vue'
import JobResumeCustomDialog from '@/components/JobResumeCustomDialog.vue'
import MemberAccessDialog from '@/components/MemberAccessDialog.vue'
import FullscreenLoading from '@/components/FullscreenLoading.vue'
import MemberDialog from '@/components/MemberDialog.vue'
import { fetchJobDetail, toggleJobFavorite, removeJobFavorite, fetchSkillGap } from '@/api/jobs'
import { fetchMemberStatus } from '@/api/member'
@@ -307,6 +310,12 @@ const router = useRouter()
const route = useRoute()
const store = useStore()
// ==================== 全屏加载状态 ====================
/** 全屏加载是否显示 */
const fullLoading = ref(false)
/** 全屏加载文案 */
const fullLoadingText = ref('')
/** 当前岗位 ID,从路由参数中获取 */
const jobId = route.params.id as string
@@ -595,10 +604,8 @@ async function handleGenerateResume() {
// 接口异常时不阻断,继续执行
}
const loadingInstance = ElLoading.service({
text: '正在分析岗位匹配度...',
background: 'rgba(0, 0, 0, 0.5)',
})
fullLoadingText.value = '正在分析岗位匹配度...'
fullLoading.value = true
try {
const res = await fetchSkillGap(jobId)
if (res.code === 0 && res.data) {
@@ -609,7 +616,7 @@ async function handleGenerateResume() {
ElMessage.error('分析失败,请稍后重试')
return
} finally {
loadingInstance.close()
fullLoading.value = false
}
showResumeCustomDialog.value = true
}
+29 -67
View File
@@ -2,29 +2,30 @@
<!-- 登录页面 左右分栏布局包含登录和简历上传流程 -->
<div class="login-view">
<!-- ==================== 登录阶段step 1 & 2==================== -->
<template v-if="step === 1 || step === 2">
<!-- 左侧视觉动画面板 -->
<section class="login-visual" ref="loginVisualRef" aria-label="OfferPai AI job platform">
<div class="ai-hub-stage" ref="stageRef">
<!-- 标题 -->
<div class="hero-title" aria-hidden="true">
AI 驱动的-
<span class="hero-title__second-line">一站式求职辅助平台</span>
</div>
<!-- Nova 机器人核心眼睛跟踪鼠标 -->
<div class="nova-core" ref="novaCoreRef" aria-hidden="true">
<img class="nova-core-base" :src="novaCoreSrc" alt="" draggable="false" />
<img class="nova-eye nova-eye-left" ref="novaEyeLeftRef" :src="novaEyeLeftSrc" alt="" draggable="false" />
<img class="nova-eye nova-eye-right" ref="novaEyeRightRef" :src="novaEyeRightSrc" alt="" draggable="false" />
</div>
<!-- ==================== 左侧视觉动画面板所有阶段共用 ==================== -->
<section class="login-visual" ref="loginVisualRef" aria-label="OfferPai AI job platform">
<div class="ai-hub-stage" ref="stageRef">
<!-- 标题 -->
<div class="hero-title" aria-hidden="true">
AI 驱动的-
<span class="hero-title__second-line">一站式求职辅助平台</span>
</div>
<!-- 底部 logo -->
<img class="login-jianshi-logo" :src="jianshiLogoSrc" alt="" aria-hidden="true" draggable="false" />
</section>
<!-- Nova 机器人核心眼睛跟踪鼠标 -->
<div class="nova-core" ref="novaCoreRef" aria-hidden="true">
<img class="nova-core-base" :src="novaCoreSrc" alt="" draggable="false" />
<img class="nova-eye nova-eye-left" ref="novaEyeLeftRef" :src="novaEyeLeftSrc" alt="" draggable="false" />
<img class="nova-eye nova-eye-right" ref="novaEyeRightRef" :src="novaEyeRightSrc" alt="" draggable="false" />
</div>
</div>
<!-- 底部 logo -->
<img class="login-jianshi-logo" :src="jianshiLogoSrc" alt="" aria-hidden="true" draggable="false" />
</section>
<!-- 右侧表单面板 -->
<div class="login-view__right">
<!-- ==================== 右侧面板 ==================== -->
<div class="login-view__right">
<!-- ===== 登录阶段step 1 & 2===== -->
<template v-if="step === 1 || step === 2">
<div class="login-view__logo-row login-view__logo-row--right">
<div class="login-view__logo-box login-view__logo-box--green">O</div>
<span class="login-view__logo-text login-view__logo-text--dark">Offer派</span>
@@ -81,38 +82,10 @@
<span class="login-view__agreement-link" @click="openAgreement('hf8375i8')">隐私政策</span>
</div>
</div>
</div>
</template>
</template>
<!-- ==================== 简历上传阶段step 3==================== -->
<template v-if="step === 3">
<!-- 左侧品牌面板 职位卡片风格 -->
<div class="login-view__left login-view__left--resume">
<!-- 顶部 Logo -->
<div class="login-view__resume-logo">
<div class="login-view__logo-box login-view__logo-box--light">O</div>
<span class="login-view__logo-text login-view__logo-text--white">Offer派</span>
</div>
<!-- 浮动职位卡片 -->
<div class="login-view__job-cards">
<div v-for="(job, idx) in jobCards" :key="idx" class="login-view__job-card" :style="job.style">
<span class="login-view__job-card-icon" :style="{ background: job.iconBg, color: job.iconColor }">{{ job.icon }}</span>
<div class="login-view__job-card-info">
<span class="login-view__job-card-title">{{ job.title }}</span>
<span class="login-view__job-card-company">{{ job.company }}</span>
</div>
<span class="login-view__job-card-salary">{{ job.salary }}</span>
</div>
</div>
<!-- 标语 -->
<div class="login-view__resume-slogan">
<h1>校招批量海投</h1>
<h1>年轻人有更多的面试机会</h1>
</div>
</div>
<!-- 右侧简历上传面板 -->
<div class="login-view__right">
<!-- ===== 简历上传阶段step 3===== -->
<template v-if="step === 3">
<div class="login-view__form-wrap login-view__form-wrap--resume">
<!-- 状态1上传区域 -->
@@ -151,8 +124,9 @@
</template>
</div>
</div>
</template>
</template>
</div>
<!-- 协议预览弹窗 -->
<AgreementPreviewDialog v-model="showAgreementDialog" :code="agreementCode" />
@@ -287,19 +261,7 @@ let parsingTimer: ReturnType<typeof setInterval> | null = null
/** 文件选择器引用 */
const fileInputRef = ref<HTMLInputElement | null>(null)
/** 左侧职位卡片硬编码数据 */
const jobCards = [
{ icon: '网', iconBg: '#FFE8E8', iconColor: '#E85635', title: '前端工程师', company: '网易', salary: '18-28K', style: 'top: 0.6rem; left: 0.4rem;' },
{ icon: '网', iconBg: '#FFE8E8', iconColor: '#E85635', title: '运营专员', company: '阿里巴巴', salary: '15-20K', style: 'top: 0.6rem; right: 0.8rem;' },
{ icon: '百', iconBg: '#E8F0FF', iconColor: '#4A7FE5', title: '算法工程师', company: '百度', salary: '30-45K', style: 'top: 1.5rem; left: 0.2rem;' },
{ icon: '美', iconBg: '#FFF3E8', iconColor: '#E8953F', title: '商业分析实习生', company: '美团', salary: '15K', style: 'top: 1.8rem; left: 2.8rem;' },
{ icon: '小', iconBg: '#E8FFE8', iconColor: '#4CAF50', title: '硬件产品', company: '小米', salary: '18-24K', style: 'top: 2.5rem; left: 0.3rem;' },
{ icon: '网', iconBg: '#FFE8E8', iconColor: '#E85635', title: '产品运营', company: '京东', salary: '18-25K', style: 'top: 2.8rem; right: 0.6rem;' },
{ icon: '字', iconBg: '#F0E8FF', iconColor: '#7C4DFF', title: '产品经理', company: '字节跳动', salary: '25-35K', style: 'top: 3.5rem; left: 1.2rem;' },
{ icon: '增', iconBg: '#E8FFF5', iconColor: '#12C7BE', title: '增长运营', company: '拼多多', salary: '20-28K', style: 'top: 3.5rem; right: 0.4rem;' },
{ icon: '滴', iconBg: '#FFF8E1', iconColor: '#FFC107', title: '后端开发', company: '滴滴', salary: '22-32K', style: 'top: 4.3rem; left: 0.5rem;' },
{ icon: '网', iconBg: '#FFE8E8', iconColor: '#E85635', title: '数据分析师', company: '腾讯', salary: '20-30K', style: 'top: 4.6rem; right: 0.8rem;' },
]
/** 解析中文字轮换列表 */
const parsingSteps = ['正在解析个人资料...', '正在解析教育背景...', '正在解析工作经历...', '正在解析技能特长...', '简历解析成功,即将跳转']
+16 -12
View File
@@ -37,6 +37,8 @@
</div>
</div>
</div>
<!-- 全屏加载遮罩 -->
<FullscreenLoading :visible="fullLoading" :text="fullLoadingText" title="Nova 正在帮您努力处理~" />
</template>
<script setup lang="ts">
@@ -49,13 +51,19 @@ import ProfilePageContent from '@/components/ProfilePageContent.vue'
import ResumeUploadDialog from '@/components/ResumeUploadDialog.vue'
import { saveProfile, fetchProfile, fetchEducation, saveEducation, fetchWork, saveWork, fetchInternship, saveInternship, fetchProject, saveProject, fetchCompetition, saveCompetition, syncProfileFromResume } from '@/api/profile'
import { uploadResume } from '@/utils/aiRequest'
import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
import 'element-plus/es/components/loading/style/css'
import { ElMessage, ElMessageBox } from 'element-plus'
import FullscreenLoading from '@/components/FullscreenLoading.vue'
import type { SaveEducationItem, SaveWorkItem, SaveProjectItem, SaveCompetitionItem } from '@/api/profile'
const router = useRouter()
const store = useStore()
// ==================== 全屏加载状态 ====================
/** 全屏加载是否显示 */
const fullLoading = ref(false)
/** 全屏加载文案 */
const fullLoadingText = ref('')
/** 页面挂载时加载公共分类数据和个人资料 */
onMounted(async () => {
if (!store.state.regions.length) {
@@ -208,29 +216,25 @@ const showWelcomeDialog = ref(false)
/** 欢迎弹窗确认上传回调 — 上传简历并同步个人资料,完成后刷新页面 */
async function handleWelcomeUpload(file: File) {
const loading = ElLoading.service({
lock: true,
text: '简历解析中,请耐心等待…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'profile-welcome-loading',
})
fullLoadingText.value = '简历解析中,请耐心等待…'
fullLoading.value = true
try {
const res = await uploadResume(file)
if (res.code === 0 && res.data?.resumeId) {
// 继续同步个人资料
loading.setText('正在同步个人资料…')
fullLoadingText.value = '正在同步个人资料…'
await syncProfileFromResume(String(res.data.resumeId))
loading.close()
fullLoading.value = false
ElMessage.success('简历上传并同步成功')
// 刷新页面数据
router.go(0)
} else {
loading.close()
fullLoading.value = false
ElMessage.error(res.msg || '上传失败')
}
} catch {
loading.close()
fullLoading.value = false
ElMessage.error('上传失败,请稍后重试')
}
}
+15 -11
View File
@@ -98,6 +98,8 @@
v-model="uploadDialogVisible"
@confirm="handleUploadConfirm"
/>
<!-- 全屏加载遮罩 -->
<FullscreenLoading :visible="fullLoading" :text="fullLoadingText" title="Nova 正在帮您努力处理~" />
</div>
</template>
@@ -113,13 +115,19 @@ import { uploadResume } from '@/utils/aiRequest'
import {
fetchResumeList, deleteResume, setDefaultResume, type ResumeListItem,
} from '@/api/resume'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
// 命令式调用的组件,按需引入插件不会自动加载其样式,需手动引入
import 'element-plus/es/components/loading/style/css'
import 'element-plus/es/components/message-box/style/css'
import FullscreenLoading from '@/components/FullscreenLoading.vue'
const router = useRouter()
// ==================== 全屏加载状态 ====================
/** 全屏加载是否显示 */
const fullLoading = ref(false)
/** 全屏加载文案 */
const fullLoadingText = ref('')
// ==================== 头像颜色池 ====================
/** 头像背景色列表,按索引循环取色 */
@@ -297,12 +305,8 @@ function handleUpload() {
/** 上传弹窗确认回调 — 接收 File 对象,调用接口上传 */
async function handleUploadConfirm(file: File) {
// 全屏加载提示,AI 接口响应较慢
const loading = ElLoading.service({
lock: true,
text: '简历解析中,请耐心等待…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
fullLoadingText.value = '简历解析中,请耐心等待…'
fullLoading.value = true
try {
const res = await uploadResume(file)
@@ -311,14 +315,14 @@ async function handleUploadConfirm(file: File) {
loadResumeList()
// 等待让后端异步处理数据,再关闭加载动画并跳转详情页
await new Promise(resolve => setTimeout(resolve, 2000))
loading.close()
fullLoading.value = false
goDetail(res.data.resumeId)
} else {
loading.close()
fullLoading.value = false
ElMessage.error(res.msg || '上传失败')
}
} catch {
loading.close()
fullLoading.value = false
ElMessage.error('上传失败,请稍后重试')
}
}
+42 -42
View File
@@ -160,28 +160,30 @@
<rect x="2" y="3" width="12" height="10" rx="1.5" stroke="currentColor" stroke-width="1.2"/>
<path d="M2 5.5l6 4 6-4" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
</svg>
{{ resumeMain.email }}
<span>{{ resumeMain.email }}</span>
</span>
<!-- 手机号 -->
<span v-if="resumeMain.mobileNumber" class="resume-detail__contact-item">
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<path d="M4 2h2.5l1.5 3-1.5 1.5a8 8 0 003 3L11 8l3 1.5V12a2 2 0 01-2 2C6.5 14 2 9.5 2 4a2 2 0 012-2z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
</svg>
{{ resumeMain.mobileNumber }}
<span>{{ resumeMain.mobileNumber }}</span>
</span>
<!-- 微信号 -->
<span v-if="resumeMain.wechatNumber" class="resume-detail__contact-item">
<el-icon class="fs14 color-9"><ChatLineRound /></el-icon>
{{ resumeMain.wechatNumber }}
<span>{{ resumeMain.wechatNumber }}</span>
</span>
<!-- 作品集链接 -->
<span v-if="resumeMain.portfolioUrl" class="resume-detail__contact-item">
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<path d="M3 8a5 5 0 015-5h1a5 5 0 010 10H8a5 5 0 01-5-5z" stroke="currentColor" stroke-width="1.2"/>
<path d="M7 6l2 2-2 2" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
<span v-if="resumeMain.portfolioUrl" class="resume-detail__contact-item dib">
<svg xmlns="http://www.w3.org/2000/svg" class=" mr6" width="0.15rem" height="0.15rem" viewBox="0 0 16 16">
<path d="M0 0h16v16H0z" fill="none" />
<path fill="none" stroke="currentColor" d="M14.5 8A6.5 6.5 0 0 1 8 14.5M14.5 8A6.5 6.5 0 0 0 8 1.5M14.5 8c0 1.657-2.91 3-6.5 3S1.5 9.657 1.5 8m13 0c0-1.657-2.91-3-6.5-3S1.5 6.343 1.5 8M8 14.5A6.5 6.5 0 0 1 1.5 8M8 14.5c1.657 0 3-2.91 3-6.5S9.657 1.5 8 1.5m0 13c-1.657 0-3-2.91-3-6.5s1.343-6.5 3-6.5M1.5 8A6.5 6.5 0 0 1 8 1.5" />
</svg>
<a :href="resumeMain.portfolioUrl" target="_blank" rel="noopener noreferrer" class="resume-detail__portfolio-link">{{ resumeMain.portfolioUrl }}</a>
<a :href="resumeMain.portfolioUrl?.startsWith('http') ? resumeMain.portfolioUrl : `http://${resumeMain.portfolioUrl}`" target="_blank" rel="noopener noreferrer" class="resume-detail__portfolio-link">{{ resumeMain.portfolioUrl }}</a>
</span>
</div>
</template>
@@ -253,13 +255,14 @@
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ edu.school }}</span>
<span v-if="edu.major || edu.degree" class="resume-detail__exp-role">{{ edu.major }}{{ edu.degree ? '·' + edu.degree : '' }}</span>
<span v-if="edu.startDate || edu.endDate" class="resume-detail__exp-date">{{ edu.startDate }}<span v-if="edu.startDate && edu.endDate">-</span>{{ edu.endDate }}</span>
<span v-if="hasDiagnosis && getIssueByRecord('education', edu.id!) && getIssueByRecord('education', edu.id!)!.status === 0" class="resume-detail__fix-tag">
<svg viewBox="0 0 16 16" class="resume-detail__fix-tag-icon"><circle cx="8" cy="8" r="7" fill="#FF8D0B"/><text x="8" y="12" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">!</text></svg>
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('education', edu.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 时间默认显示hover时隐藏 -->
<span v-if="edu.startDate || edu.endDate" class="resume-detail__exp-date resume-detail__exp-date--toggle">{{ edu.startDate }}<span v-if="edu.startDate && edu.endDate">-</span>{{ edu.endDate }}</span>
<!-- 编辑按钮 hover时显示 -->
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('education', edu)" aria-label="编辑">
<el-icon><EditPen /></el-icon>
@@ -302,13 +305,14 @@
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ exp.companyName }}</span>
<span v-if="exp.position" class="resume-detail__exp-role">{{ exp.position }}</span>
<span v-if="exp.startDate || exp.endDate" class="resume-detail__exp-date">{{ exp.startDate }}<span v-if="exp.startDate && exp.endDate">-</span>{{ exp.endDate }}</span>
<span v-if="hasDiagnosis && getIssueByRecord('work', exp.id!) && getIssueByRecord('work', exp.id!)!.status === 0" class="resume-detail__fix-tag">
<svg viewBox="0 0 16 16" class="resume-detail__fix-tag-icon"><circle cx="8" cy="8" r="7" fill="#FF8D0B"/><text x="8" y="12" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">!</text></svg>
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('work', exp.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 时间默认显示hover时隐藏 -->
<span v-if="exp.startDate || exp.endDate" class="resume-detail__exp-date resume-detail__exp-date--toggle">{{ exp.startDate }}<span v-if="exp.startDate && exp.endDate">-</span>{{ exp.endDate }}</span>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('work', exp)" aria-label="编辑">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
@@ -339,13 +343,14 @@
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ intern.companyName }}</span>
<span v-if="intern.position" class="resume-detail__exp-role">{{ intern.position }}</span>
<span v-if="intern.startDate || intern.endDate" class="resume-detail__exp-date">{{ intern.startDate }}<span v-if="intern.startDate && intern.endDate">-</span>{{ intern.endDate }}</span>
<span v-if="hasDiagnosis && getIssueByRecord('internship', intern.id!) && getIssueByRecord('internship', intern.id!)!.status === 0" class="resume-detail__fix-tag">
<svg viewBox="0 0 16 16" class="resume-detail__fix-tag-icon"><circle cx="8" cy="8" r="7" fill="#FF8D0B"/><text x="8" y="12" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">!</text></svg>
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('internship', intern.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 时间默认显示hover时隐藏 -->
<span v-if="intern.startDate || intern.endDate" class="resume-detail__exp-date resume-detail__exp-date--toggle">{{ intern.startDate }}<span v-if="intern.startDate && intern.endDate">-</span>{{ intern.endDate }}</span>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('internship', intern)" aria-label="编辑">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
@@ -376,13 +381,14 @@
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ proj.projectName }}</span>
<span v-if="proj.role" class="resume-detail__exp-role">{{ proj.role }}</span>
<span v-if="proj.startDate || proj.endDate" class="resume-detail__exp-date">{{ proj.startDate }}<span v-if="proj.startDate && proj.endDate">-</span>{{ proj.endDate }}</span>
<span v-if="hasDiagnosis && getIssueByRecord('project', proj.id!) && getIssueByRecord('project', proj.id!)!.status === 0" class="resume-detail__fix-tag">
<svg viewBox="0 0 16 16" class="resume-detail__fix-tag-icon"><circle cx="8" cy="8" r="7" fill="#FF8D0B"/><text x="8" y="12" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">!</text></svg>
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('project', proj.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 时间默认显示hover时隐藏 -->
<span v-if="proj.startDate || proj.endDate" class="resume-detail__exp-date resume-detail__exp-date--toggle">{{ proj.startDate }}<span v-if="proj.startDate && proj.endDate">-</span>{{ proj.endDate }}</span>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('project', proj)" aria-label="编辑">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
@@ -413,13 +419,14 @@
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ comp.competitionName }}</span>
<span v-if="comp.award" class="resume-detail__exp-role">{{ comp.award }}</span>
<span v-if="comp.awardDate" class="resume-detail__exp-date">{{ comp.awardDate }}</span>
<span v-if="hasDiagnosis && getIssueByRecord('competition', comp.id!) && getIssueByRecord('competition', comp.id!)!.status === 0" class="resume-detail__fix-tag">
<svg viewBox="0 0 16 16" class="resume-detail__fix-tag-icon"><circle cx="8" cy="8" r="7" fill="#FF8D0B"/><text x="8" y="12" text-anchor="middle" font-size="11" fill="#fff" font-weight="bold">!</text></svg>
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('competition', comp.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 时间默认显示hover时隐藏 -->
<span v-if="comp.awardDate" class="resume-detail__exp-date resume-detail__exp-date--toggle">{{ comp.awardDate }}</span>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('competition', comp)" aria-label="编辑">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
@@ -500,6 +507,8 @@
<MemberAccessDialog v-model="showMemberAccessDialog" @open-member="showMemberDialog = true" />
<!-- 会员购买弹窗 -->
<MemberDialog v-model="showMemberDialog" />
<!-- 全屏加载遮罩 -->
<FullscreenLoading :visible="fullLoading" :text="fullLoadingText" title="Nova 正在帮您努力处理~" />
</div>
</template>
@@ -557,11 +566,18 @@ import {
type DescriptionParagraph,
} from '@/api/resume'
import {ArrowLeft, EditPen, Plus, DeleteFilled, ChatLineRound} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import FullscreenLoading from '@/components/FullscreenLoading.vue'
// ==================== Store 实例 ====================
const store = useStore()
// ==================== 全屏加载状态 ====================
/** 全屏加载是否显示 */
const fullLoading = ref(false)
/** 全屏加载文案 */
const fullLoadingText = ref('')
// ==================== 诊断数据相关 ====================
/** 会员权限拦截弹窗 */
@@ -936,12 +952,8 @@ async function handleDiagnose() {
}
// 全屏加载提示,AI 接口响应较慢
const loading = ElLoading.service({
lock: true,
text: '简历诊断中,请耐心等待…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
fullLoadingText.value = '简历诊断中,请耐心等待…'
fullLoading.value = true
try {
const res = await triggerResumeDiagnosis(resumeId)
if (res.code === 0) {
@@ -954,7 +966,7 @@ async function handleDiagnose() {
} catch {
ElMessage.error('诊断请求失败,请稍后重试')
} finally {
loading.close()
fullLoading.value = false
}
}
@@ -1098,12 +1110,8 @@ function joinDescriptionText(desc: DescriptionParagraph[]): string {
async function handleFixAllSave(items: FixAllSubmitItem[]) {
if (!items.length) return
const loading = ElLoading.service({
lock: true,
text: '正在保存修改…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
fullLoadingText.value = '正在保存修改…'
fullLoading.value = true
try {
for (const item of items) {
@@ -1123,7 +1131,7 @@ async function handleFixAllSave(items: FixAllSubmitItem[]) {
} catch {
ElMessage.error('保存失败,请稍后重试')
} finally {
loading.close()
fullLoading.value = false
}
}
@@ -1131,12 +1139,8 @@ async function handleFixAllSave(items: FixAllSubmitItem[]) {
async function handleFixAllUseAi(items: FixAllSubmitItem[]) {
if (!items.length) return
const loading = ElLoading.service({
lock: true,
text: '正在应用AI润色结果…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
fullLoadingText.value = '正在应用AI润色结果…'
fullLoading.value = true
try {
for (const item of items) {
@@ -1152,7 +1156,7 @@ async function handleFixAllUseAi(items: FixAllSubmitItem[]) {
} catch {
ElMessage.error('操作失败,请稍后重试')
} finally {
loading.close()
fullLoading.value = false
}
}
@@ -1215,12 +1219,8 @@ async function updateModuleContent(item: FixAllSubmitItem) {
/** 修复抽屉提交新版本 — 更新本地数据、调用保存接口、刷新页面 */
async function handleFixSubmit(content: string[]) {
const loading = ElLoading.service({
lock: true,
text: '正在保存修改…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
fullLoadingText.value = '正在保存修改…'
fullLoading.value = true
try {
const moduleType = fixModuleType.value
@@ -1262,7 +1262,7 @@ async function handleFixSubmit(content: string[]) {
} catch {
ElMessage.error('保存失败,请稍后重试')
} finally {
loading.close()
fullLoading.value = false
}
}