简历上传,pdf简历下载
This commit is contained in:
+104
-37
@@ -77,15 +77,22 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import { uploadResume } from '@/utils/aiRequest'
|
||||
import { fetchResumeList, type ResumeListItem } from '@/api/resume'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// ==================== 头像颜色池 ====================
|
||||
|
||||
/** 头像背景色列表,按索引循环取色 */
|
||||
const avatarColors = ['#1A1A2E', '#4FC2C9', '#F2994A', '#6C5CE7', '#E17055', '#00B894', '#FDCB6E']
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
/** 简历列表项类型 */
|
||||
/** 简历列表项(前端展示用) */
|
||||
interface ResumeItem {
|
||||
id: string
|
||||
name: string
|
||||
@@ -97,41 +104,68 @@ interface ResumeItem {
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
// ==================== 数据(模拟数据,后续对接接口) ====================
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* 将 Instant 时间戳转为友好的相对时间文案
|
||||
* @param instant 后端返回的 Instant 对象
|
||||
*/
|
||||
function formatTime(instant?: { seconds?: number; nanos?: number }): string {
|
||||
if (!instant?.seconds) return '-'
|
||||
const date = new Date(instant.seconds * 1000)
|
||||
const now = Date.now()
|
||||
const diff = now - date.getTime()
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
if (minutes < 1) return '刚刚'
|
||||
if (minutes < 60) return `${minutes}分钟前`
|
||||
const hours = Math.floor(minutes / 60)
|
||||
if (hours < 24) return `${hours}小时前`
|
||||
const days = Math.floor(hours / 24)
|
||||
if (days < 30) return `${days}天前`
|
||||
const months = Math.floor(days / 30)
|
||||
if (months < 12) return `${months}个月前`
|
||||
return `${Math.floor(months / 12)}年前`
|
||||
}
|
||||
|
||||
/**
|
||||
* 将后端简历数据转为前端展示结构
|
||||
*/
|
||||
function mapResumeItem(item: ResumeListItem, index: number): ResumeItem {
|
||||
const name = item.resumeName || '未命名简历'
|
||||
return {
|
||||
id: String(item.id ?? ''),
|
||||
name,
|
||||
avatarLetter: name.charAt(0) || '?',
|
||||
avatarColor: avatarColors[index % avatarColors.length],
|
||||
isDefault: item.isDefault === 1,
|
||||
targetJob: item.targetPosition || '',
|
||||
updatedAt: formatTime(item.updateTime),
|
||||
createdAt: formatTime(item.createTime),
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据 ====================
|
||||
|
||||
/** 简历列表 */
|
||||
const resumeList = ref<ResumeItem[]>([
|
||||
{
|
||||
id: '1',
|
||||
name: '李华_产品经理',
|
||||
avatarLetter: 'D',
|
||||
avatarColor: '#1A1A2E',
|
||||
isDefault: true,
|
||||
targetJob: '产品经理',
|
||||
updatedAt: '1个月前',
|
||||
createdAt: '1个月前',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: '李华_产品运营',
|
||||
avatarLetter: 'C',
|
||||
avatarColor: '#4FC2C9',
|
||||
isDefault: false,
|
||||
targetJob: '产品运营',
|
||||
updatedAt: '1个月前',
|
||||
createdAt: '1个月前',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
name: '李华_产品经理',
|
||||
avatarLetter: '?',
|
||||
avatarColor: '#BFBFBF',
|
||||
isDefault: false,
|
||||
targetJob: '',
|
||||
updatedAt: '1个月前',
|
||||
createdAt: '1个月前',
|
||||
},
|
||||
])
|
||||
const resumeList = ref<ResumeItem[]>([])
|
||||
|
||||
/** 加载简历列表 */
|
||||
async function loadResumeList() {
|
||||
try {
|
||||
const res = await fetchResumeList()
|
||||
if (res.code === '0' && res.data) {
|
||||
resumeList.value = res.data.map(mapResumeItem)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取简历列表失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
onMounted(() => {
|
||||
loadResumeList()
|
||||
})
|
||||
|
||||
// ==================== 页面状态 ====================
|
||||
|
||||
@@ -154,9 +188,42 @@ function handleAction(action: string, id: string) {
|
||||
console.log(action, id)
|
||||
}
|
||||
|
||||
/** 上传简历 */
|
||||
/** 上传简历 — 弹出文件选择,选择后调用 AI 接口上传 */
|
||||
function handleUpload() {
|
||||
console.log('上传简历')
|
||||
const input = document.createElement('input')
|
||||
input.type = 'file'
|
||||
// 限定 pdf 和 word 格式
|
||||
input.accept = '.pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
||||
|
||||
input.onchange = async () => {
|
||||
const file = input.files?.[0]
|
||||
if (!file) return
|
||||
|
||||
// 全屏加载提示,AI 接口响应较慢
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '简历解析中,请耐心等待…',
|
||||
background: 'rgba(0, 0, 0, 0.5)',
|
||||
customClass: 'resume-upload-loading',
|
||||
})
|
||||
|
||||
try {
|
||||
const res = await uploadResume(file)
|
||||
if (res.code === 0) {
|
||||
// 上传成功,刷新列表并跳转详情页
|
||||
loadResumeList()
|
||||
goDetail(String(res.data.resumeId))
|
||||
} else {
|
||||
ElMessage.error(res.msg || '上传失败')
|
||||
}
|
||||
} catch {
|
||||
ElMessage.error('上传失败,请稍后重试')
|
||||
} finally {
|
||||
loading.close()
|
||||
}
|
||||
}
|
||||
|
||||
input.click()
|
||||
}
|
||||
|
||||
/** 跳转到简历详情页 */
|
||||
|
||||
Reference in New Issue
Block a user