feat: add seeker center (resume editor, applications, profile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
TianyangZhang 2026-03-25 08:42:46 +08:00
parent 0a44e4a18d
commit ca629a403c
3 changed files with 156 additions and 3 deletions

View File

@ -1,3 +1,35 @@
<template> <template>
<div>我的投递 - 开发中</div> <div>
<h2>我的投递</h2>
<el-table :data="applications" v-loading="loading" border>
<el-table-column prop="job_title" label="职位" />
<el-table-column prop="company_name" label="公司" />
<el-table-column prop="applied_at" label="投递时间" :formatter="(r,c,v) => v?.slice(0,10)" />
<el-table-column prop="status" label="状态">
<template #default="{ row }">
<el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-empty v-if="applications.length === 0 && !loading" description="暂无投递记录" />
</div>
</template> </template>
<script setup>
import { ref, onMounted } from 'vue'
import { getMyApplications } from '@/api/applications'
const applications = ref([])
const loading = ref(false)
const STATUS_MAP = { pending:'待查看', viewed:'已查看', interviewing:'面试中', hired:'已录用', rejected:'已拒绝' }
const STATUS_TYPE = { pending:'info', viewed:'', interviewing:'warning', hired:'success', rejected:'danger' }
const statusLabel = s => STATUS_MAP[s] || s
const statusType = s => STATUS_TYPE[s] || ''
onMounted(async () => {
loading.value = true
const { data } = await getMyApplications()
applications.value = data.results
loading.value = false
})
</script>

View File

@ -1,3 +1,36 @@
<template> <template>
<div>账号设置 - 开发中</div> <div>
<h2>账号设置</h2>
<el-card style="max-width:480px">
<el-form :model="form" label-width="80px">
<el-form-item label="邮箱"><el-input v-model="form.email" /></el-form-item>
<el-form-item label="手机号"><el-input v-model="form.phone" /></el-form-item>
<el-button type="primary" @click="save" :loading="saving">保存</el-button>
</el-form>
</el-card>
</div>
</template> </template>
<script setup>
import { ref, onMounted } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { updateMe } from '@/api/auth'
import { ElMessage } from 'element-plus'
const auth = useAuthStore()
const form = ref({ email: '', phone: '' })
const saving = ref(false)
onMounted(() => {
form.value.email = auth.user?.email || ''
form.value.phone = auth.user?.phone || ''
})
async function save() {
saving.value = true
try {
await updateMe({ email: form.value.email, phone: form.value.phone })
ElMessage.success('保存成功')
} catch { ElMessage.error('保存失败') }
finally { saving.value = false }
}
</script>

View File

@ -1,3 +1,91 @@
<template> <template>
<div>我的简历 - 开发中</div> <div v-loading="loading">
<h2>我的简历</h2>
<el-form :model="form" label-width="80px" v-if="form">
<el-card style="margin-bottom:16px">
<template #header>基本信息</template>
<el-form-item label="姓名"><el-input v-model="form.name" /></el-form-item>
<el-form-item label="性别">
<el-radio-group v-model="form.gender">
<el-radio value="male"></el-radio>
<el-radio value="female"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="生日"><el-date-picker v-model="form.birthday" type="date" value-format="YYYY-MM-DD" /></el-form-item>
</el-card>
<el-card style="margin-bottom:16px">
<template #header>
教育经历
<el-button size="small" @click="addEducation" style="float:right">添加</el-button>
</template>
<div v-for="(edu, i) in form.education" :key="i" style="margin-bottom:8px">
<el-row :gutter="12">
<el-col :span="8"><el-input v-model="edu.school" placeholder="学校名称" /></el-col>
<el-col :span="6"><el-input v-model="edu.degree" placeholder="学历" /></el-col>
<el-col :span="7"><el-input v-model="edu.major" placeholder="专业" /></el-col>
<el-col :span="3"><el-button type="danger" link @click="form.education.splice(i,1)">删除</el-button></el-col>
</el-row>
</div>
</el-card>
<el-card style="margin-bottom:16px">
<template #header>
工作经历
<el-button size="small" @click="addExperience" style="float:right">添加</el-button>
</template>
<div v-for="(exp, i) in form.experience" :key="i" style="margin-bottom:8px">
<el-row :gutter="12">
<el-col :span="8"><el-input v-model="exp.company" placeholder="公司名称" /></el-col>
<el-col :span="7"><el-input v-model="exp.position" placeholder="职位" /></el-col>
<el-col :span="6"><el-input v-model="exp.duration" placeholder="时长2年" /></el-col>
<el-col :span="3"><el-button type="danger" link @click="form.experience.splice(i,1)">删除</el-button></el-col>
</el-row>
</div>
</el-card>
<el-card style="margin-bottom:16px">
<template #header>简历附件</template>
<el-upload action="/api/resumes/me/" :headers="uploadHeaders" name="attachment" accept=".pdf,.doc,.docx" method="patch">
<el-button>上传简历PDF/Word</el-button>
</el-upload>
</el-card>
<el-button type="primary" @click="save" :loading="saving">保存简历</el-button>
</el-form>
</div>
</template> </template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { getMyResume, updateMyResume } from '@/api/resumes'
import { ElMessage } from 'element-plus'
const form = ref(null)
const loading = ref(false)
const saving = ref(false)
const uploadHeaders = computed(() => ({
Authorization: `Bearer ${localStorage.getItem('access_token')}`
}))
onMounted(async () => {
loading.value = true
const { data } = await getMyResume()
form.value = { ...data, education: data.education || [], experience: data.experience || [] }
loading.value = false
})
const addEducation = () => form.value.education.push({ school: '', degree: '', major: '' })
const addExperience = () => form.value.experience.push({ company: '', position: '', duration: '' })
async function save() {
saving.value = true
try {
await updateMyResume(form.value)
ElMessage.success('简历已保存')
} catch {
ElMessage.error('保存失败')
} finally {
saving.value = false
}
}
</script>