ai助手投递偏好设置修复

This commit is contained in:
2026-07-10 16:29:31 +08:00
parent e8e2e09e62
commit 8cd21e7b98
11 changed files with 219 additions and 51 deletions
+4 -2
View File
@@ -23,7 +23,7 @@ export interface AgentConfig {
autoOptimizeResume?: number autoOptimizeResume?: number
/** 自动匹配分析 0=关闭 1=开启 */ /** 自动匹配分析 0=关闭 1=开启 */
autoMatchAnalysis?: number autoMatchAnalysis?: number
/** 是否愿意接受部门调剂 */ /** 是否开启深度人岗匹配分析,可选值:是/否(null或空字符串视为关闭) */
acceptDeptTransfer?: string acceptDeptTransfer?: string
/** 是否接受地点调剂 */ /** 是否接受地点调剂 */
acceptLocationTransfer?: string acceptLocationTransfer?: string
@@ -312,7 +312,9 @@ export function optimizeAgentResume(params: OptimizeResumeParams) {
/** 匹配度评分请求参数 */ /** 匹配度评分请求参数 */
export interface MatchScoreParams { export interface MatchScoreParams {
/** 定制简历 ID(字符串,避免大整数精度丢失) */ /** 定制简历 ID(字符串,避免大整数精度丢失) */
customizeResumeId: string customizeResumeId?: string
/** 普通简历 ID(关闭优化时使用) */
resumeId?: string
/** 岗位 ID(字符串,避免大整数精度丢失) */ /** 岗位 ID(字符串,避免大整数精度丢失) */
jobId: string jobId: string
} }
@@ -189,6 +189,11 @@
color: #F59E0B; color: #F59E0B;
} }
// 匹配度偏低(红橙色)
&--low-match {
color: #FE6D52;
}
// 强调色(品牌色) // 强调色(品牌色)
&--accent { &--accent {
color: $accent; color: $accent;
+16 -1
View File
@@ -131,10 +131,25 @@
&__ai-tip { &__ai-tip {
position: relative; position: relative;
background: $bg-white; background: $bg-white;
border: 1px solid $accent;
border-radius: 0.08rem; border-radius: 0.08rem;
padding: 0.08rem 0.12rem 0.07rem 0.12rem; padding: 0.08rem 0.12rem 0.07rem 0.12rem;
z-index: 1; z-index: 1;
// 渐变边框:从上到下 #9FD051 → #52CAD1(与导师页 stats 一致)
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: 0.08rem;
padding: 0.01rem;
background: linear-gradient(180deg, #9FD051 0%, #52CAD1 100%);
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
} }
// 提示条内容行 // 提示条内容行
+1 -1
View File
@@ -21,7 +21,7 @@
margin-bottom: 0.16rem; margin-bottom: 0.16rem;
background: #fff; background: #fff;
>h2{ >h2{
font-size: 0.24rem; font-size: 0.18rem;
font-weight: 700; font-weight: 700;
color: $text-dark; color: $text-dark;
} }
+12 -1
View File
@@ -23,7 +23,7 @@
} }
&__title { &__title {
font-size: 0.24rem; font-size: 0.18rem;
font-weight: 700; font-weight: 700;
color: $text-dark; color: $text-dark;
margin: 0; margin: 0;
@@ -138,6 +138,13 @@
opacity: 0; opacity: 0;
transition: opacity 0.15s; transition: opacity 0.15s;
} }
&--ellipsis {
max-width: 3.6rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
} }
&__row:hover &__td--action { &__row:hover &__td--action {
@@ -166,6 +173,10 @@
&__name { &__name {
font-weight: 500; font-weight: 500;
max-width: 3.6rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
&__default-tag { &__default-tag {
+2 -2
View File
@@ -187,7 +187,7 @@ const hintText = computed(() => {
return '正在根据岗位信息优化简历...' return '正在根据岗位信息优化简历...'
} }
if (props.currentStep === 2) { if (props.currentStep === 2) {
if (props.stepPhase === 'low-match') return '当前匹配度偏低,是否仍要继续投递?' if (props.stepPhase === 'low-match') return `当前匹配度偏低${props.matchScore ?? 0},是否仍要继续投递?`
return '正在根据最终投递简历复核岗位匹配度...' return '正在根据最终投递简历复核岗位匹配度...'
} }
if (props.currentStep === 3) { if (props.currentStep === 3) {
@@ -203,7 +203,7 @@ const hintText = computed(() => {
const hintClass = computed(() => { const hintClass = computed(() => {
if (isCompleted.value) return '' if (isCompleted.value) return ''
if (props.currentStep === 1 && props.stepPhase === 'done') return 'agent-apply-progress__hint--warning' if (props.currentStep === 1 && props.stepPhase === 'done') return 'agent-apply-progress__hint--warning'
if (props.currentStep === 2 && props.stepPhase === 'low-match') return 'agent-apply-progress__hint--warning' if (props.currentStep === 2 && props.stepPhase === 'low-match') return 'agent-apply-progress__hint--low-match'
if (props.currentStep === 3) return 'agent-apply-progress__hint--accent' if (props.currentStep === 3) return 'agent-apply-progress__hint--accent'
if (props.currentStep === 4) return 'agent-apply-progress__hint--accent' if (props.currentStep === 4) return 'agent-apply-progress__hint--accent'
return '' return ''
+4 -3
View File
@@ -621,7 +621,7 @@ function handleSelectResume(r: ResumeListItem) {
// ==================== 投递偏好 ==================== // ==================== 投递偏好 ====================
const autoOptimizeSwitch = ref(false) const autoOptimizeSwitch = ref(false)
const autoMatchAnalysisSwitch = ref(true) const autoMatchAnalysisSwitch = ref(false)
/** 切换简历优化开关 */ /** 切换简历优化开关 */
function toggleOptimize() { function toggleOptimize() {
@@ -641,7 +641,8 @@ async function loadAgentConfigData() {
const res = await fetchAgentConfig() const res = await fetchAgentConfig()
if (res.code === '0' && res.data) { if (res.code === '0' && res.data) {
if (res.data.autoOptimizeResume !== undefined) autoOptimizeSwitch.value = res.data.autoOptimizeResume === 1 if (res.data.autoOptimizeResume !== undefined) autoOptimizeSwitch.value = res.data.autoOptimizeResume === 1
if (res.data.autoMatchAnalysis !== undefined) autoMatchAnalysisSwitch.value = res.data.autoMatchAnalysis === 1 // acceptDeptTransfer: "是"=开启,null/空字符串/"否"=关闭
autoMatchAnalysisSwitch.value = res.data.acceptDeptTransfer === '是'
} }
} catch { /* 静默 */ } } catch { /* 静默 */ }
} }
@@ -651,7 +652,7 @@ async function handleSavePreference() {
try { try {
await saveAgentConfig({ await saveAgentConfig({
autoOptimizeResume: autoOptimizeSwitch.value ? 1 : 0, autoOptimizeResume: autoOptimizeSwitch.value ? 1 : 0,
autoMatchAnalysis: autoMatchAnalysisSwitch.value ? 1 : 0, acceptDeptTransfer: autoMatchAnalysisSwitch.value ? '是' : '否',
}) })
} catch { /* 静默 */ } } catch { /* 静默 */ }
} }
+169 -38
View File
@@ -814,6 +814,14 @@ async function handleStartApply() {
if (isApplying.value) return if (isApplying.value) return
isApplying.value = true isApplying.value = true
// 刷新最新配置(确保优化开关状态准确)
try {
const configRes = await fetchAgentConfig()
if (configRes.code === '0' && configRes.data) {
agentConfig.value = configRes.data
}
} catch { /* 静默 */ }
await loadApplyList() await loadApplyList()
if (applyJobList.value.length === 0) { if (applyJobList.value.length === 0) {
@@ -825,22 +833,60 @@ async function handleStartApply() {
// 取第一个岗位 // 取第一个岗位
const targetJob = applyJobList.value[0] const targetJob = applyJobList.value[0]
// 创建进度条目 // 判断是否开启简历针对性优化(从配置中读取)
const progressItem: ApplyProgressItem = { const isOptimizeEnabled = agentConfig.value?.autoOptimizeResume === 1
id: Date.now(), // 判断是否开启深度人岗匹配分析
jobInfo: targetJob, const isMatchEnabled = agentConfig.value?.acceptDeptTransfer === '是'
step: 1,
stepPhase: 'loading', if (isOptimizeEnabled) {
resumeName: '岗位专属简历', // 开启优化:从第1步开始
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 1,
stepPhase: 'loading',
resumeName: '岗位专属简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
// 开始第1步:优化简历
await startResumeOptimization(targetJob)
} else if (isMatchEnabled) {
// 关闭优化但开启匹配分析:跳过第1步,直接进入第2步
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 2,
stepPhase: 'loading',
resumeName: '默认简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
// 直接进入第2步:匹配分析(使用默认简历ID)
await startMatchAnalysisWithResumeId()
} else {
// 优化和匹配分析都关闭:跳过第1、2步,直接进入第3步
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 3,
stepPhase: 'ready',
resumeName: '默认简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
} }
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
// 开始第1步:优化简历
await startResumeOptimization(targetJob)
} }
/** 执行简历优化(第1步) */ /** 执行简历优化(第1步) */
@@ -916,22 +962,33 @@ async function handleViewResumeStep() {
} }
} }
/** 第1步完成:确认简历 → 自动进入第2步匹配分析 */ /** 第1步完成:确认简历 → 自动进入第2步匹配分析(或跳过) */
async function handleConfirmResumeStep() { async function handleConfirmResumeStep() {
const progressItem = applyProgressList.value[0] const progressItem = applyProgressList.value[0]
if (!progressItem) return if (!progressItem) return
// 进入第2步
progressItem.step = 2
progressItem.stepPhase = 'loading'
applyProgressList.value = [...applyProgressList.value]
saveProgressToStorage()
// 关闭简历预览抽屉 // 关闭简历预览抽屉
showResumeDrawer.value = false showResumeDrawer.value = false
// 调用匹配分析流程 // 判断是否开启深度人岗匹配分析
await startMatchAnalysis() const isMatchEnabled = agentConfig.value?.acceptDeptTransfer === '是'
if (isMatchEnabled) {
// 进入第2步
progressItem.step = 2
progressItem.stepPhase = 'loading'
applyProgressList.value = [...applyProgressList.value]
saveProgressToStorage()
// 调用匹配分析流程
await startMatchAnalysis()
} else {
// 匹配分析关闭:跳过第2步直接进入第3步
progressItem.step = 3
progressItem.stepPhase = 'ready'
applyProgressList.value = [...applyProgressList.value]
saveProgressToStorage()
}
} }
/** 执行匹配分析(第2步) */ /** 执行匹配分析(第2步) */
@@ -984,6 +1041,42 @@ async function startMatchAnalysis() {
} }
} }
/** 执行匹配分析(第2步) — 关闭优化时使用默认简历ID */
async function startMatchAnalysisWithResumeId() {
const progressItem = applyProgressList.value[0]
if (!progressItem) return
try {
// 使用默认简历ID调用匹配分析接口
const scoreRes = await fetchMatchScore({
resumeId: defaultResumeId.value,
jobId: String(progressItem.jobInfo.id),
})
if (applyCancelled.value) return
const score = scoreRes?.data ?? 0
progressItem.matchScore = score
if (score >= 40) {
// 匹配度 >= 40,直接进入第3步
progressItem.step = 3
progressItem.stepPhase = 'ready'
} else {
// 匹配度 < 40,显示低匹配提示
progressItem.step = 2
progressItem.stepPhase = 'low-match'
}
// 触发响应式更新
applyProgressList.value = [...applyProgressList.value]
saveProgressToStorage()
} catch (e) {
if (applyCancelled.value) return
console.error('[Agent] 匹配分析失败(默认简历)', e)
ElMessage.error('匹配分析失败,请重试')
}
}
/** 第2步匹配度低:不投递 — 取消该岗位 */ /** 第2步匹配度低:不投递 — 取消该岗位 */
async function handleNotApplyStep() { async function handleNotApplyStep() {
const progressItem = applyProgressList.value[0] const progressItem = applyProgressList.value[0]
@@ -1095,21 +1188,59 @@ async function autoStartNextApply() {
// 取下一个岗位 // 取下一个岗位
const targetJob = applyJobList.value[0] const targetJob = applyJobList.value[0]
const progressItem: ApplyProgressItem = {
id: Date.now(), // 判断是否开启简历针对性优化
jobInfo: targetJob, const isOptimizeEnabled = agentConfig.value?.autoOptimizeResume === 1
step: 1, // 判断是否开启深度人岗匹配分析
stepPhase: 'loading', const isMatchEnabled = agentConfig.value?.acceptDeptTransfer === '是'
resumeName: '岗位专属简历',
if (isOptimizeEnabled) {
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 1,
stepPhase: 'loading',
resumeName: '岗位专属简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
// 开始第1步:优化简历
await startResumeOptimization(targetJob)
} else if (isMatchEnabled) {
// 关闭优化但开启匹配分析:跳过第1步,直接进入第2步
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 2,
stepPhase: 'loading',
resumeName: '默认简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
await startMatchAnalysisWithResumeId()
} else {
// 优化和匹配分析都关闭:跳过第1、2步,直接进入第3步
const progressItem: ApplyProgressItem = {
id: Date.now(),
jobInfo: targetJob,
step: 3,
stepPhase: 'ready',
resumeName: '默认简历',
}
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
} }
applyProgressList.value = [progressItem]
saveProgressToStorage()
await nextTick()
scrollContentToBottom()
// 开始第1步:优化简历
await startResumeOptimization(targetJob)
} }
</script> </script>
+4 -1
View File
@@ -3,7 +3,7 @@
<SideNav /> <SideNav />
<div class="job-detail__content"> <div class="job-detail__content">
<!-- 页面标题 + Tab 切换 --> <!-- 页面标题 + Tab 切换 -->
<JobPageHeader :activeTab="''" /> <JobPageHeader :activeTab="cachedTab" />
<!-- 主内容卡片 --> <!-- 主内容卡片 -->
<div class="job-detail__main-card"> <div class="job-detail__main-card">
<!-- 返回按钮 --> <!-- 返回按钮 -->
@@ -218,6 +218,9 @@ const fullLoadingText = ref('')
/** 当前岗位 ID,从路由参数中获取 */ /** 当前岗位 ID,从路由参数中获取 */
const jobId = route.params.id as string const jobId = route.params.id as string
/** 从 store 缓存中读取 Jobs 页面上次切换的 Tab,保持切换记录 */
const cachedTab = computed(() => store.state.jobListCache?.tab || 'recommend')
// ==================== 工具函数 ==================== // ==================== 工具函数 ====================
/** 工作类型映射:使用全局统一的 formatEmploymentType */ /** 工作类型映射:使用全局统一的 formatEmploymentType */
+1 -1
View File
@@ -110,7 +110,7 @@
<!-- 提示条主体 --> <!-- 提示条主体 -->
<div class="jobs-page__ai-tip"> <div class="jobs-page__ai-tip">
<!-- 尖角::before(边框色)::after(白色)双伪元素实现通过动态left定位 --> <!-- 尖角::before(边框色)::after(白色)双伪元素实现通过动态left定位 -->
<span class="jobs-page__ai-tip-arrow" :style="{ left: aiTipArrowLeft + 'px' }"></span> <span v-if="false" class="jobs-page__ai-tip-arrow" :style="{ left: aiTipArrowLeft + 'px' }"></span>
<div class="jobs-page__ai-tip-content"> <div class="jobs-page__ai-tip-content">
<img class="jobs-page__ai-tip-logo" src="@/assets/images/logo-offerpai-2.png" alt="Nova" /> <img class="jobs-page__ai-tip-logo" src="@/assets/images/logo-offerpai-2.png" alt="Nova" />
<span class="jobs-page__ai-tip-text"> <span class="jobs-page__ai-tip-text">
+1 -1
View File
@@ -50,7 +50,7 @@
<span v-if="item.isDefault" class="resume-page__default-tag">默认</span> <span v-if="item.isDefault" class="resume-page__default-tag">默认</span>
</div> </div>
</td> </td>
<td class="resume-page__td">{{ item.targetJob }}</td> <td class="resume-page__td resume-page__td--ellipsis">{{ item.targetJob }}</td>
<td class="resume-page__td">{{ item.updatedAt }}</td> <td class="resume-page__td">{{ item.updatedAt }}</td>
<!-- 操作按钮列 --> <!-- 操作按钮列 -->
<td class="resume-page__td resume-page__td--action"> <td class="resume-page__td resume-page__td--action">