feat(aihr): align figma pages for release

This commit is contained in:
2026-07-14 02:04:36 +08:00
parent 922fe7ff29
commit 87360a25d3
51 changed files with 3575 additions and 629 deletions
+131 -2
View File
@@ -89,6 +89,50 @@
</article>
</section>
<section class="panel pending-review-panel">
<div class="panel-title with-actions">
<div class="panel-title-main">
<el-icon><DocumentChecked /></el-icon>
<div>
<h2>答案待复核</h2>
<p>员工反馈为「没解决」的答案会进入这里,复核后从待办队列移除。</p>
</div>
</div>
<div class="review-toolbar">
<el-select v-model="pendingReviewStatus" size="small" @change="loadPendingReviews">
<el-option label="待复核" value="待复核" />
<el-option label="已复核" value="已复核" />
</el-select>
<el-button size="small" :loading="pendingReviewLoading" @click="loadPendingReviews">刷新</el-button>
</div>
</div>
<el-table v-loading="pendingReviewLoading" :data="pendingReviews" size="small" class="record-table">
<el-table-column prop="queryText" label="员工问题" min-width="260" />
<el-table-column prop="verdict" label="反馈" width="90">
<template #default="{ row }">
<span class="review-verdict negative">{{ row.verdict === 'down' ? '没解决' : row.verdict }}</span>
</template>
</el-table-column>
<el-table-column prop="position" label="岗位" width="120" />
<el-table-column prop="createTime" label="提交时间" width="170" />
<el-table-column label="操作" width="110" fixed="right">
<template #default="{ row }">
<el-button
v-if="row.reviewStatus === '待复核'"
link
type="danger"
:loading="reviewingFeedbackId === row.id"
@click="markFeedbackReviewed(row)"
>
标记已复核
</el-button>
<span v-else class="review-done">已复核</span>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!pendingReviewLoading && !pendingReviews.length" description="暂无待复核答案" />
</section>
<section class="panel record-panel">
<div class="panel-title">
<el-icon><Tickets /></el-icon>
@@ -110,9 +154,18 @@
import { DocumentChecked, Reading, Tickets, Upload } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import type { UploadFile } from 'element-plus';
import { computed, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { reviewKnowledgeSop, searchKnowledgeSop, uploadKnowledgeDoc, type SopRecord, type SopSearchResponse } from '@/api/aihr/sop';
import {
listAnswerFeedback,
reviewAnswerFeedback,
reviewKnowledgeSop,
searchKnowledgeSop,
uploadKnowledgeDoc,
type AnswerFeedbackItem,
type SopRecord,
type SopSearchResponse
} from '@/api/aihr/sop';
const router = useRouter();
@@ -212,6 +265,10 @@ const uploading = ref(false);
const uploadedRecords = ref<SopRecord[]>([]);
const reviewSaving = ref(false);
const reviewStatus = ref('');
const pendingReviewStatus = ref('待复核');
const pendingReviewLoading = ref(false);
const reviewingFeedbackId = ref<number | null>(null);
const pendingReviews = ref<AnswerFeedbackItem[]>([]);
const activeSeed = computed(() => sopSeeds[selectedCategory.value]);
const answerText = computed(() => apiResult.value?.answer ?? activeSeed.value.answer);
@@ -318,6 +375,32 @@ async function markReview(usable: boolean) {
}
}
async function loadPendingReviews() {
pendingReviewLoading.value = true;
try {
const { data } = await listAnswerFeedback(pendingReviewStatus.value);
pendingReviews.value = data ?? [];
} catch {
pendingReviews.value = [];
ElMessage.error('答案待复核列表加载失败');
} finally {
pendingReviewLoading.value = false;
}
}
async function markFeedbackReviewed(row: AnswerFeedbackItem) {
reviewingFeedbackId.value = row.id;
try {
await reviewAnswerFeedback(row.id);
pendingReviews.value = pendingReviews.value.filter((item) => item.id !== row.id);
ElMessage.success('答案已标记为已复核');
} catch {
ElMessage.error('答案复核失败');
} finally {
reviewingFeedbackId.value = null;
}
}
async function handleDocSelected(uploadFile: UploadFile) {
const raw = uploadFile.raw;
if (!raw) return;
@@ -359,6 +442,10 @@ async function generateTraining() {
trainingGenerated.value = true;
ElMessage.success('已基于引用片段生成训练题');
}
onMounted(() => {
loadPendingReviews();
});
</script>
<style lang="scss" scoped>
@@ -425,6 +512,48 @@ async function generateTraining() {
}
}
.panel-title.with-actions {
justify-content: space-between;
}
.panel-title-main,
.review-toolbar {
display: flex;
align-items: center;
}
.panel-title-main {
gap: 8px;
p {
margin: 4px 0 0;
color: #657184;
font-size: 12px;
font-weight: 400;
}
}
.review-toolbar {
gap: 8px;
}
.pending-review-panel {
margin-top: 12px;
}
.review-verdict,
.review-done {
font-size: 12px;
}
.review-verdict.negative {
color: #c80f1f;
}
.review-done {
color: #15844a;
}
.category-list {
display: grid;
gap: 8px;