登陆页左侧新广告特效

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
+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',
})
+67 -27
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>
<!-- 作品集链接显示在个人信息卡片内 -->
<div 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 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>
<!-- 作品集链接显示在个人信息卡片内 -->
<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 class="profile-page-content__item-period">{{ exp.startDate }} - {{ exp.endDate || '至今' }}</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)">
<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 class="profile-page-content__item-period">{{ exp.startDate }} - {{ exp.endDate }}</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)">
<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 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>
</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)">
<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 class="profile-page-content__item-period">{{ comp.awardDate }}</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)">
<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>