218 lines
5.8 KiB
Vue
218 lines
5.8 KiB
Vue
<!-- app/components/dashboard/QaRecordsTable.vue - 最近问答记录表格,基于已准备好的 TableCom -->
|
||
<script lang="ts" setup>
|
||
import { Eye } from "lucide-vue-next";
|
||
import { storeToRefs } from "pinia";
|
||
|
||
import TableCom from "@/components/Table/TableCom.vue";
|
||
import type { TableColumn } from "@/components/Table/type";
|
||
import TooltipCom from "@/components/TooltipCom.vue";
|
||
import { Badge } from "@/components/ui/badge";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
Card,
|
||
CardContent,
|
||
CardDescription,
|
||
CardHeader,
|
||
CardTitle
|
||
} from "@/components/ui/card";
|
||
import {
|
||
Select,
|
||
SelectContent,
|
||
SelectItem,
|
||
SelectTrigger,
|
||
SelectValue
|
||
} from "@/components/ui/select";
|
||
import {
|
||
DASHBOARD_PAGE_SIZE_OPTIONS,
|
||
QUESTION_TYPE_LABELS,
|
||
useAnswerStore
|
||
} from "@/stores/answer";
|
||
import { getOptionLines } from "@/utils";
|
||
|
||
const answerStore = useAnswerStore();
|
||
const {
|
||
pagedRecords,
|
||
recordCount,
|
||
recordsLoading,
|
||
recordsPage,
|
||
recordsPageSize
|
||
} = storeToRefs(answerStore);
|
||
|
||
/** TableCom 列配置集中在组件顶部,便于和其他项目的表格风格保持一致 */
|
||
const columns = computed<TableColumn[]>(() => [
|
||
{
|
||
label: "时间",
|
||
slot: "time",
|
||
width: 170
|
||
},
|
||
{
|
||
label: "题型",
|
||
slot: "type",
|
||
width: 100,
|
||
cellAlign: "center",
|
||
headerAlign: "center"
|
||
},
|
||
{
|
||
label: "题目",
|
||
slot: "question",
|
||
minWidth: 280
|
||
},
|
||
{
|
||
label: "选项",
|
||
slot: "options",
|
||
minWidth: 220
|
||
},
|
||
{
|
||
label: "答案",
|
||
slot: "answer",
|
||
minWidth: 160
|
||
},
|
||
{
|
||
label: "操作",
|
||
slot: "action",
|
||
width: 90,
|
||
cellAlign: "center",
|
||
headerAlign: "center"
|
||
}
|
||
]);
|
||
|
||
/** 题型英文值兜底展示原值,避免未来 OCS 扩展题型时表格为空 */
|
||
const getQuestionTypeLabel = (type: string) => {
|
||
return QUESTION_TYPE_LABELS[type] || type || "自动判断";
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<Card>
|
||
<CardHeader>
|
||
<div class="flex items-center justify-between gap-4">
|
||
<div>
|
||
<CardTitle>最近问答记录</CardTitle>
|
||
<CardDescription>分页展示,按创建时间倒序</CardDescription>
|
||
</div>
|
||
<Select
|
||
:model-value="String(recordsPageSize)"
|
||
@update:model-value="(v) => answerStore.setRecordsPageSize(Number(v))"
|
||
>
|
||
<SelectTrigger class="w-28">
|
||
<SelectValue />
|
||
</SelectTrigger>
|
||
<SelectContent>
|
||
<SelectItem
|
||
v-for="size in DASHBOARD_PAGE_SIZE_OPTIONS"
|
||
:key="size"
|
||
:value="String(size)"
|
||
>
|
||
{{ size }} 条/页
|
||
</SelectItem>
|
||
</SelectContent>
|
||
</Select>
|
||
</div>
|
||
</CardHeader>
|
||
|
||
<CardContent>
|
||
<TableCom
|
||
:data="pagedRecords"
|
||
:columns="columns"
|
||
:load="recordsLoading"
|
||
:buffer="true"
|
||
:pagination="{
|
||
currentPage: recordsPage,
|
||
total: recordCount,
|
||
pageSize: recordsPageSize,
|
||
align: 'end'
|
||
}"
|
||
cell-align="left"
|
||
header-align="start"
|
||
cell-class-name="align-top"
|
||
@page:change="answerStore.setRecordsPage"
|
||
>
|
||
<template #time="{ value }">
|
||
<span class="whitespace-nowrap text-foreground">{{ value }}</span>
|
||
</template>
|
||
|
||
<template #type="{ value }">
|
||
<Badge variant="secondary" class="text-center">
|
||
{{ getQuestionTypeLabel(value) }}
|
||
</Badge>
|
||
</template>
|
||
|
||
<template #question="{ value }">
|
||
<div class="max-w-136 truncate text-foreground" :title="value">
|
||
{{ value }}
|
||
</div>
|
||
</template>
|
||
|
||
<template #options="{ row }">
|
||
<TooltipCom side="top" align="start">
|
||
<template #trigger>
|
||
<div class="max-w-md cursor-pointer truncate">
|
||
<template v-if="row.options">
|
||
<template
|
||
v-for="(line, i) in getOptionLines(
|
||
row.options,
|
||
row.answer ?? ''
|
||
)"
|
||
:key="i"
|
||
>
|
||
<span v-if="i > 0" class="text-muted-foreground">
|
||
|
||
</span>
|
||
<span
|
||
:class="
|
||
line.isCorrect
|
||
? 'text-green-500 font-medium'
|
||
: 'text-muted-foreground'
|
||
"
|
||
>
|
||
{{ line.text }}
|
||
</span>
|
||
</template>
|
||
</template>
|
||
<span v-else class="text-muted-foreground">-</span>
|
||
</div>
|
||
</template>
|
||
<div
|
||
v-if="row.options"
|
||
class="max-w-sm whitespace-pre-wrap px-3 py-1 text-xs leading-5"
|
||
>
|
||
<div
|
||
v-for="(line, i) in getOptionLines(
|
||
row.options,
|
||
row.answer ?? ''
|
||
)"
|
||
:key="i"
|
||
class="whitespace-pre-wrap mb-2 border rounded-lg px-2 py-1 last:mb-0 min-w-16"
|
||
:class="line.isCorrect ? 'text-green-500 font-medium' : ''"
|
||
>
|
||
{{ line.text }}
|
||
</div>
|
||
</div>
|
||
</TooltipCom>
|
||
</template>
|
||
|
||
<template #answer="{ value }">
|
||
<div
|
||
class="max-w-[20rem] truncate font-medium text-foreground"
|
||
:title="value"
|
||
>
|
||
{{ value }}
|
||
</div>
|
||
</template>
|
||
|
||
<template #action="{ row }">
|
||
<Button
|
||
type="button"
|
||
variant="outline"
|
||
size="icon-sm"
|
||
title="查看详情"
|
||
@click.stop="answerStore.selectRecord(row)"
|
||
>
|
||
<Eye class="size-4" />
|
||
</Button>
|
||
</template>
|
||
</TableCom>
|
||
</CardContent>
|
||
</Card>
|
||
</template>
|