Files
offerpai_web/src/components/AgentApplyProgressPanel.vue
T

255 lines
8.4 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>
<!-- 申请进度面板 右侧面板显示已投递岗位列表带筛选搜索滚动分页 -->
<div class="agent-apply-progress-panel">
<!-- 顶部标题栏 -->
<div class="agent-apply-progress-panel__header">
<span class="agent-apply-progress-panel__title">申请进度</span>
<!-- 关闭按钮 -->
<button class="agent-apply-progress-panel__close-btn" @click="emit('close')">
<svg viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" fill="#1A1A2E" />
<path d="M8 8l8 8M16 8l-8 8" stroke="#fff" stroke-width="1.5" stroke-linecap="round" />
</svg>
</button>
</div>
<!-- 筛选栏状态下拉 + 搜索框 -->
<div class="agent-apply-progress-panel__filter">
<!-- 状态下拉选择 -->
<div class="agent-apply-progress-panel__select-wrap">
<select v-model="selectedStatus" class="agent-apply-progress-panel__select" @change="handleFilterChange">
<option :value="null">全部</option>
<option :value="0">已投递</option>
<option :value="1">面试中</option>
<option :value="2">有Offer</option>
<option :value="3">未通过</option>
<option :value="4">已结束</option>
</select>
</div>
<!-- 搜索框 -->
<input
v-model="keyword"
class="agent-apply-progress-panel__search"
placeholder="搜索岗位/公司"
@keyup.enter="handleSearch"
/>
</div>
<!-- 列表内容可滚动触底加载更多 -->
<div ref="listRef" class="agent-apply-progress-panel__list" @scroll="handleScroll">
<!-- 岗位项 -->
<div
v-for="job in jobList"
:key="job.id"
class="agent-apply-progress-panel__item"
>
<!-- 左侧信息区域 -->
<div class="agent-apply-progress-panel__info">
<!-- 公司 Logo -->
<div class="agent-apply-progress-panel__logo">
<svg viewBox="0 0 24 24" fill="none">
<rect x="3" y="7" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"/>
<path d="M7 7V5a2 2 0 012-2h6a2 2 0 012 2v2" stroke="currentColor" stroke-width="1.5"/>
</svg>
</div>
<!-- 岗位详情 -->
<div class="agent-apply-progress-panel__detail">
<div class="agent-apply-progress-panel__company">{{ job.companyShortName || job.companyName }}</div>
<div class="agent-apply-progress-panel__position">{{ job.title }}</div>
<!-- 标签 -->
<div class="agent-apply-progress-panel__tags">
<span v-if="job.regionName" class="agent-apply-progress-panel__tag">{{ job.regionName }}</span>
<span v-if="job.categoryName" class="agent-apply-progress-panel__tag">{{ job.categoryName }}</span>
<span v-for="tag in (job.tags || []).slice(0, 2)" :key="tag" class="agent-apply-progress-panel__tag">{{ tag }}</span>
</div>
</div>
</div>
<!-- 右侧匹配度 + 状态下拉 + 删除按钮 -->
<div class="agent-apply-progress-panel__right">
<!-- 匹配度环形 -->
<div class="agent-apply-progress-panel__score">
<svg class="agent-apply-progress-panel__ring" viewBox="0 0 44 44">
<circle cx="22" cy="22" r="18" fill="none" stroke="#E8E8E8" stroke-width="3" />
<circle
cx="22" cy="22" r="18" fill="none"
stroke="#4FC2C9" stroke-width="3"
stroke-linecap="round"
:stroke-dasharray="2 * Math.PI * 18"
:stroke-dashoffset="2 * Math.PI * 18 * (1 - (job.matchScore || 0) / 100)"
transform="rotate(-90 22 22)"
/>
</svg>
<span class="agent-apply-progress-panel__score-text">{{ job.matchScore || 0 }}%</span>
</div>
<!-- 状态下拉 -->
<select
class="agent-apply-progress-panel__status-select"
:value="job.status"
@change="handleStatusChange(job, $event)"
>
<option :value="0">已投递</option>
<option :value="1">面试中</option>
<option :value="2">有Offer</option>
<option :value="3">未通过</option>
<option :value="4">已结束</option>
</select>
<!-- 删除按钮 -->
<button class="agent-apply-progress-panel__delete-btn" @click="handleDelete(job)">
<svg viewBox="0 0 16 16" fill="none">
<circle cx="8" cy="8" r="7" fill="#BFBFBF" />
<path d="M5.5 5.5l5 5M10.5 5.5l-5 5" stroke="#fff" stroke-width="1.2" stroke-linecap="round" />
</svg>
</button>
</div>
</div>
<!-- 加载更多提示 -->
<div v-if="loadingMore" class="agent-apply-progress-panel__loading">加载中...</div>
<!-- 没有更多数据 -->
<div v-if="noMore && jobList.length > 0" class="agent-apply-progress-panel__no-more">暂无更多数据</div>
<!-- 空状态 -->
<div v-if="!loading && jobList.length === 0 && !loadingMore" class="agent-apply-progress-panel__empty">暂无数据</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { fetchApplyList } from '@/api/jobs'
import { applyJob, cancelApplyJob } from '@/api/agent'
import type { JobListItem } from '@/api/jobs'
/** 事件 */
const emit = defineEmits<{
/** 关闭面板 */
(e: 'close'): void
}>()
/** 岗位列表数据 */
const jobList = ref<JobListItem[]>([])
/** 当前页码 */
const currentPage = ref(1)
/** 每页条数 */
const pageSize = 30
/** 是否正在加载首页 */
const loading = ref(false)
/** 是否正在加载更多 */
const loadingMore = ref(false)
/** 是否没有更多数据 */
const noMore = ref(false)
/** 当前选中的状态筛选 */
const selectedStatus = ref<number | null>(null)
/** 搜索关键词 */
const keyword = ref('')
/** 列表容器 ref */
const listRef = ref<HTMLElement | null>(null)
/** 加载投递列表 */
async function loadList(page: number) {
if (page === 1) {
loading.value = true
} else {
loadingMore.value = true
}
try {
const res = await fetchApplyList({
pageNum: page,
pageSize,
status: selectedStatus.value,
keyword: keyword.value || undefined,
})
if (res.code === '0' && res.data) {
const list = res.data.list || []
if (page === 1) {
jobList.value = list
} else {
jobList.value.push(...list)
}
/* 判断是否还有更多数据 */
const total = Number(res.data.total || 0)
if (jobList.value.length >= total || list.length < pageSize) {
noMore.value = true
}
}
} catch {
console.error('[AgentApplyProgressPanel] 加载投递列表失败')
} finally {
loading.value = false
loadingMore.value = false
}
}
/** 重置列表并重新加载 */
function resetAndLoad() {
currentPage.value = 1
jobList.value = []
noMore.value = false
loadList(1)
}
/** 筛选状态变更 */
function handleFilterChange() {
resetAndLoad()
}
/** 搜索框回车 */
function handleSearch() {
resetAndLoad()
}
/** 滚动触底加载更多 */
function handleScroll() {
if (!listRef.value || loadingMore.value || noMore.value) return
const { scrollTop, scrollHeight, clientHeight } = listRef.value
if (scrollTop + clientHeight >= scrollHeight - 50) {
currentPage.value++
loadList(currentPage.value)
}
}
/** 修改单个岗位的投递状态 */
async function handleStatusChange(job: JobListItem, event: Event) {
const newStatus = Number((event.target as HTMLSelectElement).value)
try {
await applyJob({ jobId: job.id, status: newStatus })
job.status = newStatus
ElMessage.success('状态已更新')
} catch {
ElMessage.error('状态更新失败')
/* 恢复原值 — 触发视图刷新 */
;(event.target as HTMLSelectElement).value = String(job.status)
}
}
/** 删除(取消投递) */
async function handleDelete(job: JobListItem) {
try {
await cancelApplyJob(job.id)
const idx = jobList.value.findIndex(j => j.id === job.id)
if (idx !== -1) {
jobList.value.splice(idx, 1)
}
ElMessage.success('已删除')
} catch {
ElMessage.error('删除失败')
}
}
onMounted(() => {
loadList(1)
})
</script>
<style scoped lang="scss">
@use '../assets/styles/components/agent-apply-progress-panel';
</style>