Initial MVP for QA asset backend
This commit is contained in:
126
hy_qa_asset_backend/frontend/components/StandardQATable.tsx
Normal file
126
hy_qa_asset_backend/frontend/components/StandardQATable.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
"use client";
|
||||
|
||||
import { Ban, CheckCircle2, PauseCircle, Pencil, RotateCw } from "lucide-react";
|
||||
import { api, formatDateTime } from "@/lib/api";
|
||||
import type { StandardQAItem } from "@/lib/types";
|
||||
import RiskBadge from "./RiskBadge";
|
||||
import StatusBadge from "./StatusBadge";
|
||||
|
||||
export default function StandardQATable({ items, onChanged }: { items: StandardQAItem[]; onChanged: () => void }) {
|
||||
async function action(id: number, path: string) {
|
||||
try {
|
||||
await api.post(`/standard-qa/${id}/${path}`, {});
|
||||
onChanged();
|
||||
} catch (error) {
|
||||
alert(error instanceof Error ? error.message : "操作失败");
|
||||
}
|
||||
}
|
||||
|
||||
async function edit(item: StandardQAItem) {
|
||||
const standardQuestion = window.prompt("标准问题", item.standard_question);
|
||||
if (standardQuestion === null) return;
|
||||
const standardAnswer = window.prompt("标准回答", item.standard_answer);
|
||||
if (standardAnswer === null) return;
|
||||
await api.patch(`/standard-qa/${item.id}`, {
|
||||
standard_question: standardQuestion,
|
||||
standard_answer: standardAnswer
|
||||
});
|
||||
onChanged();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="table-wrap rounded-md border border-line bg-white">
|
||||
<table className="min-w-[1350px] w-full border-collapse text-left text-sm">
|
||||
<thead className="bg-panel text-xs uppercase text-slate-500">
|
||||
<tr>
|
||||
<th className="px-4 py-3">标准问题</th>
|
||||
<th className="px-4 py-3">标准回答</th>
|
||||
<th className="px-4 py-3">相似问法</th>
|
||||
<th className="px-4 py-3">主题</th>
|
||||
<th className="px-4 py-3">标签</th>
|
||||
<th className="px-4 py-3">阶段</th>
|
||||
<th className="px-4 py-3">适用人群</th>
|
||||
<th className="px-4 py-3">边界</th>
|
||||
<th className="px-4 py-3">禁止表达</th>
|
||||
<th className="px-4 py-3">来源</th>
|
||||
<th className="px-4 py-3">状态</th>
|
||||
<th className="px-4 py-3">更新</th>
|
||||
<th className="px-4 py-3">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item) => (
|
||||
<tr key={item.id} className="border-t border-line align-top">
|
||||
<td className="max-w-sm px-4 py-3 font-medium text-ink">{item.standard_question}</td>
|
||||
<td className="max-w-md whitespace-pre-wrap px-4 py-3">{item.standard_answer}</td>
|
||||
<td className="px-4 py-3">{item.similar_questions.join("、") || "-"}</td>
|
||||
<td className="px-4 py-3">{item.primary_topic}</td>
|
||||
<td className="px-4 py-3">{item.problem_tags.join("、") || "-"}</td>
|
||||
<td className="px-4 py-3">{item.course_stage}</td>
|
||||
<td className="px-4 py-3">{item.audience_tags.join("、") || "-"}</td>
|
||||
<td className="max-w-xs px-4 py-3">{item.answer_boundary ?? "-"}</td>
|
||||
<td className="px-4 py-3">{item.forbidden_expressions.join("、") || "-"}</td>
|
||||
<td className="px-4 py-3">
|
||||
QA #{item.source_raw_qa_id}
|
||||
<br />
|
||||
场次 #{item.session_id}
|
||||
</td>
|
||||
<td className="space-y-2 px-4 py-3">
|
||||
<StatusBadge status={item.audit_status} />
|
||||
<StatusBadge status={item.call_status} />
|
||||
<RiskBadge level={item.risk_level} />
|
||||
</td>
|
||||
<td className="px-4 py-3">{formatDateTime(item.updated_at)}</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button
|
||||
onClick={() => edit(item)}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-line px-2 text-xs hover:bg-slate-50"
|
||||
>
|
||||
<Pencil className="h-3.5 w-3.5" aria-hidden />
|
||||
编辑
|
||||
</button>
|
||||
<button
|
||||
onClick={() => action(item.id, "mark-callable")}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-emerald-200 px-2 text-xs text-emerald-700 hover:bg-emerald-50"
|
||||
>
|
||||
<CheckCircle2 className="h-3.5 w-3.5" aria-hidden />
|
||||
可调用
|
||||
</button>
|
||||
<button
|
||||
onClick={() => action(item.id, "mark-not-callable")}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-line px-2 text-xs hover:bg-slate-50"
|
||||
>
|
||||
<PauseCircle className="h-3.5 w-3.5" aria-hidden />
|
||||
暂不调用
|
||||
</button>
|
||||
<button
|
||||
onClick={() => action(item.id, "need-review")}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-line px-2 text-xs hover:bg-slate-50"
|
||||
>
|
||||
<RotateCw className="h-3.5 w-3.5" aria-hidden />
|
||||
复审
|
||||
</button>
|
||||
<button
|
||||
onClick={() => action(item.id, "disable")}
|
||||
className="inline-flex h-8 items-center gap-1 rounded-md border border-rose-200 px-2 text-xs text-rose-700 hover:bg-rose-50"
|
||||
>
|
||||
<Ban className="h-3.5 w-3.5" aria-hidden />
|
||||
禁用
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{items.length === 0 ? (
|
||||
<tr>
|
||||
<td className="px-4 py-8 text-center text-slate-500" colSpan={13}>
|
||||
暂无标准问答
|
||||
</td>
|
||||
</tr>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user