實作 Excel 匯出匯入題目管理
This commit is contained in:
@@ -3,19 +3,20 @@ import * as XLSX from "xlsx"
|
||||
export interface LogicQuestionImport {
|
||||
id: number
|
||||
question: string
|
||||
optionA: string
|
||||
optionB: string
|
||||
optionC: string
|
||||
optionD: string
|
||||
correctAnswer: string
|
||||
explanation?: string
|
||||
option_a: string
|
||||
option_b: string
|
||||
option_c: string
|
||||
option_d: string
|
||||
option_e?: string
|
||||
correct_answer: string
|
||||
explanation: string
|
||||
}
|
||||
|
||||
export interface CreativeQuestionImport {
|
||||
id: number
|
||||
statement: string
|
||||
category: "innovation" | "imagination" | "flexibility" | "originality"
|
||||
isReverse: boolean
|
||||
is_reverse: boolean
|
||||
}
|
||||
|
||||
export interface ImportResult {
|
||||
@@ -25,6 +26,61 @@ export interface ImportResult {
|
||||
errors?: string[]
|
||||
}
|
||||
|
||||
// 匯出功能
|
||||
export function exportLogicQuestionsToExcel(questions: LogicQuestionImport[]): void {
|
||||
const headers = [
|
||||
"題目ID",
|
||||
"題目內容",
|
||||
"選項A",
|
||||
"選項B",
|
||||
"選項C",
|
||||
"選項D",
|
||||
"選項E",
|
||||
"正確答案",
|
||||
"解釋"
|
||||
]
|
||||
|
||||
const data = questions.map(q => [
|
||||
q.id,
|
||||
q.question,
|
||||
q.option_a,
|
||||
q.option_b,
|
||||
q.option_c,
|
||||
q.option_d,
|
||||
q.option_e || "",
|
||||
q.correct_answer,
|
||||
q.explanation
|
||||
])
|
||||
|
||||
const worksheet = XLSX.utils.aoa_to_sheet([headers, ...data])
|
||||
const workbook = XLSX.utils.book_new()
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "邏輯思維題目")
|
||||
|
||||
XLSX.writeFile(workbook, "邏輯思維題目範本.xlsx")
|
||||
}
|
||||
|
||||
export function exportCreativeQuestionsToExcel(questions: CreativeQuestionImport[]): void {
|
||||
const headers = [
|
||||
"題目ID",
|
||||
"陳述內容",
|
||||
"類別",
|
||||
"反向計分"
|
||||
]
|
||||
|
||||
const data = questions.map(q => [
|
||||
q.id,
|
||||
q.statement,
|
||||
q.category,
|
||||
q.is_reverse ? "是" : "否"
|
||||
])
|
||||
|
||||
const worksheet = XLSX.utils.aoa_to_sheet([headers, ...data])
|
||||
const workbook = XLSX.utils.book_new()
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "創意能力題目")
|
||||
|
||||
XLSX.writeFile(workbook, "創意能力題目範本.xlsx")
|
||||
}
|
||||
|
||||
export function parseExcelFile(file: File, type: "logic" | "creative"): Promise<ImportResult> {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader()
|
||||
@@ -78,30 +134,32 @@ function parseLogicQuestions(data: any[][]): ImportResult {
|
||||
const question: LogicQuestionImport = {
|
||||
id: Number.parseInt(row[0]) || i,
|
||||
question: row[1]?.toString() || "",
|
||||
optionA: row[2]?.toString() || "",
|
||||
optionB: row[3]?.toString() || "",
|
||||
optionC: row[4]?.toString() || "",
|
||||
optionD: row[5]?.toString() || "",
|
||||
correctAnswer: row[6]?.toString() || "",
|
||||
explanation: row[7]?.toString() || "",
|
||||
option_a: row[2]?.toString() || "",
|
||||
option_b: row[3]?.toString() || "",
|
||||
option_c: row[4]?.toString() || "",
|
||||
option_d: row[5]?.toString() || "",
|
||||
option_e: row[6]?.toString() || undefined,
|
||||
correct_answer: row[7]?.toString() || "",
|
||||
explanation: row[8]?.toString() || "",
|
||||
}
|
||||
|
||||
// 驗證必填欄位
|
||||
if (
|
||||
!question.question ||
|
||||
!question.optionA ||
|
||||
!question.optionB ||
|
||||
!question.optionC ||
|
||||
!question.optionD ||
|
||||
!question.correctAnswer
|
||||
!question.option_a ||
|
||||
!question.option_b ||
|
||||
!question.option_c ||
|
||||
!question.option_d ||
|
||||
!question.correct_answer
|
||||
) {
|
||||
errors.push(`第 ${i + 1} 行:缺少必填欄位`)
|
||||
continue
|
||||
}
|
||||
|
||||
// 驗證正確答案格式
|
||||
if (!["A", "B", "C", "D"].includes(question.correctAnswer.toUpperCase())) {
|
||||
errors.push(`第 ${i + 1} 行:正確答案必須是 A、B、C 或 D`)
|
||||
const validAnswers = question.option_e ? ["A", "B", "C", "D", "E"] : ["A", "B", "C", "D"]
|
||||
if (!validAnswers.includes(question.correct_answer.toUpperCase())) {
|
||||
errors.push(`第 ${i + 1} 行:正確答案必須是 ${validAnswers.join("、")}`)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -141,7 +199,7 @@ function parseCreativeQuestions(data: any[][]): ImportResult {
|
||||
id: Number.parseInt(row[0]) || i,
|
||||
statement: row[1]?.toString() || "",
|
||||
category: (row[2]?.toString().toLowerCase() as any) || "innovation",
|
||||
isReverse: row[3]?.toString().toLowerCase() === "是" || row[3]?.toString().toLowerCase() === "true",
|
||||
is_reverse: row[3]?.toString().toLowerCase() === "是" || row[3]?.toString().toLowerCase() === "true",
|
||||
}
|
||||
|
||||
// 驗證必填欄位
|
||||
|
Reference in New Issue
Block a user