带文件拖动上传的新版简历上传功能,独立文件操作组件,简历列表页和个人资料页有调用

This commit is contained in:
2026-05-26 19:20:34 +08:00
parent 4b16341f92
commit 8b6d424b2f
10 changed files with 582 additions and 87 deletions
+80 -45
View File
@@ -9,7 +9,15 @@
<!-- 简历上传区域 -->
<div class="profile-welcome-dialog__form">
<div class="profile-welcome-dialog__label">*简历</div>
<div class="profile-welcome-dialog__upload-area" @click="handleUploadClick">
<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">
@@ -18,7 +26,8 @@
<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">支持上传PDFWORD格式文件大小不超过10M</p>
<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>
@@ -66,6 +75,9 @@ const route = useRoute()
/** 已上传的文件名 */
const uploadedFileName = ref('')
/** 拖拽悬停状态 */
const isDragover = ref(false)
/** 内部控制弹窗显隐(用于上传时临时隐藏) */
const dialogVisible = ref(false)
@@ -87,61 +99,84 @@ watch(() => props.modelValue, (val) => {
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 = async () => {
input.onchange = () => {
const file = input.files?.[0]
if (!file) 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('上传失败,请稍后重试')
}
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)