feat: 评分标准 JSON 导入/导出

This commit is contained in:
2026-07-16 06:40:38 +08:00
parent 98191b4372
commit d5b5ce2ffe
4 changed files with 79 additions and 3 deletions
+31
View File
@@ -283,6 +283,37 @@ export function setupIpc(win: BrowserWindow): void {
return null
})
ipcMain.handle('rubric:exportJson', async () => {
const d = getDb()
const rows: any[] = d.prepare('SELECT * FROM rubric ORDER BY sort_order').all()
if (!rows.length) throw new Error('暂无评分标准可导出')
const questions = rows.map(r => ({
questionTitle: r.question_title,
maxScore: r.max_score,
referenceAnswer: r.reference_answer,
rubric: r.rubric
}))
const result = await dialogBox.showSaveDialog({
defaultPath: '评分标准.json',
filters: [{ name: 'JSON', extensions: ['json'] }]
})
if (result.canceled || !result.filePath) return false
fs.writeFileSync(result.filePath, JSON.stringify(questions, null, 2), 'utf-8')
return true
})
ipcMain.handle('rubric:importJson', async () => {
const result = await dialogBox.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'JSON', extensions: ['json'] }]
})
if (result.canceled || result.filePaths.length === 0) return null
const content = fs.readFileSync(result.filePaths[0], 'utf-8')
const questions = JSON.parse(content)
if (!Array.isArray(questions)) throw new Error('JSON 格式错误:应为数组')
return { questions, currentIndex: 0 }
})
// --- Grading data bridge (toolbar → result window) ---
ipcMain.handle('grading:storeData', (_event, data: any) => {
gradingPayload = data
+3 -1
View File
@@ -50,7 +50,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
rubric: {
storeData: (data: any) => ipcRenderer.invoke('rubric:storeData', data),
getData: () => ipcRenderer.invoke('rubric:getData')
getData: () => ipcRenderer.invoke('rubric:getData'),
exportJson: () => ipcRenderer.invoke('rubric:exportJson'),
importJson: () => ipcRenderer.invoke('rubric:importJson')
},
template: {
+43 -2
View File
@@ -34,8 +34,14 @@
</div>
<template #footer>
<el-button size="small" @click="handleClose">取消</el-button>
<el-button type="primary" size="small" @click="handleSave">应用</el-button>
<div class="footer-left">
<el-button size="small" @click="handleImport">导入</el-button>
<el-button size="small" @click="handleExport">导出</el-button>
</div>
<div class="footer-right">
<el-button size="small" @click="handleClose">取消</el-button>
<el-button type="primary" size="small" @click="handleSave">应用</el-button>
</div>
</template>
</el-dialog>
</template>
@@ -96,12 +102,39 @@ async function handleSave() {
}
}
async function handleExport() {
try {
await window.electronAPI?.rubric.exportJson()
ElMessage.success('导出成功')
} catch (err) {
ElMessage.error('导出失败:' + (err as Error).message)
}
}
async function handleImport() {
try {
const data = await window.electronAPI?.rubric.importJson()
if (data?.questions?.length) {
questions.value = JSON.parse(JSON.stringify(data.questions))
activeIndex.value = 0
ElMessage.success(`已导入 ${data.questions.length}`)
}
} catch (err) {
ElMessage.error('导入失败:' + (err as Error).message)
}
}
function handleClose() {
window.electronAPI?.dialog.close()
}
</script>
<style scoped>
:deep(.el-dialog__footer) {
display: flex;
justify-content: space-between;
align-items: center;
}
.question-tabs {
display: flex;
flex-direction: column;
@@ -136,4 +169,12 @@ function handleClose() {
max-height: 480px;
overflow-y: auto;
}
.footer-left {
display: flex;
gap: 8px;
}
.footer-right {
display: flex;
gap: 8px;
}
</style>
+2
View File
@@ -81,6 +81,8 @@ export interface ElectronAPI {
rubric: {
storeData: (data: any) => Promise<void>
getData: () => Promise<any>
exportJson: () => Promise<boolean>
importJson: () => Promise<{ questions: QuestionItem[]; currentIndex: number } | null>
}
template: {
list: () => Promise<unknown[]>