-
+
所有用户可见
@@ -57,31 +57,31 @@ export function KnowledgeCardFields({
2-3 句趣味解读,让用户答完题后学到新知识
- {basicError && (
-
{basicError}
+ {summaryError && (
+
{summaryError}
)}
- {basicCount}/{BASIC_MAX}
+ {summaryCount}/{SUMMARY_MAX}
- {/* 深度版 */}
+ {/* 深度解析 */}
-
{QUESTION_SOURCE_LABELS[question.source]}
+
{question.source ? QUESTION_SOURCE_LABELS[question.source] : "—"}
@@ -108,7 +108,7 @@ export function UgcReviewDialog({
{/* 题干 */}
-
{question.stem}
+
{(question.stem as { text: string }).text}
{/* 正确答案 */}
@@ -130,37 +130,50 @@ export function UgcReviewDialog({
{/* 知识卡 */}
-
-
-
{question.knowledgeCardBasic}
-
+ {question.knowledgeCard && (
+ <>
+
+
+
{question.knowledgeCard.summary}
+
- {question.knowledgeCardDeep && (
-
-
-
- {question.knowledgeCardDeep}
-
-
+ {question.knowledgeCard.deepDive && (
+
+
+
+ {question.knowledgeCard.deepDive}
+
+
+ )}
+
+ {question.knowledgeCard.sourceRef && (
+
+
+
{question.knowledgeCard.sourceRef}
+
+ )}
+ >
)}
{/* 统计信息 */}
-
-
-
-
{question.stats.timesAnswered}
+ {question.stats && (
+
+
+
+
{question.stats.timesAnswered}
+
+
+
+
{(question.stats.correctRate * 100).toFixed(0)}%
+
+
+
+
{(question.stats.avgTimeMs / 1000).toFixed(1)}秒
+
-
-
-
{(question.stats.correctRate * 100).toFixed(0)}%
-
-
-
-
{(question.stats.avgTimeMs / 1000).toFixed(1)}秒
-
-
+ )}
diff --git a/src/components/question/columns.tsx b/src/components/question/columns.tsx
index d0f1efb..e4248b6 100644
--- a/src/components/question/columns.tsx
+++ b/src/components/question/columns.tsx
@@ -57,10 +57,11 @@ export function getColumns(ctx: ColumnContext): ColumnDef
[] {
accessorKey: "stem",
header: "题干",
cell: ({ row }) => {
- const stem = row.getValue("stem") as string
+ const stem = row.getValue("stem") as { text: string } | undefined
+ const text = stem?.text ?? ""
return (
-
- {stem.length > 60 ? stem.slice(0, 60) + "..." : stem}
+
+ {text.length > 60 ? text.slice(0, 60) + "..." : text}
)
},
@@ -82,7 +83,8 @@ export function getColumns(ctx: ColumnContext): ColumnDef[] {
accessorKey: "source",
header: "来源",
cell: ({ row }) => {
- const source = row.getValue("source") as "system" | "ugc"
+ const source = row.getValue("source") as "system" | "ugc" | undefined
+ if (!source) return —
return (
{QUESTION_SOURCE_LABELS[source]}
@@ -136,10 +138,11 @@ export function getColumns(ctx: ColumnContext): ColumnDef[] {
id: "stats",
header: "统计",
cell: ({ row }) => {
- const { timesAnswered, correctRate } = row.original.stats
+ const stats = row.original.stats
+ if (!stats) return —
return (
- {timesAnswered} 次 · {(correctRate * 100).toFixed(0)}%
+ {stats.timesAnswered} 次 · {(stats.correctRate * 100).toFixed(0)}%
)
},
diff --git a/src/lib/api/question-api.ts b/src/lib/api/question-api.ts
index 71ef5d0..cc38bc4 100644
--- a/src/lib/api/question-api.ts
+++ b/src/lib/api/question-api.ts
@@ -13,13 +13,13 @@ import type {
export interface FetchQuestionsParams {
page?: number
limit?: number
- search?: string
+ keyword?: string
status?: QuestionStatus
categoryId?: string
difficulty?: Difficulty
source?: "system" | "ugc"
- sort?: "createdAt" | "difficulty" | "updatedAt"
- order?: "asc" | "desc"
+ sortBy?: "createdAt" | "difficulty" | "updatedAt"
+ sortOrder?: "asc" | "desc"
}
export async function fetchQuestions(
@@ -28,13 +28,13 @@ export async function fetchQuestions(
const searchParams = new URLSearchParams()
if (params.page) searchParams.set("page", String(params.page))
if (params.limit) searchParams.set("limit", String(params.limit))
- if (params.search) searchParams.set("search", params.search)
+ if (params.keyword) searchParams.set("keyword", params.keyword)
if (params.status) searchParams.set("status", params.status)
if (params.categoryId) searchParams.set("categoryId", params.categoryId)
if (params.difficulty) searchParams.set("difficulty", String(params.difficulty))
if (params.source) searchParams.set("source", params.source)
- if (params.sort) searchParams.set("sort", params.sort)
- if (params.order) searchParams.set("order", params.order)
+ if (params.sortBy) searchParams.set("sortBy", params.sortBy)
+ if (params.sortOrder) searchParams.set("sortOrder", params.sortOrder)
return apiClient
.get("questions", { searchParams })
diff --git a/src/routes/questions/index.tsx b/src/routes/questions/index.tsx
index 3e1c5a1..3e9ff8f 100644
--- a/src/routes/questions/index.tsx
+++ b/src/routes/questions/index.tsx
@@ -126,15 +126,15 @@ export default function QuestionsPage() {
const res = await fetchQuestions({
page,
limit: PAGE_SIZE,
- search: search || undefined,
+ keyword: search || undefined,
status: statusFilter !== "all" ? statusFilter : undefined,
categoryId: categoryFilter !== "all" ? categoryFilter : undefined,
difficulty: difficultyFilter !== "all"
? (Number(difficultyFilter) as Difficulty)
: undefined,
source: sourceTab !== "all" ? sourceTab : undefined,
- sort: sortField,
- order: sortOrder,
+ sortBy: sortField,
+ sortOrder,
})
setQuestions(res.data)
setTotal(res.pagination.total)
@@ -289,14 +289,14 @@ export default function QuestionsPage() {
try {
const res = await fetchQuestions({
limit: 10000,
- search: search || undefined,
+ keyword: search || undefined,
status: statusFilter !== "all" ? statusFilter : undefined,
categoryId: categoryFilter !== "all" ? categoryFilter : undefined,
difficulty: difficultyFilter !== "all"
? (Number(difficultyFilter) as Difficulty)
: undefined,
- sort: sortField,
- order: sortOrder,
+ sortBy: sortField,
+ sortOrder,
})
exportToCsv("questions.csv", [
{ key: "stem", label: "题干" },
diff --git a/src/types/question.ts b/src/types/question.ts
index 5fe050d..a750969 100644
--- a/src/types/question.ts
+++ b/src/types/question.ts
@@ -1,43 +1,60 @@
export type QuestionStatus = "draft" | "reviewing" | "published" | "archived"
export type Difficulty = 1 | 2 | 3 | 4 | 5
+export type QuestionContentType = "text" | "image" | "video" | "audio"
+
+/** Stem structure matching duoqi-api — at least `text` field */
+export interface QuestionStem {
+ text: string
+ [key: string]: unknown
+}
+
+/** Knowledge card nested object matching duoqi-api */
+export interface KnowledgeCard {
+ id?: string
+ summary: string
+ deepDive?: string
+ sourceRef?: string
+}
export interface Question {
id: string
- stem: string
+ stem: QuestionStem
+ contentType: QuestionContentType
correctAnswer: string
distractors: string[]
categoryId: string
difficulty: Difficulty
status: QuestionStatus
- knowledgeCardBasic: string
- knowledgeCardDeep?: string
- sourceRef?: string
- source: "system" | "ugc"
- stats: {
+ knowledgeCard?: KnowledgeCard
+ /** Optional — not returned by list endpoint, may be present in detail */
+ source?: "system" | "ugc"
+ /** Optional — not currently returned by API, reserved for future use */
+ stats?: {
timesAnswered: number
correctRate: number
avgTimeMs: number
}
- createdAt: string
+ createdAt?: string
updatedAt: string
}
+/** Payload for creating / updating a question */
export interface QuestionFormData {
- stem: string
+ stem: { text: string }
+ contentType: QuestionContentType
correctAnswer: string
distractors: string[]
categoryId: string
difficulty: Difficulty
- status: QuestionStatus
- knowledgeCardBasic: string
- knowledgeCardDeep?: string
- sourceRef?: string
+ knowledgeCard?: {
+ summary: string
+ deepDive?: string
+ sourceRef?: string
+ }
}
// ── Import API types (match duoqi-api spec) ──
-export type QuestionContentType = "text" | "image" | "video" | "audio"
-
export interface ImportQuestionItem {
stem: { text: string }
contentType: QuestionContentType