简历优化和岗位简历
This commit is contained in:
+395
-172
@@ -11,6 +11,24 @@
|
||||
@save="handleSaveEdit"
|
||||
/>
|
||||
|
||||
<!-- 简历分析报告抽屉 -->
|
||||
<ResumeAnalysisReportDrawer
|
||||
v-model="showReportDrawer"
|
||||
:report="diagnosisReport"
|
||||
:issues="diagnosisIssues"
|
||||
@optimize="handleClose_report"
|
||||
/>
|
||||
|
||||
<!-- 简历评估问题修复抽屉 -->
|
||||
<ResumeIssueFixDrawer
|
||||
v-model="showFixDrawer"
|
||||
:module-type="fixModuleType"
|
||||
:record-id="fixRecordId"
|
||||
:original-content="fixOriginalContent"
|
||||
:issue="fixIssue"
|
||||
@submit="handleFixSubmit"
|
||||
/>
|
||||
|
||||
<!-- 顶部标题 -->
|
||||
<h2 class="resume-detail__page-title">我的简历</h2>
|
||||
|
||||
@@ -50,26 +68,37 @@
|
||||
<!-- 简历评分区域 -->
|
||||
<div class="resume-detail__score-bar">
|
||||
<div class="resume-detail__score-left">
|
||||
<!-- 评级 -->
|
||||
<span class="resume-detail__score-avatar">
|
||||
{{ getAvatarLetter() }}
|
||||
{{ diagnosisReport.grade }}
|
||||
</span>
|
||||
<span class="resume-detail__score-badge">良好</span>
|
||||
<button class="resume-detail__score-link" @click="handleViewReport">查看评估报告 ></button>
|
||||
<!-- 有诊断报告时显示评级和查看链接 -->
|
||||
<template v-if="hasDiagnosis">
|
||||
<span class="resume-detail__score-badge">{{ gradeLabel }}</span>
|
||||
<button class="resume-detail__score-link" @click="handleViewReport">查看评估报告 ></button>
|
||||
</template>
|
||||
</div>
|
||||
<div class="resume-detail__score-right">
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">0</span>
|
||||
<span class="resume-detail__score-label">紧急修复项</span>
|
||||
</div>
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">0</span>
|
||||
<span class="resume-detail__score-label">严重问题</span>
|
||||
</div>
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">0</span>
|
||||
<span class="resume-detail__score-label">可选修复项</span>
|
||||
</div>
|
||||
<button class="resume-detail__diagnose-btn" @click="handleDiagnose">重新诊断</button>
|
||||
<!-- 有诊断报告时显示三项计数和重新诊断按钮 -->
|
||||
<template v-if="hasDiagnosis">
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">{{ diagnosisReport.urgentTotal || 0 }}</span>
|
||||
<span class="resume-detail__score-label">紧急修复</span>
|
||||
</div>
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">{{ diagnosisReport.importantTotal || 0 }}</span>
|
||||
<span class="resume-detail__score-label">重点优化</span>
|
||||
</div>
|
||||
<div class="resume-detail__score-item">
|
||||
<span class="resume-detail__score-num">{{ diagnosisReport.expressionTotal || 0 }}</span>
|
||||
<span class="resume-detail__score-label">表达提升</span>
|
||||
</div>
|
||||
<button class="resume-detail__diagnose-btn" @click="handleDiagnose">重新诊断</button>
|
||||
</template>
|
||||
<!-- 没有诊断报告时显示开始诊断按钮 -->
|
||||
<template v-else>
|
||||
<button class="resume-detail__diagnose-btn resume-detail__diagnose-btn--start" @click="handleDiagnose">开始诊断</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -120,18 +149,6 @@
|
||||
{{ resumeMain.wechatNumber }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.personalInfo.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('personalInfo')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.personalInfo.activeType === t.value }"
|
||||
@click="cardIssueConfig.personalInfo.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('personalInfo')">修复</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 个人概述 -->
|
||||
@@ -144,15 +161,15 @@
|
||||
</button>
|
||||
</div>
|
||||
<p class="resume-detail__summary-text">{{ resumeMain.summary }}</p>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.summary.show" class="resume-detail__card-issue">
|
||||
<!-- 个人概述的问题操作区(仅诊断数据中有 summary 类型且 status===0 时显示) -->
|
||||
<div v-for="issue in getIssuesByModule('summary')" :key="issue.id" v-show="issue.status === 0" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('summary')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.summary.activeType === t.value }"
|
||||
@click="cardIssueConfig.summary.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
v-for="t in getIssueTypeCounts(issue)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('summary', resumeId) === t.value }"
|
||||
@click="setActiveType('summary', resumeId, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('summary')">修复</button>
|
||||
</div>
|
||||
@@ -170,18 +187,6 @@
|
||||
<a :href="resumeMain.portfolioUrl" target="_blank" rel="noopener noreferrer" class="resume-detail__portfolio-link">
|
||||
{{ resumeMain.portfolioUrl }}
|
||||
</a>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.portfolio.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('portfolio')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.portfolio.activeType === t.value }"
|
||||
@click="cardIssueConfig.portfolio.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('portfolio')">修复</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 教育背景 -->
|
||||
@@ -199,18 +204,20 @@
|
||||
<div v-if="edu.description?.length" class="resume-detail__desc-list">
|
||||
<p v-for="p in edu.description" :key="p.id" class="resume-detail__desc-item">{{ p.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.education.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('education')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.education.activeType === t.value }"
|
||||
@click="cardIssueConfig.education.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('education')">修复</button>
|
||||
<!-- 每段教育经历的问题操作区(status===0 待处理才显示) -->
|
||||
<template v-if="hasDiagnosis && getIssueByRecord('education', edu.id!) && getIssueByRecord('education', edu.id!)!.status === 0">
|
||||
<div class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getIssueTypeCounts(getIssueByRecord('education', edu.id!)!)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('education', edu.id!) === t.value }"
|
||||
@click="setActiveType('education', edu.id!, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('education', edu.id)">修复</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -232,18 +239,20 @@
|
||||
<ul v-if="exp.description?.length" class="resume-detail__desc-list pl16">
|
||||
<li v-for="p in exp.description" :key="p.id" class="resume-detail__desc-item fs12 color-5">{{ p.text }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.work.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('work')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.work.activeType === t.value }"
|
||||
@click="cardIssueConfig.work.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('work')">修复</button>
|
||||
<!-- 每段工作经历的问题操作区(status===0 待处理才显示) -->
|
||||
<template v-if="hasDiagnosis && getIssueByRecord('work', exp.id!) && getIssueByRecord('work', exp.id!)!.status === 0">
|
||||
<div class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getIssueTypeCounts(getIssueByRecord('work', exp.id!)!)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('work', exp.id!) === t.value }"
|
||||
@click="setActiveType('work', exp.id!, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('work', exp.id)">修复</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -265,18 +274,20 @@
|
||||
<div v-if="intern.description?.length" class="resume-detail__desc-list">
|
||||
<p v-for="p in intern.description" :key="p.id" class="resume-detail__desc-item">{{ p.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.internship.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('internship')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.internship.activeType === t.value }"
|
||||
@click="cardIssueConfig.internship.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('internship')">修复</button>
|
||||
<!-- 每段实习经历的问题操作区(status===0 待处理才显示) -->
|
||||
<template v-if="hasDiagnosis && getIssueByRecord('internship', intern.id!) && getIssueByRecord('internship', intern.id!)!.status === 0">
|
||||
<div class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getIssueTypeCounts(getIssueByRecord('internship', intern.id!)!)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('internship', intern.id!) === t.value }"
|
||||
@click="setActiveType('internship', intern.id!, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('internship', intern.id)">修复</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -298,18 +309,20 @@
|
||||
<div v-if="proj.description?.length" class="resume-detail__desc-list">
|
||||
<p v-for="p in proj.description" :key="p.id" class="resume-detail__desc-item">{{ p.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.project.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('project')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.project.activeType === t.value }"
|
||||
@click="cardIssueConfig.project.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('project')">修复</button>
|
||||
<!-- 每段项目经历的问题操作区(status===0 待处理才显示) -->
|
||||
<template v-if="hasDiagnosis && getIssueByRecord('project', proj.id!) && getIssueByRecord('project', proj.id!)!.status === 0">
|
||||
<div class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getIssueTypeCounts(getIssueByRecord('project', proj.id!)!)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('project', proj.id!) === t.value }"
|
||||
@click="setActiveType('project', proj.id!, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('project', proj.id)">修复</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -331,18 +344,20 @@
|
||||
<div v-if="comp.description?.length" class="resume-detail__desc-list">
|
||||
<p v-for="p in comp.description" :key="p.id" class="resume-detail__desc-item">{{ p.text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.competition.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('competition')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.competition.activeType === t.value }"
|
||||
@click="cardIssueConfig.competition.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('competition')">修复</button>
|
||||
<!-- 每段竞赛经历的问题操作区(status===0 待处理才显示) -->
|
||||
<template v-if="hasDiagnosis && getIssueByRecord('competition', comp.id!) && getIssueByRecord('competition', comp.id!)!.status === 0">
|
||||
<div class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getIssueTypeCounts(getIssueByRecord('competition', comp.id!)!)" :key="t.value"
|
||||
class="resume-detail__issue-type-btn resume-detail__issue-type-btn--active"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': getActiveType('competition', comp.id!) === t.value }"
|
||||
@click="setActiveType('competition', comp.id!, t.value)"
|
||||
>{{ t.count }} {{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('competition', comp.id)">修复</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -360,18 +375,6 @@
|
||||
{{ skill }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.skills.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('skills')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.skills.activeType === t.value }"
|
||||
@click="cardIssueConfig.skills.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('skills')">修复</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 证书 -->
|
||||
@@ -388,18 +391,6 @@
|
||||
{{ cert }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 问题操作区 -->
|
||||
<div v-if="cardIssueConfig.certificates.show" class="resume-detail__card-issue">
|
||||
<div class="resume-detail__issue-type-group">
|
||||
<button
|
||||
v-for="t in getCardIssueTypes('certificates')" :key="t.value"
|
||||
class="resume-detail__issue-type-btn"
|
||||
:class="{ 'resume-detail__issue-type-btn--active': cardIssueConfig.certificates.activeType === t.value }"
|
||||
@click="cardIssueConfig.certificates.activeType = t.value"
|
||||
>{{ t.label }}</button>
|
||||
</div>
|
||||
<button class="resume-detail__card-btn resume-detail__card-btn--dark" @click="handleFixIssue('certificates')">修复</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -407,10 +398,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import ProfileEditDrawer from '@/components/ProfileEditDrawer.vue'
|
||||
import ResumeAnalysisReportDrawer from '@/components/ResumeAnalysisReportDrawer.vue'
|
||||
import ResumeIssueFixDrawer from '@/components/ResumeIssueFixDrawer.vue'
|
||||
import {
|
||||
fetchResumeMain,
|
||||
fetchResumeEducation,
|
||||
@@ -418,6 +411,8 @@ import {
|
||||
fetchResumeInternship,
|
||||
fetchResumeProject,
|
||||
fetchResumeCompetition,
|
||||
fetchResumeDiagnosis,
|
||||
triggerResumeDiagnosis,
|
||||
saveResumeMain,
|
||||
saveResumeEducation,
|
||||
saveResumeWork,
|
||||
@@ -430,50 +425,101 @@ import {
|
||||
type ResumeInternship,
|
||||
type ResumeProject,
|
||||
type ResumeCompetition,
|
||||
type DiagnosisReport,
|
||||
type DiagnosisIssue,
|
||||
type DescriptionParagraph,
|
||||
} from '@/api/resume'
|
||||
|
||||
// ==================== 问题类型相关 ====================
|
||||
// ==================== 诊断数据相关 ====================
|
||||
|
||||
/** 问题类型枚举值 */
|
||||
type IssueType = 'urgent' | 'optimize' | 'expression'
|
||||
|
||||
/** 问题类型选项列表 */
|
||||
const issueTypes: { label: string; value: IssueType }[] = [
|
||||
{ label: '紧急修复项', value: 'urgent' },
|
||||
{ label: '重点优化项', value: 'optimize' },
|
||||
{ label: '表达提升', value: 'expression' },
|
||||
]
|
||||
/** 是否已有诊断报告 */
|
||||
const hasDiagnosis = ref(false)
|
||||
|
||||
/** 单个卡片的问题配置 */
|
||||
interface CardIssueItem {
|
||||
/** 是否显示问题操作区 */
|
||||
show: boolean
|
||||
/** 当前选中的问题类型 */
|
||||
activeType: IssueType
|
||||
/** 该模块拥有的问题类型列表(只显示这些) */
|
||||
types: IssueType[]
|
||||
}
|
||||
/** 诊断报告数据 */
|
||||
const diagnosisReport = ref<DiagnosisReport>({})
|
||||
|
||||
/** 所有卡片模块的 key */
|
||||
type CardKey = 'personalInfo' | 'summary' | 'portfolio' | 'education' | 'work' | 'internship' | 'project' | 'competition' | 'skills' | 'certificates'
|
||||
/** 诊断问题列表 */
|
||||
const diagnosisIssues = ref<DiagnosisIssue[]>([])
|
||||
|
||||
/** 各模块问题操作区的显示与选中状态配置,每个模块只包含自己实际存在的问题类型 */
|
||||
const cardIssueConfig = reactive<Record<CardKey, CardIssueItem>>({
|
||||
personalInfo: { show: true, activeType: 'urgent', types: ['urgent'] },
|
||||
summary: { show: true, activeType: 'expression', types: ['expression'] },
|
||||
portfolio: { show: true, activeType: 'optimize', types: ['optimize'] },
|
||||
education: { show: true, activeType: 'urgent', types: ['urgent'] },
|
||||
work: { show: true, activeType: 'optimize', types: ['optimize'] },
|
||||
internship: { show: true, activeType: 'optimize', types: ['optimize'] },
|
||||
project: { show: true, activeType: 'expression', types: ['expression'] },
|
||||
competition: { show: true, activeType: 'urgent', types: ['urgent'] },
|
||||
skills: { show: true, activeType: 'optimize', types: ['optimize'] },
|
||||
certificates: { show: true, activeType: 'urgent', types: ['urgent'] },
|
||||
/** 评级文字映射 */
|
||||
const gradeLabel = computed(() => {
|
||||
const map: Record<string, string> = { A: '优秀', B: '良好', C: '一般', D: '待提升' }
|
||||
return map[diagnosisReport.value.grade || ''] || '未评级'
|
||||
})
|
||||
|
||||
/** 根据模块 key 获取该模块可用的问题类型选项 */
|
||||
function getCardIssueTypes(cardKey: CardKey) {
|
||||
return issueTypes.filter(t => cardIssueConfig[cardKey].types.includes(t.value))
|
||||
/** 每个子经历的问题操作区当前选中类型,key 为 "moduleType_recordId" */
|
||||
const issueActiveTypes = reactive<Record<string, IssueType>>({})
|
||||
|
||||
/** 根据模块类型获取该模块的所有 issue */
|
||||
function getIssuesByModule(moduleType: string): DiagnosisIssue[] {
|
||||
return diagnosisIssues.value.filter(i => i.moduleType === moduleType)
|
||||
}
|
||||
|
||||
/** 根据模块类型和记录 ID 获取对应的 issue */
|
||||
function getIssueByRecord(moduleType: string, recordId: string): DiagnosisIssue | undefined {
|
||||
return diagnosisIssues.value.find(i => i.moduleType === moduleType && i.moduleRecordId === recordId)
|
||||
}
|
||||
|
||||
/** 计算单个 issue 的子类型计数,将 urgentIssues/importantIssues/expressionIssues 的 value 求和 */
|
||||
function sumSubCounts(obj?: Record<string, number>): number {
|
||||
if (!obj) return 0
|
||||
return Object.values(obj).reduce((s, v) => s + v, 0)
|
||||
}
|
||||
|
||||
/** 获取单个 issue 的三类问题计数,用于渲染问题类型按钮 */
|
||||
function getIssueTypeCounts(issue: DiagnosisIssue) {
|
||||
const urgent = sumSubCounts(issue.urgentIssues)
|
||||
const important = sumSubCounts(issue.importantIssues)
|
||||
const expression = sumSubCounts(issue.expressionIssues)
|
||||
const result: { label: string; value: IssueType; count: number }[] = []
|
||||
if (urgent > 0) result.push({ label: '紧急修复', value: 'urgent', count: urgent })
|
||||
if (important > 0) result.push({ label: '重点优化', value: 'optimize', count: important })
|
||||
if (expression > 0) result.push({ label: '表达提升', value: 'expression', count: expression })
|
||||
return result
|
||||
}
|
||||
|
||||
/** 获取某个子经历问题操作区的当前选中类型 */
|
||||
function getActiveType(moduleType: string, recordId: string): IssueType {
|
||||
const key = `${moduleType}_${recordId}`
|
||||
if (!issueActiveTypes[key]) {
|
||||
// 默认选中第一个有计数的类型
|
||||
const issue = getIssueByRecord(moduleType, recordId)
|
||||
if (issue) {
|
||||
const types = getIssueTypeCounts(issue)
|
||||
issueActiveTypes[key] = types.length > 0 ? types[0].value : 'urgent'
|
||||
} else {
|
||||
issueActiveTypes[key] = 'urgent'
|
||||
}
|
||||
}
|
||||
return issueActiveTypes[key]
|
||||
}
|
||||
|
||||
/** 设置某个子经历问题操作区的选中类型 */
|
||||
function setActiveType(moduleType: string, recordId: string, type: IssueType) {
|
||||
issueActiveTypes[`${moduleType}_${recordId}`] = type
|
||||
}
|
||||
|
||||
/** 加载诊断报告数据 */
|
||||
async function loadDiagnosis() {
|
||||
try {
|
||||
const res = await fetchResumeDiagnosis(resumeId)
|
||||
if (res.code === 0 && res.data) {
|
||||
hasDiagnosis.value = true
|
||||
diagnosisReport.value = res.data.report
|
||||
diagnosisIssues.value = res.data.issues || []
|
||||
} else {
|
||||
// data 为 null,没有诊断报告
|
||||
hasDiagnosis.value = false
|
||||
diagnosisReport.value = {}
|
||||
diagnosisIssues.value = []
|
||||
}
|
||||
} catch {
|
||||
console.error('[ResumeDetail] 加载诊断报告失败')
|
||||
hasDiagnosis.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 路由相关 ====================
|
||||
@@ -541,6 +587,8 @@ async function loadResumeDetail() {
|
||||
|
||||
onMounted(() => {
|
||||
loadResumeDetail()
|
||||
// 加载诊断报告
|
||||
loadDiagnosis()
|
||||
})
|
||||
|
||||
// ==================== 计算属性 ====================
|
||||
@@ -570,15 +618,190 @@ function handleExport() { console.log('导出') }
|
||||
function handleDelete() { console.log('删除') }
|
||||
|
||||
/** 查看评估报告 */
|
||||
function handleViewReport() { console.log('查看评估报告') }
|
||||
function handleViewReport() {
|
||||
showReportDrawer.value = true
|
||||
}
|
||||
|
||||
/** 重新诊断简历 */
|
||||
function handleDiagnose() { console.log('重新诊断') }
|
||||
/** 简历分析报告抽屉显示状态 */
|
||||
const showReportDrawer = ref(false)
|
||||
|
||||
/** 修复指定模块的问题,传入模块 key */
|
||||
function handleFixIssue(cardKey: CardKey) {
|
||||
const config = cardIssueConfig[cardKey]
|
||||
console.log(`修复模块: ${cardKey}, 问题类型: ${config.activeType}`)
|
||||
/** 分析报告点击去优化 — 关闭抽屉 */
|
||||
function handleClose_report() {
|
||||
showReportDrawer.value = false
|
||||
}
|
||||
|
||||
/** 重新诊断 / 开始诊断 — 调用 AI 诊断接口,完成后刷新诊断数据 */
|
||||
async function handleDiagnose() {
|
||||
// 全屏加载提示,AI 接口响应较慢
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '简历诊断中,请耐心等待…',
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
customClass: 'resume-upload-loading',
|
||||
})
|
||||
try {
|
||||
const res = await triggerResumeDiagnosis(resumeId)
|
||||
if (res.code === 0) {
|
||||
// 诊断完成,重新拉取诊断报告数据
|
||||
await loadDiagnosis()
|
||||
ElMessage.success('诊断完成')
|
||||
} else {
|
||||
ElMessage.error(res.msg || '诊断失败,请稍后重试')
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('诊断请求失败,请稍后重试')
|
||||
} finally {
|
||||
loading.close()
|
||||
}
|
||||
}
|
||||
|
||||
/** 修复指定模块的问题 */
|
||||
function handleFixIssue(moduleType: string, recordId?: string) {
|
||||
fixModuleType.value = moduleType
|
||||
// 确定记录 ID:个人概述用 resumeId,经历用传入的 recordId
|
||||
const rid = moduleType === 'summary' ? resumeId : (recordId || '')
|
||||
fixRecordId.value = rid
|
||||
// 获取原文内容
|
||||
fixOriginalContent.value = getOriginalContent(moduleType, rid)
|
||||
// 获取匹配的诊断问题
|
||||
fixIssue.value = getIssueByRecord(moduleType, rid)
|
||||
showFixDrawer.value = true
|
||||
}
|
||||
|
||||
// ==================== 问题修复抽屉相关 ====================
|
||||
|
||||
/** 修复抽屉显示状态 */
|
||||
const showFixDrawer = ref(false)
|
||||
|
||||
/** 修复抽屉当前模块类型 */
|
||||
const fixModuleType = ref('')
|
||||
|
||||
/** 修复抽屉当前记录 ID */
|
||||
const fixRecordId = ref('')
|
||||
|
||||
/** 修复抽屉原文内容 */
|
||||
const fixOriginalContent = ref<string | DescriptionParagraph[]>('')
|
||||
|
||||
/** 修复抽屉匹配的诊断问题 */
|
||||
const fixIssue = ref<DiagnosisIssue | undefined>(undefined)
|
||||
|
||||
/** 根据模块类型和记录 ID 获取原文内容 */
|
||||
function getOriginalContent(moduleType: string, recordId: string): string | DescriptionParagraph[] {
|
||||
if (moduleType === 'summary') {
|
||||
return resumeMain.value.summary || ''
|
||||
}
|
||||
// 5 大经历模块 — 找到对应记录的 description 数组
|
||||
const listMap: Record<string, any[]> = {
|
||||
education: educationList.value,
|
||||
work: workList.value,
|
||||
internship: internshipList.value,
|
||||
project: projectList.value,
|
||||
competition: competitionList.value,
|
||||
}
|
||||
const list = listMap[moduleType]
|
||||
if (!list) return ''
|
||||
const record = list.find((item: any) => item.id === recordId)
|
||||
return record?.description || []
|
||||
}
|
||||
|
||||
/** 修复抽屉提交新版本 — 更新本地数据、调用保存接口、刷新页面 */
|
||||
async function handleFixSubmit(content: string[]) {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在保存修改…',
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
customClass: 'resume-upload-loading',
|
||||
})
|
||||
|
||||
try {
|
||||
const moduleType = fixModuleType.value
|
||||
const recordId = fixRecordId.value
|
||||
|
||||
if (moduleType === 'summary') {
|
||||
// 个人概述:content 只有一个元素,直接更新 summary 字段
|
||||
const newSummary = content.join('\n')
|
||||
resumeMain.value.summary = newSummary
|
||||
await saveResumeMain({ resumeId, summary: newSummary })
|
||||
} else {
|
||||
// 5 大经历模块:找到对应经历记录,按 id 对应更新 description 数组里的 text
|
||||
const listMap: Record<string, any[]> = {
|
||||
education: educationList.value,
|
||||
work: workList.value,
|
||||
internship: internshipList.value,
|
||||
project: projectList.value,
|
||||
competition: competitionList.value,
|
||||
}
|
||||
const list = listMap[moduleType]
|
||||
if (list) {
|
||||
const record = list.find((item: any) => item.id === recordId)
|
||||
if (record?.description) {
|
||||
// 按顺序更新 description 数组里每个对象的 text,保留原有 id
|
||||
content.forEach((text, idx) => {
|
||||
if (idx < record.description.length) {
|
||||
record.description[idx].text = text
|
||||
}
|
||||
})
|
||||
// 调用对应模块的保存接口(全量覆盖)
|
||||
await saveModuleData(moduleType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重新加载所有简历数据和诊断报告
|
||||
await Promise.all([loadResumeDetail(), loadDiagnosis()])
|
||||
ElMessage.success('修改已保存')
|
||||
} catch {
|
||||
ElMessage.error('保存失败,请稍后重试')
|
||||
} finally {
|
||||
loading.close()
|
||||
}
|
||||
}
|
||||
|
||||
/** 根据模块类型调用对应的保存接口(全量覆盖) */
|
||||
async function saveModuleData(moduleType: string) {
|
||||
if (moduleType === 'education') {
|
||||
await saveResumeEducation(resumeId, educationList.value.map(edu => ({
|
||||
school: edu.school,
|
||||
major: edu.major,
|
||||
degree: edu.degree,
|
||||
studyType: edu.studyType,
|
||||
startDate: edu.startDate,
|
||||
endDate: edu.endDate,
|
||||
description: edu.description,
|
||||
})))
|
||||
} else if (moduleType === 'work') {
|
||||
await saveResumeWork(resumeId, workList.value.map(w => ({
|
||||
companyName: w.companyName,
|
||||
position: w.position,
|
||||
startDate: w.startDate,
|
||||
endDate: w.endDate,
|
||||
description: w.description,
|
||||
})))
|
||||
} else if (moduleType === 'internship') {
|
||||
await saveResumeInternship(resumeId, internshipList.value.map(i => ({
|
||||
companyName: i.companyName,
|
||||
position: i.position,
|
||||
startDate: i.startDate,
|
||||
endDate: i.endDate,
|
||||
description: i.description,
|
||||
})))
|
||||
} else if (moduleType === 'project') {
|
||||
await saveResumeProject(resumeId, projectList.value.map(p => ({
|
||||
projectName: p.projectName,
|
||||
companyName: p.companyName,
|
||||
role: p.role,
|
||||
startDate: p.startDate,
|
||||
endDate: p.endDate,
|
||||
description: p.description,
|
||||
})))
|
||||
} else if (moduleType === 'competition') {
|
||||
await saveResumeCompetition(resumeId, competitionList.value.map(c => ({
|
||||
competitionName: c.competitionName,
|
||||
award: c.award,
|
||||
awardDate: c.awardDate,
|
||||
description: c.description,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 编辑抽屉相关 ====================
|
||||
|
||||
Reference in New Issue
Block a user