Files
offerpai_web/src/components/ProfileWelcomeDialog.vue
T

192 lines
6.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>