新会员页面

This commit is contained in:
2026-05-15 19:02:11 +08:00
parent f341408254
commit 14c7660770
31 changed files with 3496 additions and 396 deletions
+579 -79
View File
@@ -28,26 +28,37 @@
</svg>
</div>
<!-- 状态标签 -->
<span class="agent-main__status-tag">准备就绪</span>
<span v-if="applyJobList.length > 0" class="agent-main__status-tag">准备就绪</span>
<!-- 提示文字 -->
<span class="agent-main__status-text">已添加一个岗位随时可以开始投递</span>
<!-- 开始按钮 -->
<button class="agent-main__start-btn">开始 </button>
<span class="agent-main__status-text">{{ applyJobList.length > 0 ? `已添加${applyJobList.length}个岗位,随时可以开始投递` : '请添加岗位' }}</span>
<!-- 开始按钮投递中时置灰不可点击暂停时显示继续可点击 -->
<button
class="agent-main__start-btn"
:disabled="isApplying && !isPaused"
@click="handleTopBarStartClick"
>{{ isApplying ? (isPaused ? '继续' : '投递中...') : '开始 ▸' }}</button>
<!-- 右侧工具按钮 -->
<div class="agent-main__tools">
<!-- 设置按钮 -->
<button class="agent-main__tool-btn" title="设置">
<!-- 待投递列表按钮 -->
<button class="agent-main__tool-btn" title="待投递列表" @click.stop="toggleTaskListDropdown">
<svg viewBox="0 0 24 24" fill="none">
<path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
</button>
<!-- 待投递列表下拉弹窗 -->
<AgentTaskListDropdown
v-if="showTaskListDropdown"
:pending-list="applyJobList"
@removed="handleTaskRemoved"
@view-all="showTaskListDropdown = false"
/>
<!-- 齿轮按钮 -->
<button class="agent-main__tool-btn" title="配置">
<svg viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.5" />
<path d="M12 1v2m0 18v2m-9-11h2m18 0h2m-3.3-7.7-1.4 1.4M4.7 19.3l1.4-1.4m0-11.8L4.7 4.7m14.6 14.6-1.4-1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
</button>
<!-- <button class="agent-main__tool-btn" title="配置">-->
<!-- <svg viewBox="0 0 24 24" fill="none">-->
<!-- <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="1.5" />-->
<!-- <path d="M12 1v2m0 18v2m-9-11h2m18 0h2m-3.3-7.7-1.4 1.4M4.7 19.3l1.4-1.4m0-11.8L4.7 4.7m14.6 14.6-1.4-1.4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />-->
<!-- </svg>-->
<!-- </button>-->
</div>
</div>
@@ -69,16 +80,26 @@
<div v-else-if="msg.type === 'recommend'" class="agent-main__chat-row">
<!-- 如果 extra 里有岗位数据用岗位列表组件展示 -->
<AgentChatJobList
v-if="getRecommendJobsFromExtra(msg.extra).length > 0"
:summary="getRecommendSummaryFromExtra(msg.extra)"
:jobs="getRecommendJobsFromExtra(msg.extra)"
@view-all="handleOpenRecommendPanel(msg.id)"
/>
</div>
<!-- 投递进度消息 -->
<div v-else-if="msg.type === 'apply_progress'" class="agent-main__chat-row">
<div class="agent-main__chat-bubble">{{ msg.content }}</div>
<!-- 投递进度消息 仅显示最新 recommend 之后的 apply_progress -->
<div v-else-if="msg.type === 'apply_progress' && isVisibleApplyProgress(msg.id)" class="agent-main__chat-row">
<AgentApplyProgress
:job-info="getApplyProgressJobInfo(msg.extra)"
:current-step="getApplyProgressStep(msg.extra)"
:resume-name="getApplyProgressResumeName(msg.extra)"
:paused="isPaused"
@cancel="handleCancelApply(msg)"
@confirm-resume="handleConfirmResumeStep(msg)"
@skip="handleSkipApply(msg)"
@applied="handleAppliedStep(msg)"
@go-apply="handleGoApplyStep(msg)"
@toggle-pause="handleTogglePause"
/>
</div>
</template>
@@ -105,9 +126,11 @@
</div>
</div>
<!-- 右侧匹配岗位添加面板 -->
<!-- 右侧匹配岗位添加面板 / 简历生成进度 / 简历预览 -->
<div v-if="showRightPanel" class="agent-main__right">
<!-- 模式1匹配岗位添加面板 -->
<AgentMatchJobAdd
v-if="rightPanelMode === 'recommend'"
:jobs="activeRecommendJobs"
:loading-job-ids="loadingJobIds"
:panel-loading="panelLoading"
@@ -116,6 +139,21 @@
@toggle="handleToggleJobApply"
@add-all="handleAddAllJobs"
/>
<!-- 模式2简历生成进度 -->
<div v-else-if="rightPanelMode === 'generating'" class="agent-resume-generating">
<div class="agent-resume-generating__progress-bar">
<div class="agent-resume-generating__progress-fill" :style="{ width: generateProgress + '%' }"></div>
</div>
<div class="agent-resume-generating__title">正在生成岗位专属简历</div>
<div class="agent-resume-generating__hint">
<svg viewBox="0 0 16 16" fill="none" class="agent-resume-generating__hint-icon"><circle cx="8" cy="8" r="7" stroke="currentColor" stroke-width="1.2"/><path d="M8 5v3M8 10.5v.5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
<span>生成简历大约需要10-20</span>
</div>
</div>
<!-- 模式3简历预览 -->
<div v-else-if="rightPanelMode === 'resume'" class="agent-main__right-resume">
<JobResumeTemplate :resume-data="applyResumeData" />
</div>
</div>
</div>
</div>
@@ -128,12 +166,16 @@ import SideNav from '@/components/SideNav.vue'
import AgentSetupWizard from '@/components/AgentSetupWizard.vue'
import AgentChatJobList from '@/components/AgentChatJobList.vue'
import AgentMatchJobAdd from '@/components/AgentMatchJobAdd.vue'
import { fetchAgentConfig, saveAgentConfig, fetchAgentRecommend, fetchAgentChatMessages, addAgentChatMessage, applyJob, cancelApplyJob, fetchApplyByJobIds, sendAgentChat } from '@/api/agent'
import AgentApplyProgress from '@/components/AgentApplyProgress.vue'
import AgentTaskListDropdown from '@/components/AgentTaskListDropdown.vue'
import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
import { fetchAgentConfig, saveAgentConfig, fetchAgentRecommend, fetchAgentChatMessages, addAgentChatMessage, applyJob, cancelApplyJob, fetchApplyByJobIds, sendAgentChat, optimizeAgentResume } from '@/api/agent'
import type { AgentConfig, AgentRecommendJob, AgentChatMessage, AgentChatHistoryItem } from '@/api/agent'
import { fetchApplyList } from '@/api/jobs'
import { fetchAgentTaskList } from '@/api/jobs'
import type { JobListItem } from '@/api/jobs'
import { fetchResumeList } from '@/api/resume'
import store from '@/stores'
import { getIntentionCategoryNames, getIntentionRegionNames, getIntentionIndustryNames } from '@/utils/intention'
import AiThinkingIndicator from '@/components/tools/AiThinkingIndicator.vue'
/** 页面初始加载状态 */
@@ -165,9 +207,12 @@ const activeRecommendJobs = computed(() => {
/** 聊天输入框内容 */
const chatInput = ref('')
/** 投递列表数据 */
/** 投递列表数据 */
const applyJobList = ref<JobListItem[]>([])
/** 待投递列表下拉弹窗是否显示 */
const showTaskListDropdown = ref(false)
/** 正在请求中的岗位 ID 列表(传给子组件控制按钮 loading) */
const loadingJobIds = ref<number[]>([])
@@ -178,7 +223,15 @@ const panelLoading = ref(false)
const defaultResumeId = ref('')
/** 是否正在发送消息(防止重复发送 + 显示等待状态) */
const isSending = ref(false)/** 聊天区域 DOM 引用 */
const isSending = ref(false)
/** 是否正在投递流程中(防止重复点击开始按钮) */
const isApplying = ref(false)
/** 是否暂停投递 */
const isPaused = ref(false)
/** 聊天区域 DOM 引用 */
const chatAreaRef = ref<HTMLElement | null>(null)
/** 滚动聊天区域到底部(平滑滚动) */
@@ -191,8 +244,15 @@ function scrollChatToBottom() {
/** 页面挂载时查询配置 */
onMounted(async () => {
await loadAgentConfig()
// 点击页面其他区域关闭待投递列表下拉
document.addEventListener('click', closeTaskDropdownOnClickOutside)
})
/** 点击外部关闭待投递列表下拉 */
function closeTaskDropdownOnClickOutside() {
showTaskListDropdown.value = false
}
/** 查询求职助手配置 */
async function loadAgentConfig() {
pageLoading.value = true
@@ -227,6 +287,20 @@ async function loadRecommendJobs() {
if (res.code === '0' && res.data && res.data.list?.length > 0) {
const summary = res.data.summary || ''
const jobList = res.data.list
// if(jobList.length==0){
// const aiNow = Math.floor(Date.now() / 1000)
// chatMessages.value.push({
// id: Date.now() + 1,
// type: 'assistant',
// content: '暂无相关职位推荐',
// extra: '',
// createTime: { seconds: aiNow, nanos: 0 },
// })
// try {
// await addAgentChatMessage({ type: 'assistant', content: '暂无相关职位推荐' })
// } catch { /* 静默 */ }
// }
/* 将完整岗位数据序列化为 extra JSON */
const extraJson = JSON.stringify({ summary, list: jobList })
@@ -275,15 +349,31 @@ async function loadChatMessages() {
}
}
/** 加载岗位投递列表 */
/** 加载求职助手任务列表(tab=1 进行中/待投递) */
async function loadApplyList() {
try {
const res = await fetchApplyList({ pageNum: 1, pageSize: 100,status:0 })
const res = await fetchAgentTaskList({ pageNum: 1, pageSize: 100, tab: 1 })
if (res.code === '0' && res.data) {
applyJobList.value = res.data.list || []
}
} catch {
console.error('[Agent] 加载投递列表失败')
console.error('[Agent] 加载求职助手任务列表失败')
}
}
/** 切换待投递列表下拉弹窗显隐 */
function toggleTaskListDropdown() {
showTaskListDropdown.value = !showTaskListDropdown.value
if (showTaskListDropdown.value) {
loadApplyList()
}
}
/** 岗位从待投递列表中移除后的回调 */
function handleTaskRemoved(jobId: string) {
const idx = applyJobList.value.findIndex(j => j.id === jobId)
if (idx !== -1) {
applyJobList.value.splice(idx, 1)
}
}
@@ -320,58 +410,6 @@ function buildChatHistory(): AgentChatHistoryItem[] {
})
}
/**
* 从 store 中获取意向岗位类型名称列表
* 根据 jobIntention.categoryIds 在 jobCategories 树中查找名称
*/
function getIntentionCategoryNames(): string[] {
const ids = store.state.jobIntention.categoryIds || []
if (ids.length === 0) return []
const names: string[] = []
for (const group of store.state.jobCategories) {
for (const child of group.children || []) {
for (const leaf of child.children || []) {
if (ids.includes(Number(leaf.id))) names.push(leaf.name)
}
}
}
return names
}
/**
* 从 store 中获取意向城市名称列表
* 根据 jobIntention.regionCodes 在 regions 树中查找名称
*/
function getIntentionRegionNames(): string[] {
const codes = store.state.jobIntention.regionCodes || []
if (codes.length === 0) return []
const names: string[] = []
for (const province of store.state.regions) {
if (codes.includes(province.code)) { names.push(province.name); continue }
for (const city of province.children || []) {
if (codes.includes(city.code)) names.push(city.name)
}
}
return names
}
/**
* 从 store 中获取意向行业名称列表
* 根据 jobIntention.industryIds 在 industries 树中查找名称
*/
function getIntentionIndustryNames(): string[] {
const ids = store.state.jobIntention.industryIds || []
if (ids.length === 0) return []
const names: string[] = []
for (const group of store.state.industries) {
if (ids.includes(Number(group.id))) { names.push(group.name); continue }
for (const child of group.children || []) {
if (ids.includes(Number(child.id))) names.push(child.name)
}
}
return names
}
/**
* 从消息的 extra JSON 中解析推荐岗位列表
* extra 格式预期为 JSON 字符串,包含 list 字段
@@ -478,6 +516,7 @@ async function handleToggleJobApply(job: AgentRecommendJob) {
}
/* 更新对话消息中的岗位状态 */
updateJobStatusInMessages(job.id, isRemoving ? null : -1)
await loadApplyList()
ElMessage.success(isRemoving ? '岗位已从待投递移除' : '岗位已添加到待投递')
} catch {
ElMessage.error('操作失败,请重试')
@@ -507,6 +546,7 @@ async function handleAddAllJobs() {
loadingJobIds.value = loadingJobIds.value.filter(id => id !== job.id)
}
}
await loadApplyList()
ElMessage.success('已全部添加到待投递')
}
@@ -568,6 +608,451 @@ async function handleOpenRecommendPanel(msgId: number) {
function handleCloseRecommendPanel() {
showRightPanel.value = false
activeRecommendMsgId.value = null
rightPanelMode.value = 'recommend'
}
// ==================== 投递进度流程 ====================
/** 右侧面板显示模式:recommend-岗位推荐 / generating-简历生成中 / resume-简历预览 */
const rightPanelMode = ref<'recommend' | 'generating' | 'resume'>('recommend')
/** 简历生成进度百分比(0-100) */
const generateProgress = ref(0)
/** 当前投递流程的简历数据 */
const applyResumeData = ref<ResumeTemplateData>({} as ResumeTemplateData)
/** 当前投递流程的简历名称 */
const applyResumeName = ref('')
/** 进度条定时器 */
let progressTimer: ReturnType<typeof setInterval> | null = null
/** 启动模拟进度条(慢慢增长到 90%,接口返回后快速涨满) */
function startProgressSimulation() {
generateProgress.value = 0
let elapsed = 0
progressTimer = setInterval(() => {
elapsed += 300
// 前 5 秒涨到 30%5-15 秒涨到 70%15-25 秒涨到 90%
if (generateProgress.value < 30) {
generateProgress.value += 2
} else if (generateProgress.value < 70) {
generateProgress.value += 1
} else if (generateProgress.value < 90) {
generateProgress.value += 0.5
}
// 最多到 90%
if (generateProgress.value >= 90) {
generateProgress.value = 90
}
}, 300)
}
/** 停止进度条并涨满 */
function finishProgress() {
if (progressTimer) {
clearInterval(progressTimer)
progressTimer = null
}
generateProgress.value = 100
}
/** 学历文字转数字(接口返回中文,模板需要数字) */
function degreeToNumber(degree?: string): number {
const map: Record<string, number> = { '大专': 1, '本科': 2, '硕士': 3, '博士': 4 }
return map[degree || ''] || 2
}
/** 将优化简历接口返回数据映射为 ResumeTemplateData 格式 */
function mapOptimizeResumeToTemplate(data: any): ResumeTemplateData {
const r = data.resume || {}
return {
name: r.name || '未填写姓名',
email: r.email || '',
mobileNumber: r.mobileNumber || '',
wechatNumber: r.wechatNumber || '',
summary: r.summary || '',
educations: (data.education || []).map((e: any) => ({
school: e.school || '',
major: e.major || '',
degree: degreeToNumber(e.degree),
startDate: e.startDate || '',
endDate: e.endDate || '',
description: (e.description || []).map((d: any) => ({ id: d.id, text: d.text || '' })),
})),
workExperiences: (data.work || []).map((w: any) => ({
companyName: w.companyName || '',
position: w.position || '',
startDate: w.startDate || '',
endDate: w.endDate || '',
description: (w.description || []).map((d: any) => ({ id: d.id, text: d.text || '' })),
})),
internships: (data.internship || []).map((i: any) => ({
companyName: i.companyName || '',
position: i.position || '',
startDate: i.startDate || '',
endDate: i.endDate || '',
description: (i.description || []).map((d: any) => ({ id: d.id, text: d.text || '' })),
})),
projects: (data.project || []).map((p: any) => ({
projectName: p.projectName || '',
companyName: p.companyName || '',
role: p.role || '',
startDate: p.startDate || '',
endDate: p.endDate || '',
description: (p.description || []).map((d: any) => ({ id: d.id, text: d.text || '' })),
})),
competitions: (data.competition || []).map((c: any) => ({
competitionName: c.competitionName || '',
award: c.award || '',
awardDate: c.awardDate || '',
description: (c.description || []).map((d: any) => ({ id: d.id, text: d.text || '' })),
})),
skills: r.skills || [],
certificates: r.certificates || [],
}
}
/**
* 点击"开始"按钮 — 启动投递流程
* 1. 刷新待投递列表
* 2. 取第一个岗位
* 3. 创建 apply_progress 消息
* 4. 开始第1步:优化简历
*/
async function handleStartApply() {
// 防抖:投递中不可重复点击
if (isApplying.value) return
isApplying.value = true
// 刷新待投递列表
await loadApplyList()
if (applyJobList.value.length === 0) {
ElMessage.warning('暂无待投递岗位,请先添加岗位')
isApplying.value = false
return
}
// 取第一个岗位
const targetJob = applyJobList.value[0]
// 构建 extra 数据
const extraData = {
jobInfo: targetJob,
resumeInfo: null as any,
step: 1,
}
// 创建 apply_progress 消息并追加到对话列表
const now = Math.floor(Date.now() / 1000)
const applyMsg: AgentChatMessage = {
id: Date.now(),
type: 'apply_progress',
content: '',
extra: JSON.stringify(extraData),
createTime: { seconds: now, nanos: 0 },
}
chatMessages.value.push(applyMsg)
await nextTick()
scrollChatToBottom()
// ========== 第1步:优化简历 ==========
// 打开右侧面板,显示生成进度
rightPanelMode.value = 'generating'
showRightPanel.value = true
startProgressSimulation()
try {
const res = await optimizeAgentResume({
resumeId: defaultResumeId.value,
jobId: targetJob.id,
})
// 接口返回后涨满进度条
finishProgress()
// 等待 1 秒让用户看到 100%
await new Promise(resolve => setTimeout(resolve, 1000))
// 解析简历数据 — 映射接口返回格式到 ResumeTemplateData
const apiData = res?.data || res
if (apiData) {
const resumeResult = mapOptimizeResumeToTemplate(apiData)
// 保存简历数据
applyResumeData.value = resumeResult
applyResumeName.value = resumeResult.name || '岗位专属简历'
// 更新 extra:存入 resumeInfostep 进到 2
extraData.resumeInfo = apiData
extraData.step = 2
applyMsg.extra = JSON.stringify(extraData)
// 切换右侧面板为简历预览
rightPanelMode.value = 'resume'
}
} catch (e) {
console.error('[Agent] 优化简历失败', e)
ElMessage.error('简历优化失败,请重试')
finishProgress()
showRightPanel.value = false
rightPanelMode.value = 'recommend'
isApplying.value = false
}
}
// ==================== apply_progress 消息辅助方法 ====================
/** 从 extra 中解析岗位信息 */
function getApplyProgressJobInfo(extra: string): JobListItem | null {
try {
const parsed = JSON.parse(extra)
return parsed.jobInfo || null
} catch {
return null
}
}
/** 从 extra 中解析当前步骤 */
function getApplyProgressStep(extra: string): number {
try {
const parsed = JSON.parse(extra)
return parsed.step || 1
} catch {
return 1
}
}
/** 从 extra 中解析简历名称 */
function getApplyProgressResumeName(extra: string): string {
try {
const parsed = JSON.parse(extra)
return parsed.resumeInfo?.name || '岗位专属简历'
} catch {
return '岗位专属简历'
}
}
/**
* apply_progress 显示模式
* 'afterRecommend' — 显示最新 recommend 之后的所有 apply_progress
* 'latestOnly' — 只显示最新一条 apply_progress
* 切换此变量即可手动切换显示效果
*/
const applyProgressDisplayMode = ref<'afterRecommend' | 'latestOnly'>('latestOnly')
/**
* 判断 apply_progress 消息是否应该显示
*/
function isVisibleApplyProgress(msgId: number): boolean {
if (applyProgressDisplayMode.value === 'latestOnly') {
// 只显示最新一条 apply_progress
for (let i = chatMessages.value.length - 1; i >= 0; i--) {
if (chatMessages.value[i].type === 'apply_progress') {
return chatMessages.value[i].id === msgId
}
}
return false
}
// afterRecommend 模式:显示最新 recommend 之后的所有 apply_progress
let lastRecommendIdx = -1
for (let i = chatMessages.value.length - 1; i >= 0; i--) {
if (chatMessages.value[i].type === 'recommend') {
lastRecommendIdx = i
break
}
}
const msgIdx = chatMessages.value.findIndex(m => m.id === msgId)
if (lastRecommendIdx === -1) return true
return msgIdx > lastRecommendIdx
}
// ==================== apply_progress 步骤事件处理 ====================
/** 顶部栏按钮点击 — 未投递时开始,暂停时恢复 */
function handleTopBarStartClick() {
if (isApplying.value && isPaused.value) {
// 恢复投递
isPaused.value = false
} else if (!isApplying.value) {
// 开始新的投递
handleStartApply()
}
}
/** 暂停/恢复投递(由 AgentApplyProgress 组件触发) */
function handleTogglePause() {
isPaused.value = !isPaused.value
}
/** 取消投递流程 */
function handleCancelApply(msg: AgentChatMessage) {
// 从消息列表中移除该条 apply_progress
const idx = chatMessages.value.findIndex(m => m.id === msg.id)
if (idx !== -1) {
chatMessages.value.splice(idx, 1)
}
// 关闭右侧面板
showRightPanel.value = false
rightPanelMode.value = 'recommend'
// 投递流程结束
isApplying.value = false
}
/** 第2步:确认简历 */
function handleConfirmResumeStep(msg: AgentChatMessage) {
try {
const extraData = JSON.parse(msg.extra)
// 勾选第2步,进入第3步
extraData.step = 3
msg.extra = JSON.stringify(extraData)
} catch { /* 忽略 */ }
}
/** 第3步:跳过投递 */
function handleSkipApply(msg: AgentChatMessage) {
try {
const extraData = JSON.parse(msg.extra)
// 勾选第3步,进入第4步(自动完成)
extraData.step = 4
msg.extra = JSON.stringify(extraData)
// 持久化保存
persistApplyProgressMessage(msg)
// 自动开始下一个岗位投递(不关闭右侧面板,避免闪烁)
autoStartNextApply()
} catch { /* 忽略 */ }
}
/** 第3步:我已投递 — 调用 /job/apply 接口 */
async function handleAppliedStep(msg: AgentChatMessage) {
try {
const extraData = JSON.parse(msg.extra)
const jobInfo = extraData.jobInfo
if (!jobInfo) return
// 调用投递接口,status=0 表示已投递,jobId 传字符串避免大整数精度丢失
const res = await applyJob({ jobId: String(jobInfo.id), status: 0 })
if (res.code === '0') {
// 勾选第3步,进入第4步
extraData.step = 4
msg.extra = JSON.stringify(extraData)
ElMessage.success('投递成功')
// 持久化保存
persistApplyProgressMessage(msg)
// 自动开始下一个岗位投递(不关闭右侧面板,避免闪烁)
autoStartNextApply()
}
} catch (e) {
console.error('[Agent] 投递失败', e)
ElMessage.error('投递失败,请重试')
}
}
/** 第3步:去投递 — 打开原链接 */
function handleGoApplyStep(msg: AgentChatMessage) {
try {
const extraData = JSON.parse(msg.extra)
const sourceUrl = extraData.jobInfo?.sourceUrl
if (sourceUrl) {
window.open(sourceUrl, '_blank')
}
} catch { /* 忽略 */ }
}
/** 持久化保存 apply_progress 消息到后端 */
async function persistApplyProgressMessage(msg: AgentChatMessage) {
try {
await addAgentChatMessage({
type: 'apply_progress',
content: msg.content,
extra: msg.extra,
})
} catch {
console.error('[Agent] 保存投递进度消息失败')
}
}
/**
* 自动开始下一个岗位投递
* 当前岗位完成(step=4)后,如果未暂停,自动刷新列表并开始下一个
*/
async function autoStartNextApply() {
// 如果已暂停,不自动继续
if (isPaused.value) {
isApplying.value = false
return
}
// 刷新待投递列表
await loadApplyList()
if (applyJobList.value.length === 0) {
// 没有更多待投递岗位,结束流程
isApplying.value = false
showRightPanel.value = false
rightPanelMode.value = 'recommend'
ElMessage.success('所有待投递岗位已处理完毕')
return
}
// 取下一个岗位,开始新一轮投递
const targetJob = applyJobList.value[0]
const extraData = {
jobInfo: targetJob,
resumeInfo: null as any,
step: 1,
}
const now = Math.floor(Date.now() / 1000)
const applyMsg: AgentChatMessage = {
id: Date.now(),
type: 'apply_progress',
content: '',
extra: JSON.stringify(extraData),
createTime: { seconds: now, nanos: 0 },
}
chatMessages.value.push(applyMsg)
await nextTick()
scrollChatToBottom()
// 第1步:优化简历
rightPanelMode.value = 'generating'
showRightPanel.value = true
startProgressSimulation()
try {
const res = await optimizeAgentResume({
resumeId: defaultResumeId.value,
jobId: targetJob.id,
})
finishProgress()
await new Promise(resolve => setTimeout(resolve, 1000))
const apiData = res?.data || res
if (apiData) {
const resumeResult = mapOptimizeResumeToTemplate(apiData)
applyResumeData.value = resumeResult
applyResumeName.value = resumeResult.name || '岗位专属简历'
extraData.resumeInfo = apiData
extraData.step = 2
applyMsg.extra = JSON.stringify(extraData)
rightPanelMode.value = 'resume'
}
} catch (e) {
console.error('[Agent] 优化简历失败', e)
ElMessage.error('简历优化失败,请重试')
finishProgress()
showRightPanel.value = false
rightPanelMode.value = 'recommend'
isApplying.value = false
}
}
/** 发送聊天消息 */
@@ -644,11 +1129,11 @@ async function handleSendMessage() {
const excludeJobIds = collectExcludeJobIds()
try {
const recRes = await fetchAgentRecommend({ preference, excludeJobIds })
if (recRes.code === '0' && recRes.data && recRes.data.list?.length > 0) {
if (recRes.code === '0' && recRes.data ) {
const summary = recRes.data.summary || ''
const jobList = recRes.data.list
const extraJson = JSON.stringify({ summary, list: jobList })
// const contentV = recRes.data.list?.length==0?'':''
/* 持久化 recommend 消息 */
try {
await addAgentChatMessage({ type: 'recommend', content: '', extra: extraJson })
@@ -664,6 +1149,21 @@ async function handleSendMessage() {
createTime: { seconds: recNow, nanos: 0 },
})
// await nextTick()
// scrollChatToBottom()
// } else {
// /* 推荐列表为空 — 插入提示消息 */
// const emptyNow = Math.floor(Date.now() / 1000)
// chatMessages.value.push({
// id: Date.now() + 2,
// type: 'assistant',
// content: '暂无相关职位推荐',
// extra: '',
// createTime: { seconds: emptyNow, nanos: 0 },
// })
// try {
// await addAgentChatMessage({ type: 'assistant', content: '暂无相关职位推荐' })
// } catch { /* 静默 */ }
await nextTick()
scrollChatToBottom()
}