192 lines
6.2 KiB
Vue
192 lines
6.2 KiB
Vue
<template>
|
||
<!-- 欢迎使用弹窗 — 首次登录无个人资料时弹出,引导上传简历 -->
|
||
<Teleport to="body">
|
||
<div v-if="dialogVisible" class="profile-welcome-overlay" :class="{ 'profile-welcome-overlay--behind': uploading }" @click.stop>
|
||
<div class="profile-welcome-dialog" @click.stop>
|
||
<!-- 标题 -->
|
||
<h2 class="profile-welcome-dialog__title">欢迎使用Offer派</h2>
|
||
|
||
<!-- 简历上传区域 -->
|
||
<div class="profile-welcome-dialog__form">
|
||
<div class="profile-welcome-dialog__label">*简历</div>
|
||
<div
|
||
class="profile-welcome-dialog__upload-area"
|
||
:class="{ 'is-dragover': isDragover }"
|
||
@click="handleUploadClick"
|
||
@dragover.prevent="isDragover = true"
|
||
@dragenter.prevent="isDragover = true"
|
||
@dragleave.prevent="isDragover = false"
|
||
@drop.prevent="handleDrop"
|
||
>
|
||
<!-- 未上传状态 -->
|
||
<template v-if="!uploadedFileName">
|
||
<div class="profile-welcome-dialog__upload-icon">
|
||
<svg viewBox="0 0 24 24" fill="none" width="28" height="28">
|
||
<path d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||
<path d="M12 14V4m0 0l-4 4m4-4l4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||
</svg>
|
||
</div>
|
||
<p class="profile-welcome-dialog__upload-tip fs16 color-3 fw600">点击或拖拽文件到这里上传</p>
|
||
<p class="profile-welcome-dialog__upload-tip">支持 PDF / DOC / DOCX,单个文件不超过 10MB</p>
|
||
</template>
|
||
<!-- 已上传状态 -->
|
||
<template v-else>
|
||
<div class="profile-welcome-dialog__upload-icon profile-welcome-dialog__upload-icon--done">✓</div>
|
||
<p class="profile-welcome-dialog__upload-tip">{{ uploadedFileName }}</p>
|
||
</template>
|
||
</div>
|
||
|
||
<!-- 隐私说明 -->
|
||
<p class="profile-welcome-dialog__privacy">
|
||
您的个人资料仅用于职位匹配、简历优化及网申投递。未经您的明确许可,我们绝不会将信息泄露给第三方或招聘人员。您可以随时更新或删除您的相关信息。
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 开始匹配按钮 -->
|
||
<button
|
||
class="profile-welcome-dialog__start-btn"
|
||
:disabled="!uploadedFileName"
|
||
@click="handleStart"
|
||
>
|
||
开始匹配
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</Teleport>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, watch } from 'vue'
|
||
import { useRouter, useRoute } from 'vue-router'
|
||
import { uploadResume } from '@/utils/aiRequest'
|
||
import { syncProfileFromResume } from '@/api/profile'
|
||
import { ElLoading, ElMessage } from 'element-plus'
|
||
import 'element-plus/es/components/loading/style/css'
|
||
|
||
/** 组件 Props */
|
||
const props = defineProps<{ modelValue: boolean }>()
|
||
|
||
/** 组件 Emits */
|
||
const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void }>()
|
||
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
|
||
/** 已上传的文件名 */
|
||
const uploadedFileName = ref('')
|
||
|
||
/** 拖拽悬停状态 */
|
||
const isDragover = ref(false)
|
||
|
||
/** 内部控制弹窗显隐(用于上传时临时隐藏) */
|
||
const dialogVisible = ref(false)
|
||
|
||
/** 上传中状态 — 降低弹窗层级 */
|
||
const uploading = ref(false)
|
||
|
||
/** 上传后获得的简历 ID */
|
||
const resumeId = ref('')
|
||
|
||
/** 弹窗打开时重置状态 */
|
||
watch(() => props.modelValue, (val) => {
|
||
if (val) {
|
||
uploadedFileName.value = ''
|
||
resumeId.value = ''
|
||
dialogVisible.value = true
|
||
} else {
|
||
dialogVisible.value = false
|
||
}
|
||
document.body.style.overflow = val ? 'hidden' : ''
|
||
})
|
||
|
||
/** 允许的文件 MIME 类型 */
|
||
const allowedTypes = [
|
||
'application/pdf',
|
||
'application/msword',
|
||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||
]
|
||
|
||
/** 处理文件校验与上传(点击和拖拽共用) */
|
||
async function processFile(file: File) {
|
||
// 格式校验
|
||
if (!allowedTypes.includes(file.type)) {
|
||
ElMessage.error('仅支持上传PDF、WORD格式文件')
|
||
return
|
||
}
|
||
|
||
// 文件大小校验(10MB)
|
||
if (file.size > 10 * 1024 * 1024) {
|
||
ElMessage.error('文件大小不能超过10M')
|
||
return
|
||
}
|
||
|
||
// 标记上传中,降低弹窗层级让 loading 显示在上面
|
||
uploading.value = true
|
||
|
||
// 全屏加载
|
||
const loading = ElLoading.service({
|
||
lock: true,
|
||
text: '简历解析中,请耐心等待…',
|
||
background: 'rgba(0, 0, 0, 0.5)',
|
||
customClass: 'profile-welcome-loading',
|
||
})
|
||
|
||
try {
|
||
// 上传简历文件
|
||
const res = await uploadResume(file)
|
||
if (res.code === 0 && res.data?.resumeId) {
|
||
resumeId.value = String(res.data.resumeId)
|
||
uploadedFileName.value = file.name
|
||
|
||
// 继续调用同步个人资料接口,加载特效延续
|
||
loading.setText('正在同步个人资料…')
|
||
await syncProfileFromResume(resumeId.value)
|
||
loading.close()
|
||
uploading.value = false
|
||
ElMessage.success('简历上传并同步成功')
|
||
} else {
|
||
loading.close()
|
||
uploading.value = false
|
||
ElMessage.error(res.msg || '上传失败')
|
||
}
|
||
} catch {
|
||
loading.close()
|
||
uploading.value = false
|
||
ElMessage.error('上传失败,请稍后重试')
|
||
}
|
||
}
|
||
|
||
/** 点击上传区域 — 弹出文件选择 */
|
||
function handleUploadClick() {
|
||
const input = document.createElement('input')
|
||
input.type = 'file'
|
||
input.accept = '.pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||
|
||
input.onchange = () => {
|
||
const file = input.files?.[0]
|
||
if (file) processFile(file)
|
||
}
|
||
|
||
input.click()
|
||
}
|
||
|
||
/** 拖拽放下文件 — 取第一个文件进行上传 */
|
||
function handleDrop(e: DragEvent) {
|
||
isDragover.value = false
|
||
const file = e.dataTransfer?.files[0]
|
||
if (file) processFile(file)
|
||
}
|
||
|
||
/** 点击开始匹配按钮 */
|
||
function handleStart() {
|
||
emit('update:modelValue', false)
|
||
// 如果当前在 profile 页面就跳转 profile,其余跳转 jobs
|
||
if (route.path === '/profile') {
|
||
// 已经在 profile 页面,刷新页面数据
|
||
router.go(0)
|
||
} else {
|
||
router.push('/jobs')
|
||
}
|
||
}
|
||
</script>
|