AI助手和Nova助手

This commit is contained in:
2026-05-06 15:07:44 +08:00
parent 1c91818494
commit f341408254
38 changed files with 4759 additions and 657 deletions
+23 -80
View File
@@ -286,9 +286,7 @@
</div>
</div>
<!-- AI正在回复的加载指示器 -->
<div v-if="aiLoading" class="job-resume-custom-dialog__ai-msg job-resume-custom-dialog__ai-msg--ai">
<div class="job-resume-custom-dialog__ai-msg-bubble job-resume-custom-dialog__ai-msg-bubble--loading">AI正在思考中...</div>
</div>
<AiThinkingIndicator v-if="aiLoading" text="AI正在思考中" />
</div>
<!-- AI输入框 -->
<div class="job-resume-custom-dialog__ai-input-area">
@@ -307,7 +305,7 @@
</div>
<!-- 编辑内容 折叠手风琴式编辑面板 -->
<div v-if="previewTab === 'edit'" class="job-resume-custom-dialog__preview-edit">
<JobResumeCustomEditPanel :resumeData="customResumeRawData" @update="onEditPanelUpdate" />
<JobResumeCustomEditPanel :resumeData="customResumeRawData" :jobId="jobId" @update="onEditPanelUpdate" />
</div>
</div>
</div>
@@ -318,7 +316,7 @@
<button class="job-resume-custom-dialog__primary-btn" @click="handleDrawerNext">立即定制简历</button>
</div>
<!-- 步骤四专属底部下载简历 + 立即去投递 -->
<div v-if="currentStep === 4" class="job-resume-custom-dialog__preview-footer">
<div v-if="currentStep === 4" class="job-resume-custom-dialog__preview-footer mt10 pt30">
<!-- 左侧下载简历按钮带下拉 -->
<div class="job-resume-custom-dialog__download-wrap">
<button class="job-resume-custom-dialog__download-btn" @click="toggleDownloadMenu">下载简历</button>
@@ -347,14 +345,15 @@
<script setup lang="ts">
import { ref, computed, nextTick, watch } from 'vue'
import html2pdf from 'html2pdf.js'
import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
import JobResumeCustomEditPanel from '@/components/JobResumeCustomEditPanel.vue'
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
import { exportResumePdf, exportResumeWord } from '@/utils/resumeExport'
import { fetchResumeList } from '@/api/resume'
import type { ResumeListItem } from '@/api/resume'
import { fetchCustomizeResume, generateCustomizeResume, aiEditResume, rollbackCustomizeResume } from '@/api/jobs'
import type { CustomizeResumeData, AiEditChatMessage } from '@/api/jobs'
import AiThinkingIndicator from '@/components/tools/AiThinkingIndicator.vue'
// ==================== 类型定义 ====================
@@ -458,7 +457,7 @@ async function fetchAndLoadCustomResume() {
})
try {
// 第一步:查询是否已有定制简历
let queryRes = await fetchCustomizeResume()
let queryRes = await fetchCustomizeResume(props.jobId)
// if (queryRes.code === 0 && queryRes.data) {
// // 已有定制简历,直接填充数据并跳转预览
// fillCustomResumeData(queryRes.data)
@@ -480,7 +479,7 @@ async function fetchAndLoadCustomResume() {
}
// 第三步:生成成功后再次查询获取简历数据
queryRes = await fetchCustomizeResume()
queryRes = await fetchCustomizeResume(props.jobId)
if (queryRes.code === 0 && queryRes.data) {
fillCustomResumeData(queryRes.data)
currentStep.value = 4
@@ -845,7 +844,7 @@ async function sendAiMessage(text: string) {
oldResumeTemplateData.value = JSON.parse(JSON.stringify(resumeTemplateData.value))
// 重新查询定制简历数据来刷新简历预览
const queryRes = await fetchCustomizeResume()
const queryRes = await fetchCustomizeResume(props.jobId)
if (queryRes.code === 0 && queryRes.data) {
fillCustomResumeData(queryRes.data)
// 开启差异对比模式
@@ -894,14 +893,14 @@ async function confirmRollback() {
if (idx < 0) return
try {
const res = await rollbackCustomizeResume()
const res = await rollbackCustomizeResume(props.jobId)
if (res.code === 0) {
// 标记该消息为已撤销
aiMessages.value[idx].rollbackStatus = 'done'
// 关闭差异对比模式
isShowDiff.value = false
// 重新查询简历数据刷新预览
const queryRes = await fetchCustomizeResume()
const queryRes = await fetchCustomizeResume(props.jobId)
if (queryRes.code === 0 && queryRes.data) {
fillCustomResumeData(queryRes.data)
}
@@ -929,78 +928,22 @@ function toggleDownloadMenu() {
async function handleDownload(type: 'pdf' | 'word') {
showDownloadMenu.value = false
if (type === 'pdf') {
// 通过 JobResumeTemplate 组件暴露的 resumeRef 获取简历DOM
const element = resumeTemplateRef.value?.resumeRef
if (!element) {
console.error('[下载简历] 无法获取简历模板DOM')
return
}
const element = resumeTemplateRef.value?.resumeRef
if (!element) {
console.error('[下载简历] 无法获取简历模板DOM')
return
}
// html2pdf 配置选项
const options = {
margin: [10, 10, 10, 10] as [number, number, number, number],
filename: `${resumeTemplateData.value.name || '简历'}_定制简历.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, useCORS: true, logging: false },
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' as const },
}
const fileName = (resumeTemplateData.value.name || '简历') + '_定制简历'
try {
await html2pdf().set(options).from(element).save()
} catch (err) {
console.error('[下载简历] PDF生成失败', err)
}
} else {
// 将简历HTML转为Word文档并下载(使用HTML格式的.doc文件,Word可正常打开)
const element = resumeTemplateRef.value?.resumeRef
if (!element) {
console.error('[下载简历] 无法获取简历模板DOM')
return
}
try {
// 获取页面样式表内容
const styleSheets = Array.from(document.styleSheets)
let cssText = ''
styleSheets.forEach((sheet) => {
try {
Array.from(sheet.cssRules).forEach((rule) => {
cssText += rule.cssText + '\n'
})
} catch {
// 跨域样式表无法读取,跳过
}
})
// 组装完整HTML文档(Word可识别的HTML格式)
const fullHtml = `
<html xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta charset="utf-8">
<meta name="ProgId" content="Word.Document">
<meta name="Generator" content="Microsoft Word 15">
<style>${cssText}</style>
</head>
<body>${element.outerHTML}</body>
</html>
`
// 生成Blob并触发下载
const blob = new Blob([fullHtml], { type: 'application/msword' })
const fileName = `${resumeTemplateData.value.name || '简历'}_定制简历.doc`
const link = document.createElement('a')
link.href = URL.createObjectURL(blob)
link.download = fileName
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(link.href)
} catch (err) {
console.error('[下载简历] Word生成失败', err)
try {
if (type === 'pdf') {
await exportResumePdf(element, fileName)
} else {
exportResumeWord(element, fileName)
}
} catch (err) {
console.error('[下载简历] 导出失败', err)
}
}