321 lines
7.7 KiB
Vue
321 lines
7.7 KiB
Vue
<template>
|
||
<view class="page candidate-record-page">
|
||
<view class="record-header">
|
||
<button class="back-button" aria-label="返回候选首页" @click="goBack">←</button>
|
||
<view>
|
||
<text class="page-title">面试记录</text>
|
||
<text class="page-subtitle">{{ latestRecord ? `${latestRecord.time} · ${latestRecord.position}` : '面试与资料审核状态' }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="message" class="status-message">{{ message }}</view>
|
||
|
||
<view v-if="!loggedIn" class="panel login-panel">
|
||
<view>
|
||
<text class="panel-title">需要登录</text>
|
||
<text class="muted block">登录后查看本人面试记录。</text>
|
||
</view>
|
||
<button class="primary-button login-button" @click="goLogin">去登录</button>
|
||
</view>
|
||
|
||
<template v-else-if="latestRecord">
|
||
<view class="score-card">
|
||
<view class="score-card-head">
|
||
<view>
|
||
<text class="score-value">{{ latestRecord.score }} 分</text>
|
||
<text class="score-meta">AI 初评 · {{ latestRecord.result }}</text>
|
||
</view>
|
||
<text class="review-badge">{{ reviewStatusLabel }}</text>
|
||
</view>
|
||
<text class="score-summary">{{ latestRecord.summary }}</text>
|
||
<text class="score-evidence">{{ questionRecords.length }} 道题 · 已保存真实回答</text>
|
||
</view>
|
||
|
||
<view class="question-card">
|
||
<text class="section-title">问答记录</text>
|
||
<view v-for="item in questionRecords" :key="item.seq" class="question-record">
|
||
<view class="question-title-row">
|
||
<text class="question-index">{{ item.seq }}</text>
|
||
<text class="question-title">{{ item.question }}</text>
|
||
</view>
|
||
<text class="answer-text">回答:{{ item.answer }}</text>
|
||
<text class="answer-rating" :class="ratingTone(item.rating)">{{ item.rating }}</text>
|
||
</view>
|
||
<view v-if="!questionRecords.length" class="empty question-empty">暂无问答明细</view>
|
||
</view>
|
||
|
||
<view class="review-status-card">
|
||
<text class="review-status-title">人工复核状态</text>
|
||
<text class="review-status-desc">{{ reviewStatusDescription }}</text>
|
||
</view>
|
||
|
||
<button class="primary-button home-button" @click="goBack">返回候选人中心</button>
|
||
</template>
|
||
|
||
<view v-else class="panel empty-record-panel">
|
||
<text class="panel-title">暂无面试记录</text>
|
||
<text class="muted block">完成一次结构化面试后,这里会展示评分和问答明细。</text>
|
||
<button class="primary-button home-button" @click="openInterview">开始面试</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, ref } from 'vue';
|
||
import { onShow } from '@dcloudio/uni-app';
|
||
import type { CandidateMaterial, InterviewRecord } from '@/types/api';
|
||
import { isLoggedIn, rememberLoginRedirect } from '@/services/auth';
|
||
import { getCandidateInterviewRecords, getCandidateMaterials } from '@/services/candidate';
|
||
import { candidateRoutes } from '@/services/candidate-view';
|
||
import { resetPageScroll } from '@/services/navigation';
|
||
|
||
const currentPath = '/pages/candidate/progress/index';
|
||
const records = ref<InterviewRecord[]>([]);
|
||
const materials = ref<CandidateMaterial[]>([]);
|
||
const loggedIn = ref(false);
|
||
const message = ref('');
|
||
|
||
const latestRecord = computed(() => records.value[0] || null);
|
||
const latestMaterial = computed(() => materials.value[0] || null);
|
||
const questionRecords = computed(() => latestRecord.value?.questions || []);
|
||
const reviewStatusLabel = computed(() => latestMaterial.value?.status === '已通过' ? '人工复核完成' : '人工复核中');
|
||
const reviewStatusDescription = computed(() => latestMaterial.value?.status === '已通过'
|
||
? 'HR 已完成 AI 评分与补充资料复核,后续通知会同步更新。'
|
||
: 'HR 正在复核 AI 评分与补充资料,预计 1-2 个工作日更新。');
|
||
|
||
const clearData = () => {
|
||
records.value = [];
|
||
materials.value = [];
|
||
};
|
||
|
||
const loadPage = async () => {
|
||
loggedIn.value = isLoggedIn();
|
||
if (!loggedIn.value) {
|
||
records.value = [];
|
||
materials.value = [];
|
||
return;
|
||
}
|
||
try {
|
||
[materials.value, records.value] = await Promise.all([
|
||
getCandidateMaterials(),
|
||
getCandidateInterviewRecords()
|
||
]);
|
||
message.value = '';
|
||
} catch (error) {
|
||
clearData();
|
||
loggedIn.value = isLoggedIn();
|
||
message.value = loggedIn.value ? (error instanceof Error ? error.message : '面试记录加载失败') : '登录已过期,请重新登录';
|
||
}
|
||
};
|
||
|
||
const ratingTone = (rating: string) => rating === '优秀' ? 'excellent' : rating === '良好' ? 'good' : 'needs-work';
|
||
|
||
const goLogin = () => {
|
||
rememberLoginRedirect(currentPath);
|
||
uni.redirectTo({ url: '/pages/auth/login/index' });
|
||
};
|
||
|
||
const openInterview = () => uni.navigateTo({ url: candidateRoutes.interview });
|
||
|
||
const goBack = () => {
|
||
if (getCurrentPages().length <= 1) {
|
||
uni.redirectTo({ url: candidateRoutes.index });
|
||
return;
|
||
}
|
||
uni.navigateBack({ delta: 1, fail: () => uni.redirectTo({ url: candidateRoutes.index }) });
|
||
};
|
||
|
||
onShow(() => {
|
||
resetPageScroll();
|
||
void loadPage();
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.candidate-record-page {
|
||
background: #f9f9f9;
|
||
}
|
||
|
||
.record-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
padding: 10px 4px 16px;
|
||
}
|
||
|
||
.back-button {
|
||
width: 28px;
|
||
min-width: 28px;
|
||
height: 28px;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
color: #111;
|
||
font-size: 24px;
|
||
line-height: 28px;
|
||
}
|
||
|
||
.page-subtitle {
|
||
display: block;
|
||
margin-top: 5px;
|
||
color: #858991;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.score-card,
|
||
.question-card,
|
||
.review-status-card {
|
||
border-radius: 18px;
|
||
background: #fff;
|
||
}
|
||
|
||
.score-card {
|
||
padding: 18px 16px;
|
||
}
|
||
|
||
.score-card-head,
|
||
.question-title-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12px;
|
||
}
|
||
|
||
.score-value {
|
||
display: block;
|
||
color: #e60012;
|
||
font-size: 28px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.score-meta,
|
||
.score-evidence {
|
||
display: block;
|
||
margin-top: 5px;
|
||
color: #858991;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.score-evidence {
|
||
margin-top: 16px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.score-summary {
|
||
display: block;
|
||
margin-top: 12px;
|
||
color: #858991;
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.review-badge {
|
||
padding: 6px 10px;
|
||
border-radius: 16px;
|
||
background: #fff4df;
|
||
color: #d98200;
|
||
font-size: 12px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.question-card {
|
||
margin-top: 14px;
|
||
padding: 18px 16px;
|
||
}
|
||
|
||
.section-title {
|
||
color: #111;
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.question-record {
|
||
padding: 18px 0 0;
|
||
}
|
||
|
||
.question-index {
|
||
display: inline-flex;
|
||
width: 22px;
|
||
height: 22px;
|
||
flex: 0 0 22px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
background: #fff0f1;
|
||
color: #e60012;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.question-title {
|
||
flex: 1;
|
||
color: #111;
|
||
font-size: 14px;
|
||
line-height: 1.45;
|
||
}
|
||
|
||
.answer-text {
|
||
display: block;
|
||
margin: 8px 0 0 34px;
|
||
color: #858991;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.answer-rating {
|
||
display: block;
|
||
margin: 6px 0 0 34px;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.answer-rating.excellent { color: #16a34a; }
|
||
.answer-rating.good { color: #e60012; }
|
||
.answer-rating.needs-work { color: #d98200; }
|
||
|
||
.review-status-card {
|
||
margin-top: 14px;
|
||
padding: 16px;
|
||
background: #eef6ff;
|
||
}
|
||
|
||
.review-status-title {
|
||
display: block;
|
||
color: #111;
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.review-status-desc {
|
||
display: block;
|
||
margin-top: 6px;
|
||
color: #858991;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.home-button {
|
||
width: 100%;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.login-panel,
|
||
.empty-record-panel {
|
||
border-radius: 18px;
|
||
}
|
||
|
||
.block {
|
||
display: block;
|
||
margin-top: 8px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.login-button {
|
||
min-width: 96px;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.question-empty {
|
||
margin-top: 16px;
|
||
}
|
||
</style>
|