登陆页左侧新广告特效

This commit is contained in:
2026-06-28 19:23:54 +08:00
parent 0888d9fa31
commit 0aebde7bd3
15 changed files with 452 additions and 133 deletions
+6 -2
View File
@@ -67,8 +67,10 @@ export interface ResumeMainData {
email?: string
/** 手机号码 */
mobileNumber?: string
/** 所在城市 */
/** 所在城市(名称) */
city?: string
/** 所在城市编码 */
regionCode?: string
/** 微信号 */
wechatNumber?: string
/** 作品集链接 */
@@ -251,8 +253,10 @@ export interface SaveResumeMainParams {
email?: string
/** 手机号码 */
mobileNumber?: string
/** 所在城市 */
/** 所在城市(名称) */
city?: string
/** 所在城市编码 */
regionCode?: string
/** 微信号 */
wechatNumber?: string
/** 作品集链接 */
+3
View File
@@ -737,10 +737,13 @@ body:not(#_) {
.ml100 {margin-left: 1rem;}
.ml135 {margin-left: 1.35rem;}
.mlauto {margin-left: auto;}
/* margin-right */
.mr0 {margin-right: 0rem;}
.mr1 {margin-right: 0.01rem;} .mr2 {margin-right: 0.02rem;} .mr3 {margin-right: 0.03rem;} .mr4 {margin-right: 0.04rem;} .mr5 {margin-right: 0.05rem;} .mr6 {margin-right: 0.06rem;} .mr7 {margin-right: 0.07rem;} .mr8 {margin-right: 0.08rem;} .mr9 {margin-right: 0.09rem;} .mr10 {margin-right: 0.1rem;} .mr11 {margin-right: 0.11rem;} .mr12 {margin-right: 0.12rem;} .mr13 {margin-right: 0.13rem;} .mr14 {margin-right: 0.14rem;} .mr15 {margin-right: 0.15rem;} .mr16 {margin-right: 0.16rem;} .mr17 {margin-right: 0.17rem;} .mr18 {margin-right: 0.18rem;} .mr19 {margin-right: 0.19rem;} .mr20 {margin-right: 0.2rem;} .mr21 {margin-right: 0.21rem;} .mr22 {margin-right: 0.22rem;} .mr23 {margin-right: 0.23rem;} .mr24 {margin-right: 0.24rem;} .mr25 {margin-right: 0.25rem;} .mr26 {margin-right: 0.26rem;} .mr27 {margin-right: 0.27rem;} .mr28 {margin-right: 0.28rem;} .mr29 {margin-right: 0.29rem;} .mr30 {margin-right: 0.3rem;} .mr31 {margin-right: 0.31rem;} .mr32 {margin-right: 0.32rem;} .mr33 {margin-right: 0.33rem;} .mr34 {margin-right: 0.34rem;} .mr35 {margin-right: 0.35rem;} .mr36 {margin-right: 0.36rem;} .mr37 {margin-right: 0.37rem;} .mr38 {margin-right: 0.38rem;} .mr39 {margin-right: 0.39rem;} .mr40 {margin-right: 0.4rem;} .mr41 {margin-right: 0.41rem;} .mr42 {margin-right: 0.42rem;} .mr43 {margin-right: 0.43rem;} .mr44 {margin-right: 0.44rem;} .mr45 {margin-right: 0.45rem;} .mr46 {margin-right: 0.46rem;} .mr47 {margin-right: 0.47rem;} .mr48 {margin-right: 0.48rem;} .mr49 {margin-right: 0.49rem;} .mr50 {margin-right: 0.5rem;} .mr51 {margin-right: 0.51rem;} .mr52 {margin-right: 0.52rem;} .mr53 {margin-right: 0.53rem;} .mr54 {margin-right: 0.54rem;} .mr55 {margin-right: 0.55rem;} .mr56 {margin-right: 0.56rem;} .mr57 {margin-right: 0.57rem;} .mr58 {margin-right: 0.58rem;} .mr59 {margin-right: 0.59rem;} .mr60 {margin-right: 0.6rem;} .mr61 {margin-right: 0.61rem;} .mr62 {margin-right: 0.62rem;} .mr63 {margin-right: 0.63rem;} .mr64 {margin-right: 0.64rem;} .mr65 {margin-right: 0.65rem;} .mr66 {margin-right: 0.66rem;} .mr67 {margin-right: 0.67rem;} .mr68 {margin-right: 0.68rem;} .mr69 {margin-right: 0.69rem;} .mr70 {margin-right: 0.7rem;} .mr71 {margin-right: 0.71rem;} .mr72 {margin-right: 0.72rem;} .mr73 {margin-right: 0.73rem;} .mr74 {margin-right: 0.74rem;} .mr75 {margin-right: 0.75rem;} .mr76 {margin-right: 0.76rem;} .mr77 {margin-right: 0.77rem;} .mr78 {margin-right: 0.78rem;} .mr79 {margin-right: 0.79rem;} .mr80 {margin-right: 0.8rem;} .mr81 {margin-right: 0.81rem;} .mr82 {margin-right: 0.82rem;} .mr83 {margin-right: 0.83rem;} .mr84 {margin-right: 0.84rem;} .mr85 {margin-right: 0.85rem;} .mr86 {margin-right: 0.86rem;} .mr87 {margin-right: 0.87rem;} .mr88 {margin-right: 0.88rem;} .mr89 {margin-right: 0.89rem;} .mr90 {margin-right: 0.9rem;} .mr91 {margin-right: 0.91rem;} .mr92 {margin-right: 0.92rem;} .mr93 {margin-right: 0.93rem;} .mr94 {margin-right: 0.94rem;} .mr95 {margin-right: 0.95rem;} .mr96 {margin-right: 0.96rem;} .mr97 {margin-right: 0.97rem;} .mr98 {margin-right: 0.98rem;} .mr99 {margin-right: 0.99rem;}
.mr100 {margin-right: 1rem;}
.mrauto {margin-right: auto;}
/* margin-right-left */
.mrl0 {margin-right: 0rem;margin-left: 0rem;}
@@ -20,6 +20,7 @@
padding-bottom: 0.14rem;
border-bottom: 1px solid $border-color;
flex-shrink: 0;
margin-bottom:0.14rem
}
&__tab-item {
@@ -62,6 +63,14 @@
line-height: 1.5;
border-radius: 0.08rem;
&:hover .profile-page-content__card-header{
background: #F7F7F7;
.profile-page-content__edit-btn-inline{
opacity: 1;
pointer-events: auto;
}
}
&:not(:last-child) {
//border-bottom: 1px solid $border-color;
}
@@ -130,9 +139,9 @@
&__info-contacts {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.16rem;
margin-bottom: 0.06rem;
}
&__contact-item {
@@ -160,18 +169,22 @@
&__item-left {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 0.2rem;
}
&__item-title {
font-size: 0.14rem;
font-size: 0.16rem;
font-weight: 600;
color: $text-dark;
margin-bottom: 0.03rem;
}
&__item-sub {
font-size: 0.12rem;
color: #666;
font-size: 0.16rem;
color: $text-dark;
margin-bottom: 0.02rem;
}
@@ -182,11 +195,10 @@
}
&__item-period {
font-size: 0.11rem;
color: $text-light;
font-size: 0.13rem;
color: #666;
white-space: nowrap;
flex-shrink: 0;
margin-left: 0.12rem;
}
&__item-desc {
@@ -261,7 +273,7 @@
border: none;
color: $accent;
cursor: pointer;
padding: 0.04rem 0.08rem;
padding: 0 0.08rem;
border-radius: 0.04rem;
display: flex;
align-items: center;
@@ -279,7 +291,6 @@
}
&:hover {
background: $theme-color;
}
}
@@ -295,12 +306,10 @@
// 标题行hover显示添加按钮(5大经历)
&__header-hover:hover {
background: #F7F7F7;
border-radius: 0.06rem;
}
&__header-hover:hover &__edit-btn-inline {
opacity: 1;
pointer-events: auto;
}
+34 -33
View File
@@ -15,14 +15,15 @@
height: 100%;
overflow: hidden;
background: #ddf4f2;
border-right: 0.08rem solid #4FC2C9;
}
.login-jianshi-logo {
position: absolute;
right: 22px;
bottom: 10px;
width: 82px;
height: 21px;
right: 0.22rem;
bottom: 0.1rem;
width: 0.82rem;
height: 0.21rem;
object-fit: contain;
pointer-events: auto;
user-select: none;
@@ -33,28 +34,28 @@
position: absolute;
left: 0;
top: 0;
width: 1100px;
height: 900px;
width: 11rem;
height: 9rem;
overflow: hidden;
background-repeat: no-repeat;
background-size: 1405px 1269px;
background-position: -210px -117px;
background-size: 14.05rem 12.69rem;
background-position: -2.1rem -1.17rem;
z-index: 1;
transform-origin: top left;
transform: translate3d(var(--stage-x, 0px), var(--stage-y, 0px), 0) scale(var(--stage-scale, 1));
transform: translate3d(var(--stage-x, 0rem), var(--stage-y, 0rem), 0) scale(var(--stage-scale, 1));
}
/* 标题 */
.hero-title {
position: absolute;
left: 35px;
top: 64px;
width: 374px;
height: 88px;
left: 0.35rem;
top: 0.64rem;
width: 3.74rem;
height: 0.88rem;
color: #2a8186;
font-size: 36px;
font-size: 0.36rem;
font-weight: 700;
line-height: 44px;
line-height: 0.44rem;
z-index: 3;
pointer-events: none;
white-space: nowrap;
@@ -62,16 +63,16 @@
.hero-title__second-line {
display: block;
padding-left: 72px;
padding-left: 0.72rem;
}
/* Nova 核心机器人 */
.nova-core {
position: absolute;
left: 183px;
top: 259px;
width: 592px;
height: 443px;
left: 1.83rem;
top: 2.59rem;
width: 5.92rem;
height: 4.43rem;
pointer-events: auto;
z-index: 2;
user-select: none;
@@ -89,8 +90,8 @@
.nova-eye {
position: absolute;
width: 20px;
height: 44px;
width: 0.2rem;
height: 0.44rem;
object-fit: contain;
pointer-events: none;
user-select: none;
@@ -100,13 +101,13 @@
}
.nova-eye-left {
left: 253.77px;
top: 177px;
left: 2.5377rem;
top: 1.77rem;
}
.nova-eye-right {
left: 310.77px;
top: 177px;
left: 3.1077rem;
top: 1.77rem;
}
.nova-eye.is-blinking {
@@ -132,8 +133,8 @@
pointer-events: none;
object-fit: contain;
will-change: opacity, transform, filter;
transform: translate3d(0, 4px, 0) scale(.996);
filter: drop-shadow(0 16px 28px rgba(56, 255, 202, .10)) brightness(1);
transform: translate3d(0, 0.04rem, 0) scale(.996);
filter: drop-shadow(0 0.16rem 0.28rem rgba(56, 255, 202, .10)) brightness(1);
}
.node.is-running {
@@ -144,18 +145,18 @@
@keyframes node-life {
0% {
opacity: 0;
transform: translate3d(0, 4px, 0) scale(.996);
filter: drop-shadow(0 16px 28px rgba(56, 255, 202, .10)) brightness(1);
transform: translate3d(0, 0.04rem, 0) scale(.996);
filter: drop-shadow(0 0.16rem 0.28rem rgba(56, 255, 202, .10)) brightness(1);
}
20.896%, 79.104% {
opacity: 1;
transform: translate3d(0, 0, 0) scale(1);
filter: drop-shadow(0 16px 28px rgba(56, 255, 202, .20)) brightness(1.045);
filter: drop-shadow(0 0.16rem 0.28rem rgba(56, 255, 202, .20)) brightness(1.045);
}
100% {
opacity: 0;
transform: translate3d(0, -4px, 0) scale(.996);
filter: drop-shadow(0 16px 28px rgba(56, 255, 202, .10)) brightness(1);
transform: translate3d(0, -0.04rem, 0) scale(.996);
filter: drop-shadow(0 0.16rem 0.28rem rgba(56, 255, 202, .10)) brightness(1);
}
}
+12 -6
View File
@@ -262,6 +262,13 @@
border-radius: 0.1rem;
margin: 0.4rem 0.18rem;
&:hover .resume-detail__section-header {
background: #F7F7F7;
.resume-detail__edit-btn-inline {
opacity: 1;
pointer-events: auto;
}
}
}
@@ -346,7 +353,7 @@
align-items: center;
gap: 0.06rem;
font-size: 0.12rem;
color: $text-dark;
color: #666;
}
&__contact-icon {
@@ -363,6 +370,7 @@
gap: 0.12rem;
margin-bottom: 0.16rem;
padding: 0.1rem 0 0.1rem 0.14rem;
border-radius: 0.06rem;
}
&__section-title {
@@ -432,19 +440,18 @@
gap: 0.04rem;
transition: all 0.2s;
flex-shrink: 0;
margin-left: auto;
font-size: 0.13rem;
font-weight: 500;
// 默认隐藏,hover卡片时显示
opacity: 0;
pointer-events: none;
margin-left: auto;
.el-icon {
font-size: 0.15rem;
}
&:hover {
background: $theme-color;
}
}
@@ -456,19 +463,18 @@
// 经历模块标题栏hover时显示添加按钮
&__section-header:hover &__edit-btn-inline {
opacity: 1;
//opacity: 1;
pointer-events: auto;
}
// 经历模块标题栏hover时背景色
&__section-header:hover {
background: #F7F7F7;
//background: #F7F7F7;
border-radius: 0.06rem;
}
// 经历条目内的编辑按钮(hover exp-item 时显示)
&__exp-edit-btn {
margin-left: auto;
}
&__exp-item:hover &__exp-edit-btn {
+106 -1
View File
@@ -33,7 +33,7 @@
<template v-if="currentStep === 1">
<div class="agent-page__right">
<div class="agent-page__profile-wrapper">
<ProfilePageContent :profile="profile" @edit="handleEdit" />
<ProfilePageContent :profile="profile" @edit="handleEdit" @save="handleInlineSave" @exp-update="handleExpUpdate" @exp-add="handleExpAdd" @exp-delete="handleExpDelete" />
</div>
</div>
<div class="agent-page__left">
@@ -433,6 +433,111 @@ async function handleSaveEdit(data: Record<string, any>) {
}
}
/** 内联保存处理(个人信息/技能/证书模块从组件内直接保存) */
async function handleInlineSave(section: string, data: Record<string, any>) {
editModule.value = section
await handleSaveEdit(data)
}
/** 单条经历更新 — 替换到完整列表后整体保存 */
async function handleExpUpdate(moduleType: string, index: number, data: Record<string, any>) {
try {
if (moduleType === 'education') {
const list = profile.value.education.map((item, i) => i === index ? data : item) as any[]
await saveEducation(list)
profile.value.education = list
} else if (moduleType === 'work') {
const list = profile.value.works.map((item, i) => i === index ? data : item) as any[]
await saveWork(list)
profile.value.works = list
} else if (moduleType === 'internship') {
const list = profile.value.internships.map((item, i) => i === index ? data : item) as any[]
await saveInternship(list)
profile.value.internships = list
} else if (moduleType === 'project') {
const list = profile.value.projects.map((item, i) => i === index ? data : item) as any[]
await saveProject(list)
profile.value.projects = list
} else if (moduleType === 'competition') {
const list = profile.value.competitions.map((item, i) => i === index ? data : item) as any[]
await saveCompetition(list)
profile.value.competitions = list
}
ElMessage.success('保存成功')
} catch {
ElMessage.error('保存失败')
}
}
/** 新增经历 */
async function handleExpAdd(moduleType: string, data: Record<string, any>) {
try {
if (moduleType === 'education') {
const list = [...profile.value.education, data] as any[]
await saveEducation(list)
profile.value.education = list
} else if (moduleType === 'work') {
const list = [...profile.value.works, data] as any[]
await saveWork(list)
profile.value.works = list
} else if (moduleType === 'internship') {
const list = [...profile.value.internships, data] as any[]
await saveInternship(list)
profile.value.internships = list
} else if (moduleType === 'project') {
const list = [...profile.value.projects, data] as any[]
await saveProject(list)
profile.value.projects = list
} else if (moduleType === 'competition') {
const list = [...profile.value.competitions, data] as any[]
await saveCompetition(list)
profile.value.competitions = list
}
ElMessage.success('添加成功')
} catch {
ElMessage.error('添加失败')
}
}
/** 删除单条经历 — 确认后从列表移除并全量保存 */
async function handleExpDelete(moduleType: string, index: number) {
try {
await ElMessageBox.confirm('确定要删除这条经历吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return
}
try {
if (moduleType === 'education') {
const list = profile.value.education.filter((_, i) => i !== index) as any[]
await saveEducation(list)
profile.value.education = list
} else if (moduleType === 'work') {
const list = profile.value.works.filter((_, i) => i !== index) as any[]
await saveWork(list)
profile.value.works = list
} else if (moduleType === 'internship') {
const list = profile.value.internships.filter((_, i) => i !== index) as any[]
await saveInternship(list)
profile.value.internships = list
} else if (moduleType === 'project') {
const list = profile.value.projects.filter((_, i) => i !== index) as any[]
await saveProject(list)
profile.value.projects = list
} else if (moduleType === 'competition') {
const list = profile.value.competitions.filter((_, i) => i !== index) as any[]
await saveCompetition(list)
profile.value.competitions = list
}
ElMessage.success('删除成功')
} catch {
ElMessage.error('删除失败')
}
}
// ==================== 第2步:确认目标 ====================
const showJobGoalDialog = ref(false)
const intentionEmploymentLabel = computed(() => formatEmploymentType(store.state.jobIntention.recruitCategory))
+1 -1
View File
@@ -492,7 +492,7 @@ async function handleAiPolish() {
aiPolishing.value = true
const loading = ElLoading.service({
lock: true,
text: 'AI润色中…',
text: 'AI正在润色中…',
background: 'rgba(0, 0, 0, 0.5)',
customClass: 'resume-upload-loading',
})
+65 -25
View File
@@ -22,18 +22,27 @@
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
</div>
<div class="profile-page-content__info-name">{{ profile.name }}</div>
<div class="profile-page-content__info-name dflex-start aliite-c"><span>{{ profile.name }}</span><span class="fs13 color-6 ml10 dflex-start aliite-c"><svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<circle cx="8" cy="6.5" r="2.5" stroke="currentColor" stroke-width="1.2"/>
<path d="M8 14s-5-4-5-7.5a5 5 0 0110 0C13 10 8 14 8 14z" stroke="currentColor" stroke-width="1.2"/>
</svg> {{ resolveRegionName(profile.regionCode) }}</span></div>
<div class="profile-page-content__info-contacts">
<span class="profile-page-content__contact-item">📱 {{ profile.phone }}</span>
<span class="profile-page-content__contact-item"> {{ profile.email }}</span>
<span class="profile-page-content__contact-item">📍 {{ resolveRegionName(profile.regionCode) }}</span>
</div>
<div class="profile-page-content__info-contacts" v-if="profile.wechat">
<span class="profile-page-content__contact-item">💬 {{ profile.wechat }}</span>
</div>
<span class="profile-page-content__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> {{ profile.phone }}</span>
<span class="profile-page-content__contact-item"><svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<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>{{ profile.email }}</span>
<span class="profile-page-content__info-contacts" v-if="profile.wechat">
<span class="profile-page-content__contact-item"><el-icon class="fs14 color-9"><ChatLineRound /></el-icon> {{ profile.wechat }}</span>
</span>
<!-- 作品集链接显示在个人信息卡片内 -->
<div v-if="profile.portfolioUrl" class="profile-page-content__info-contacts">
<span v-if="profile.portfolioUrl" class="profile-page-content__info-contacts">
<span class="profile-page-content__contact-item">🔗 <a :href="profile.portfolioUrl" target="_blank" rel="noopener noreferrer" class="profile-page-content__portfolio-link">{{ profile.portfolioUrl }}</a></span>
</span>
</div>
</template>
<!-- 编辑模式 -->
@@ -57,7 +66,7 @@
</div>
<!-- 新增 -->
<ResumeExpInlineEdit v-if="addingModule === 'education'" module-type="education" @save="handleExpAdd('education', $event)" @cancel="addingModule = ''" />
<div v-for="(edu, i) in profile.education" :key="i" class="profile-page-content__section-item">
<div v-for="(edu, i) in profile.education" :key="i" class="profile-page-content__section-item" @click.stop="startEditExp('education', i, edu)">
<template v-if="editingExpIndex !== `education_${i}`">
<div class="profile-page-content__item-row">
<div class="profile-page-content__item-left">
@@ -65,9 +74,13 @@
<div class="profile-page-content__item-sub">{{ getDegreeText(edu.studyType, edu.degree) }} · {{ edu.major }}</div>
<div class="profile-page-content__item-date">{{ edu.startDate }} - {{ edu.endDate }}</div>
</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click="startEditExp('education', i, edu)">
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click.stop="handleExpDelete('education', i)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
<ul class="profile-page-content__item-list" v-if="edu.description?.length && edu.description[0]?.text">
<li v-for="desc in edu.description" :key="desc.id">{{ desc.text }}</li>
@@ -86,17 +99,22 @@
</button>
</div>
<ResumeExpInlineEdit v-if="addingModule === 'work'" module-type="work" @save="handleExpAdd('work', $event)" @cancel="addingModule = ''" />
<div v-for="(exp, i) in profile.works" :key="i" class="profile-page-content__section-item">
<div v-for="(exp, i) in profile.works" :key="i" class="profile-page-content__section-item" @click.stop="startEditExp('work', i, exp)">
<template v-if="editingExpIndex !== `work_${i}`">
<div class="profile-page-content__item-row">
<div class="profile-page-content__item-left">
<div class="profile-page-content__item-title">{{ exp.companyName }}</div>
<div class="profile-page-content__item-sub">{{ exp.position }}</div>
</div>
<div class="profile-page-content__item-period">{{ exp.startDate }} - {{ exp.endDate || '至今' }}</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click="startEditExp('work', i, exp)">
</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click.stop="handleExpDelete('work', i)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
<ul class="profile-page-content__item-list">
<li v-for="desc in exp.description" :key="desc.id">{{ desc.text }}</li>
@@ -115,17 +133,22 @@
</button>
</div>
<ResumeExpInlineEdit v-if="addingModule === 'internship'" module-type="internship" @save="handleExpAdd('internship', $event)" @cancel="addingModule = ''" />
<div v-for="(exp, i) in profile.internships" :key="i" class="profile-page-content__section-item">
<div v-for="(exp, i) in profile.internships" :key="i" class="profile-page-content__section-item" @click.stop="startEditExp('internship', i, exp)">
<template v-if="editingExpIndex !== `internship_${i}`">
<div class="profile-page-content__item-row">
<div class="profile-page-content__item-left">
<div class="profile-page-content__item-title">{{ exp.companyName }}</div>
<div class="profile-page-content__item-sub">{{ exp.position }}</div>
</div>
<div class="profile-page-content__item-period">{{ exp.startDate }} - {{ exp.endDate }}</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click="startEditExp('internship', i, exp)">
</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click.stop="handleExpDelete('internship', i)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
<ul class="profile-page-content__item-list">
<li v-for="desc in exp.description" :key="desc.id">{{ desc.text }}</li>
@@ -144,17 +167,22 @@
</button>
</div>
<ResumeExpInlineEdit v-if="addingModule === 'project'" module-type="project" @save="handleExpAdd('project', $event)" @cancel="addingModule = ''" />
<div v-for="(proj, i) in profile.projects" :key="i" class="profile-page-content__section-item">
<div v-for="(proj, i) in profile.projects" :key="i" class="profile-page-content__section-item" @click.stop="startEditExp('project', i, proj)">
<template v-if="editingExpIndex !== `project_${i}`">
<div class="profile-page-content__item-row">
<div class="profile-page-content__item-left">
<div class="profile-page-content__item-title">{{ proj.projectName }}</div>
<div class="profile-page-content__item-sub">{{ proj.role }}</div>
</div>
<div class="profile-page-content__item-sub" v-if="proj.role">{{ proj.role }}</div>
<div class="profile-page-content__item-period">{{ proj.startDate }} - {{ proj.endDate }}</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click="startEditExp('project', i, proj)">
</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click.stop="handleExpDelete('project', i)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
<ul class="profile-page-content__item-list">
<li v-for="desc in proj.description" :key="desc.id">{{ desc.text }}</li>
@@ -173,16 +201,21 @@
</button>
</div>
<ResumeExpInlineEdit v-if="addingModule === 'competition'" module-type="competition" @save="handleExpAdd('competition', $event)" @cancel="addingModule = ''" />
<div v-for="(comp, i) in profile.competitions" :key="i" class="profile-page-content__section-item">
<div v-for="(comp, i) in profile.competitions" :key="i" class="profile-page-content__section-item" @click.stop="startEditExp('competition', i, comp)">
<template v-if="editingExpIndex !== `competition_${i}`">
<div class="profile-page-content__item-row">
<div class="profile-page-content__item-left">
<div class="profile-page-content__item-title">{{ comp.competitionName }} · {{ comp.award }}</div>
</div>
<div class="profile-page-content__item-period">{{ comp.awardDate }}</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click="startEditExp('competition', i, comp)">
</div>
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn">
<el-icon><EditPen /></el-icon><span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="profile-page-content__edit-btn-inline profile-page-content__item-edit-btn" @click.stop="handleExpDelete('competition', i)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
<ul class="profile-page-content__item-list" v-if="comp.description[0]?.text">
<li v-for="desc in comp.description" :key="desc.id">{{ desc.text }}</li>
@@ -243,7 +276,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { EditPen, Plus } from '@element-plus/icons-vue'
import {EditPen, Plus, DeleteFilled, ChatLineRound} from '@element-plus/icons-vue'
import { resolveRegionName } from '@/utils/region'
import ProfileEditDrawer from '@/components/ProfileEditDrawer.vue'
import ResumeExpInlineEdit from '@/components/ResumeExpInlineEdit.vue'
@@ -328,6 +361,8 @@ const emit = defineEmits<{
(e: 'expUpdate', moduleType: string, index: number, data: Record<string, any>): void
/** 新增经历事件 */
(e: 'expAdd', moduleType: string, data: Record<string, any>): void
/** 删除经历事件 */
(e: 'expDelete', moduleType: string, index: number): void
}>()
/** 当前编辑的模块(info/skills/certificate */
@@ -412,6 +447,11 @@ function handleExpAdd(moduleType: string, data: Record<string, any>) {
emit('expAdd', moduleType, data)
addingModule.value = ''
}
/** 删除单条经历(直接触发,不弹确认框,由父组件处理确认) */
function handleExpDelete(moduleType: string, index: number) {
emit('expDelete', moduleType, index)
}
</script>
<style scoped lang="scss">
+3 -3
View File
@@ -87,8 +87,8 @@
<!-- 底部操作按钮 -->
<div class="profile-edit-inline__footer">
<button class="profile-edit-inline__save-btn" @click="handleSave">保存</button>
<button class="profile-edit-inline__cancel-btn" @click="handleCancel">取消</button>
<button class="profile-edit-inline__save-btn" @click.stop="handleSave">保存</button>
<button class="profile-edit-inline__cancel-btn" @click.stop="handleCancel">取消</button>
</div>
<!-- AI润色结果保存取消下方 -->
@@ -177,7 +177,7 @@ async function handleAiPolish() {
return
}
aiPolishing.value = true
const loading = ElLoading.service({ lock: true, text: 'AI润色中…', background: 'rgba(0,0,0,0.5)' })
const loading = ElLoading.service({ lock: true, text: 'AI正在润色中…', background: 'rgba(0,0,0,0.5)' })
try {
const content = text.split('\n').filter(s => s.trim())
const res = await polishDiagnosisIssue(content)
+11 -1
View File
@@ -332,6 +332,10 @@ function toggleDropdown() {
visible.value = !visible.value
if (visible.value) {
searchText.value = ''
// 如果 store 中地区数据为空(页面刷新等场景),主动请求加载
if (!regions.value.length) {
store.dispatch('loadCommonData')
}
if (regions.value.length && !activeProvinceCode.value) {
activeProvinceCode.value = regions.value[0].code
}
@@ -390,5 +394,11 @@ watch(
)
/** 树数据加载完成后重新同步选中项(解决并行加载时树数据晚于 prop 到达的问题) */
watch(regions, () => syncFromProps())
watch(regions, () => {
syncFromProps()
// 地区数据加载完成后,如果面板已打开且没有激活省份,默认选中第一个
if (visible.value && regions.value.length && !activeProvinceCode.value) {
activeProvinceCode.value = regions.value[0].code
}
})
</script>
+1
View File
@@ -12,6 +12,7 @@ import type { UserInfo } from '@/api/auth'
/** 招聘分类选项:label → 接口参数 recruitCategory */
export const JOB_TYPE_OPTIONS: { label: string; value: number }[] = [
{ label: '全部', value: null },
{ label: '社招', value: 0 },
{ label: '校招', value: 1 },
{ label: '实习', value: 2 },
+8 -6
View File
@@ -562,8 +562,9 @@ function fitVisualStage() {
const offsetX = (rect.width - baseWidth * scale) / 2
const offsetY = (rect.height - baseHeight * scale) / 2
stage.style.setProperty('--stage-scale', scale.toFixed(5))
stage.style.setProperty('--stage-x', `${offsetX.toFixed(2)}px`)
stage.style.setProperty('--stage-y', `${offsetY.toFixed(2)}px`)
// translate 使用 rem(÷100
stage.style.setProperty('--stage-x', `${(offsetX / 100).toFixed(4)}rem`)
stage.style.setProperty('--stage-y', `${(offsetY / 100).toFixed(4)}rem`)
}
/** 创建一个浮动节点面板并播放动画 */
@@ -575,10 +576,11 @@ function createPanel(spec: { src: string; x: number; y: number; w: number; h: nu
img.src = spec.src
img.alt = ''
img.draggable = false
img.style.setProperty('--x', `${spec.x}px`)
img.style.setProperty('--y', `${spec.y}px`)
img.style.setProperty('--w', `${spec.w}px`)
img.style.setProperty('--h', `${spec.h}px`)
// 使用 rem 单位(1rem = 100px
img.style.setProperty('--x', `${(spec.x / 100).toFixed(4)}rem`)
img.style.setProperty('--y', `${(spec.y / 100).toFixed(4)}rem`)
img.style.setProperty('--w', `${(spec.w / 100).toFixed(4)}rem`)
img.style.setProperty('--h', `${(spec.h / 100).toFixed(4)}rem`)
img.style.setProperty('--life-ms', `${animTiming.lifeMs}ms`)
let fallbackTimer: ReturnType<typeof setTimeout> | null = null
+41 -1
View File
@@ -30,6 +30,7 @@
@save="handleInlineSave"
@exp-update="handleExpUpdate"
@exp-add="handleExpAdd"
@exp-delete="handleExpDelete"
/>
<!-- 右侧入口 -->
@@ -48,7 +49,7 @@ 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 } from 'element-plus'
import { ElMessage, ElLoading, ElMessageBox } from 'element-plus'
import 'element-plus/es/components/loading/style/css'
import type { SaveEducationItem, SaveWorkItem, SaveProjectItem, SaveCompetitionItem } from '@/api/profile'
@@ -710,4 +711,43 @@ async function handleExpAdd(moduleType: string, data: Record<string, any>) {
ElMessage.error('添加失败')
}
}
/** 删除单条经历 — 从列表中移除对应索引后全量保存 */
async function handleExpDelete(moduleType: string, index: number) {
try {
await ElMessageBox.confirm('确定要删除这条经历吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
} catch {
return //
}
try {
if (moduleType === 'education') {
const list = profile.value.education.filter((_, i) => i !== index) as any[]
await saveEducation(list)
await loadEducation()
} else if (moduleType === 'work') {
const list = profile.value.works.filter((_, i) => i !== index) as any[]
await saveWork(list)
await loadWork()
} else if (moduleType === 'internship') {
const list = profile.value.internships.filter((_, i) => i !== index) as any[]
await saveInternship(list)
await loadInternship()
} else if (moduleType === 'project') {
const list = profile.value.projects.filter((_, i) => i !== index) as any[]
await saveProject(list)
await loadProject()
} else if (moduleType === 'competition') {
const list = profile.value.competitions.filter((_, i) => i !== index) as any[]
await saveCompetition(list)
await loadCompetition()
}
ElMessage.success('删除成功')
} catch {
ElMessage.error('删除失败')
}
}
</script>
+8 -2
View File
@@ -68,7 +68,7 @@
>导出</button>
<button
class="resume-page__action-btn resume-page__action-btn--delete"
@click.stop="handleDelete(item.id)"
@click.stop="handleDelete(item.id, item.isDefault)"
>删除</button>
</div>
</td>
@@ -262,13 +262,19 @@ function handleExport(item: ResumeItem) {
}
/** 删除简历 */
async function handleDelete(id: string) {
async function handleDelete(id: string, isDefault: boolean) {
try {
await ElMessageBox.confirm('确定要删除这份简历吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
if(resumeList.value.length<=1||isDefault){
ElMessage.warning('默认简历不可删除')
return
}
const res = await deleteResume(id)
if (res.code === '0') {
ElMessage.success('删除成功')
+114 -22
View File
@@ -135,10 +135,17 @@
<div class="resume-detail__card" :class="{ 'resume-detail__card-hover': editModule !== 'info' || !showEditDrawer }">
<!-- 展示模式 -->
<template v-if="editModule !== 'info' || !showEditDrawer">
<div class="resume-detail__card-header">
<div>
<div class="resume-detail__card-header color-6">
<div class="dflex-start aliite-c">
<h3 class="resume-detail__user-name">{{ resumeMain.name || '未填写姓名' }}</h3>
<p class="resume-detail__user-title">{{ resumeMain.targetPosition || '' }}</p>
<span v-if="resumeMain.city" class="ml10 dflex-start aliite-c fs13">
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<circle cx="8" cy="6.5" r="2.5" stroke="currentColor" stroke-width="1.2"/>
<path d="M8 14s-5-4-5-7.5a5 5 0 0110 0C13 10 8 14 8 14z" stroke="currentColor" stroke-width="1.2"/>
</svg>
{{ resumeMain.city }}
</span>
</div>
<!-- 编辑按钮 hover时才显示品牌色图标+文字 -->
<button class="resume-detail__edit-btn-inline" @click="openEditDrawer('info')" aria-label="编辑个人信息">
@@ -162,20 +169,10 @@
</svg>
{{ resumeMain.mobileNumber }}
</span>
<!-- 所在城市 -->
<span v-if="resumeMain.city" class="resume-detail__contact-item">
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<circle cx="8" cy="6.5" r="2.5" stroke="currentColor" stroke-width="1.2"/>
<path d="M8 14s-5-4-5-7.5a5 5 0 0110 0C13 10 8 14 8 14z" stroke="currentColor" stroke-width="1.2"/>
</svg>
{{ resumeMain.city }}
</span>
<!-- 微信号 -->
<span v-if="resumeMain.wechatNumber" class="resume-detail__contact-item">
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__contact-icon">
<path d="M2 8a6 6 0 1112 0 6 6 0 01-12 0z" stroke="currentColor" stroke-width="1.2"/>
<path d="M5.5 7.5c.5-1 1.5-1.5 2.5-1.5s2 .5 2.5 1.5M6 10s.8 1 2 1 2-1 2-1" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
</svg>
<el-icon class="fs14 color-9"><ChatLineRound /></el-icon>
{{ resumeMain.wechatNumber }}
</span>
<!-- 作品集链接 -->
@@ -250,7 +247,7 @@
@save="handleExpAdd('education', $event)"
@cancel="expAddingModule = ''"
/>
<div v-for="edu in educationList" :key="edu.id" class="resume-detail__exp-item">
<div v-for="edu in educationList" :key="edu.id" class="resume-detail__exp-item" @click.stop="startEditExp('education', edu)">
<!-- 展示模式 -->
<template v-if="expEditingId !== edu.id">
<div class="resume-detail__exp-row">
@@ -262,11 +259,18 @@
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('education', edu.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<!-- 编辑按钮 hover时显示 -->
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click="startEditExp('education', edu)" aria-label="编辑">
<el-icon><EditPen /></el-icon>
<span>编辑</span>
</button>
<!-- 删除按钮 -->
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click.stop="handleExpDelete('education', edu.id!)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon>
<span>删除</span>
</button>
</div>
</div>
<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>
@@ -293,7 +297,7 @@
</button>
</div>
<ResumeExpInlineEdit v-if="expAddingModule === 'work'" :module-type="'work'" @save="handleExpAdd('work', $event)" @cancel="expAddingModule = ''" />
<div v-for="exp in workList" :key="exp.id" class="resume-detail__exp-item">
<div v-for="exp in workList" :key="exp.id" class="resume-detail__exp-item" @click.stop="startEditExp('work', exp)">
<template v-if="expEditingId !== exp.id">
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ exp.companyName }}</span>
@@ -304,9 +308,14 @@
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('work', exp.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<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>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click.stop="handleExpDelete('work', exp.id!)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
</div>
<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">{{ p.text }}</li>
@@ -325,7 +334,7 @@
</button>
</div>
<ResumeExpInlineEdit v-if="expAddingModule === 'internship'" :module-type="'internship'" @save="handleExpAdd('internship', $event)" @cancel="expAddingModule = ''" />
<div v-for="intern in internshipList" :key="intern.id" class="resume-detail__exp-item">
<div v-for="intern in internshipList" :key="intern.id" class="resume-detail__exp-item" @click.stop="startEditExp('internship', intern)">
<template v-if="expEditingId !== intern.id">
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ intern.companyName }}</span>
@@ -336,9 +345,14 @@
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('internship', intern.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<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>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click.stop="handleExpDelete('internship', intern.id!)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
</div>
<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>
@@ -357,7 +371,7 @@
</button>
</div>
<ResumeExpInlineEdit v-if="expAddingModule === 'project'" :module-type="'project'" @save="handleExpAdd('project', $event)" @cancel="expAddingModule = ''" />
<div v-for="proj in projectList" :key="proj.id" class="resume-detail__exp-item">
<div v-for="proj in projectList" :key="proj.id" class="resume-detail__exp-item" @click.stop="startEditExp('project', proj)">
<template v-if="expEditingId !== proj.id">
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ proj.projectName }}</span>
@@ -368,9 +382,14 @@
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('project', proj.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<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>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click.stop="handleExpDelete('project', proj.id!)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
</div>
<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>
@@ -389,7 +408,7 @@
</button>
</div>
<ResumeExpInlineEdit v-if="expAddingModule === 'competition'" :module-type="'competition'" @save="handleExpAdd('competition', $event)" @cancel="expAddingModule = ''" />
<div v-for="comp in competitionList" :key="comp.id" class="resume-detail__exp-item">
<div v-for="comp in competitionList" :key="comp.id" class="resume-detail__exp-item" @click.stop="startEditExp('competition', comp)">
<template v-if="expEditingId !== comp.id">
<div class="resume-detail__exp-row">
<span class="resume-detail__exp-name">{{ comp.competitionName }}</span>
@@ -400,9 +419,14 @@
建议使用AI润色修复 &gt;&gt;
<span class="resume-detail__fix-tooltip">{{ getIssueByRecord('competition', comp.id!)!.finding }}</span>
</span>
<div class="mlauto dflex-end">
<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>
<button class="resume-detail__edit-btn-inline resume-detail__exp-edit-btn" @click.stop="handleExpDelete('competition', comp.id!)" aria-label="删除">
<el-icon><DeleteFilled /></el-icon><span>删除</span>
</button>
</div>
</div>
<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>
@@ -480,8 +504,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useStore } from 'vuex'
import SideNav from '@/components/SideNav.vue'
import ProfileEditDrawer from '@/components/ProfileEditDrawer.vue'
import ResumeExpInlineEdit from '@/components/ResumeExpInlineEdit.vue'
@@ -531,7 +556,11 @@ import {
type DiagnosisIssue,
type DescriptionParagraph,
} from '@/api/resume'
import {ArrowLeft, EditPen, Plus} from "@element-plus/icons-vue";
import {ArrowLeft, EditPen, Plus, DeleteFilled, ChatLineRound} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
// ==================== Store ====================
const store = useStore()
// ==================== ====================
@@ -748,6 +777,23 @@ const competitionList = ref<ResumeCompetition[]>([])
// ==================== ====================
/** 根据城市名称从地区树中反查城市编码(用于后端未返回 regionCode 时的 fallback */
function resolveRegionCodeByName(cityName: string): string {
if (!cityName) return ''
const regions = store.state.regions || []
for (const province of regions) {
for (const city of province.children) {
if (city.name === cityName) return city.code
if (city.children) {
for (const district of city.children) {
if (district.name === cityName) return district.code
}
}
}
}
return ''
}
/** 加载所有简历详情数据 */
async function loadResumeDetail() {
const [mainRes, eduRes, workRes, internRes, projRes, compRes] = await Promise.all([
@@ -761,6 +807,10 @@ async function loadResumeDetail() {
if (mainRes.code === '0' && mainRes.data) {
resumeMain.value = mainRes.data
// regionCode city
if (!resumeMain.value.regionCode && resumeMain.value.city) {
resumeMain.value.regionCode = resolveRegionCodeByName(resumeMain.value.city)
}
}
if (eduRes.code === '0' && eduRes.data) {
educationList.value = eduRes.data
@@ -779,12 +829,26 @@ async function loadResumeDetail() {
}
}
// ==================== ====================
/** 当 store 中地区树数据加载完毕后,如果 resumeMain 缺少 regionCode 则尝试反查 */
watch(
() => store.state.regions,
(regions) => {
if (regions?.length && resumeMain.value.city && !resumeMain.value.regionCode) {
resumeMain.value.regionCode = resolveRegionCodeByName(resumeMain.value.city)
}
}
)
// ==================== ====================
onMounted(() => {
loadResumeDetail()
//
loadDiagnosis()
// store
store.dispatch('loadCommonData')
})
// ==================== ====================
@@ -1286,6 +1350,32 @@ async function handleExpUpdate(moduleType: string, recordId: string, data: Recor
}
}
/** 删除单条经历 */
async function handleExpDelete(moduleType: string, recordId: string) {
try {
await ElMessageBox.confirm('确定要删除这条经历吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
const deleteMap: Record<string, (id: string) => Promise<any>> = {
education: deleteResumeEducation,
work: deleteResumeWork,
internship: deleteResumeInternship,
project: deleteResumeProject,
competition: deleteResumeCompetition,
}
const deleteFn = deleteMap[moduleType]
if (deleteFn) {
await deleteFn(recordId)
await loadResumeDetail()
ElMessage.success('删除成功')
}
} catch {
}
}
/** 保存新增的经历 */
async function handleExpAdd(moduleType: string, data: Record<string, any>) {
try {
@@ -1343,7 +1433,7 @@ function openEditDrawer(section: string) {
name: resumeMain.value.name || '',
email: resumeMain.value.email || '',
phone: resumeMain.value.mobileNumber || '',
location: '',
location: resumeMain.value.regionCode || '',
wechat: resumeMain.value.wechatNumber || '',
portfolioUrl: resumeMain.value.portfolioUrl || '',
}
@@ -1446,6 +1536,7 @@ async function handleSaveEdit(data: Record<string, any>) {
email: data.email,
mobileNumber: data.phone,
city: data.locationName || data.location || '',
regionCode: data.location || '',
wechatNumber: data.wechat,
portfolioUrl: data.portfolioUrl,
})
@@ -1453,6 +1544,7 @@ async function handleSaveEdit(data: Record<string, any>) {
resumeMain.value.email = data.email
resumeMain.value.mobileNumber = data.phone
resumeMain.value.city = data.locationName || data.location || ''
resumeMain.value.regionCode = data.location || ''
resumeMain.value.wechatNumber = data.wechat
resumeMain.value.portfolioUrl = data.portfolioUrl
} else if (editModule.value === 'summary') {