fix: 投递后禁用重复点击,显示已投递提示

改动:
- 页面加载时检查用户是否已投递该职位
- 已投递的职位禁用投递按钮
- 按钮显示'已投递'而非'立即投递'
- 用户重复点击时显示警告提示'您已投递过该职位'
- 样式:禁用按钮改为灰色,cursor 改为 not-allowed

效果:
- 防止用户误操作或恶意重复投递
- 清晰提示用户已投递状态
- 即使刷新页面也能显示正确的投递状态

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
TianyangZhang 2026-03-25 16:19:19 +08:00
parent 911e872a4a
commit fbcd98dc46
1 changed files with 50 additions and 10 deletions

View File

@ -25,8 +25,13 @@
<el-icon><Star /></el-icon> <el-icon><Star /></el-icon>
{{ collected ? '已收藏' : '收藏' }} {{ collected ? '已收藏' : '收藏' }}
</el-button> </el-button>
<el-button class="btn-apply" @click="handleApply" :loading="applying"> <el-button
立即投递 class="btn-apply"
@click="handleApply"
:loading="applying"
:disabled="applied"
>
{{ applied ? '已投递' : '立即投递' }}
</el-button> </el-button>
</div> </div>
<p class="apply-hint" v-if="!auth.isLoggedIn">登录后才能投递</p> <p class="apply-hint" v-if="!auth.isLoggedIn">登录后才能投递</p>
@ -107,8 +112,14 @@
<el-icon><Message /></el-icon> <el-icon><Message /></el-icon>
<span>{{ job.organization.email }}</span> <span>{{ job.organization.email }}</span>
</div> </div>
<el-button class="btn-apply-sm" @click="handleApply" :loading="applying" style="margin-top:12px;width:100%"> <el-button
立即投递 class="btn-apply-sm"
@click="handleApply"
:loading="applying"
:disabled="applied"
style="margin-top:12px;width:100%"
>
{{ applied ? '已投递' : '立即投递' }}
</el-button> </el-button>
<p class="apply-hint-sm" v-if="applied">已投递成功</p> <p class="apply-hint-sm" v-if="applied">已投递成功</p>
</div> </div>
@ -122,7 +133,7 @@
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { getJob, toggleFavorite } from '@/api/jobs' import { getJob, toggleFavorite } from '@/api/jobs'
import { applyJob } from '@/api/applications' import { applyJob, getMyApplications } from '@/api/applications'
import { useAuthStore } from '@/stores/auth' import { useAuthStore } from '@/stores/auth'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { Location, OfficeBuilding, Star, Message } from '@element-plus/icons-vue' import { Location, OfficeBuilding, Star, Message } from '@element-plus/icons-vue'
@ -138,9 +149,23 @@ const collected = ref(false)
onMounted(async () => { onMounted(async () => {
loading.value = true loading.value = true
try {
const { data } = await getJob(route.params.id) const { data } = await getJob(route.params.id)
job.value = data job.value = data
//
if (auth.isLoggedIn && auth.isSeeker) {
try {
const { data: applications } = await getMyApplications()
//
applied.value = applications.results?.some(app => app.job.id === parseInt(route.params.id)) || false
} catch (e) {
console.error('Failed to fetch applications:', e)
}
}
} finally {
loading.value = false loading.value = false
}
}) })
function formatDate(dt) { function formatDate(dt) {
@ -159,6 +184,8 @@ async function toggleCollect() {
async function handleApply() { async function handleApply() {
if (!auth.isLoggedIn) return router.push({ name: 'Login', query: { redirect: route.fullPath } }) if (!auth.isLoggedIn) return router.push({ name: 'Login', query: { redirect: route.fullPath } })
if (!auth.isSeeker) return ElMessage.warning('管理员账号无法投递职位') if (!auth.isSeeker) return ElMessage.warning('管理员账号无法投递职位')
if (applied.value) return ElMessage.warning('您已投递过该职位')
applying.value = true applying.value = true
try { try {
await applyJob(job.value.id) await applyJob(job.value.id)
@ -166,6 +193,7 @@ async function handleApply() {
ElMessage.success('投递成功!') ElMessage.success('投递成功!')
} catch (e) { } catch (e) {
if (e.response?.status === 400) { if (e.response?.status === 400) {
applied.value = true
ElMessage.warning(e.response.data?.detail || '您已投递过该职位') ElMessage.warning(e.response.data?.detail || '您已投递过该职位')
} else { } else {
ElMessage.error('投递失败,请先完善简历') ElMessage.error('投递失败,请先完善简历')
@ -255,10 +283,16 @@ async function handleApply() {
padding: 10px 28px; padding: 10px 28px;
font-size: 15px; font-size: 15px;
} }
.btn-apply:hover { .btn-apply:hover:not(:disabled) {
background: #c42820 !important; background: #c42820 !important;
border-color: #c42820 !important; border-color: #c42820 !important;
} }
.btn-apply:disabled {
background: #ccc !important;
border-color: #ccc !important;
color: #fff !important;
cursor: not-allowed !important;
}
.apply-hint { .apply-hint {
font-size: 12px; font-size: 12px;
color: #999; color: #999;
@ -445,10 +479,16 @@ async function handleApply() {
color: #fff !important; color: #fff !important;
font-weight: 600; font-weight: 600;
} }
.btn-apply-sm:hover { .btn-apply-sm:hover:not(:disabled) {
background: #c42820 !important; background: #c42820 !important;
border-color: #c42820 !important; border-color: #c42820 !important;
} }
.btn-apply-sm:disabled {
background: #ccc !important;
border-color: #ccc !important;
color: #fff !important;
cursor: not-allowed !important;
}
.apply-hint-sm { .apply-hint-sm {
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;