feat: 评分标准 JSON 导入/导出
This commit is contained in:
@@ -283,6 +283,37 @@ export function setupIpc(win: BrowserWindow): void {
|
|||||||
return null
|
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) ---
|
// --- Grading data bridge (toolbar → result window) ---
|
||||||
ipcMain.handle('grading:storeData', (_event, data: any) => {
|
ipcMain.handle('grading:storeData', (_event, data: any) => {
|
||||||
gradingPayload = data
|
gradingPayload = data
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
|
|
||||||
rubric: {
|
rubric: {
|
||||||
storeData: (data: any) => ipcRenderer.invoke('rubric:storeData', data),
|
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: {
|
template: {
|
||||||
|
|||||||
@@ -34,8 +34,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button size="small" @click="handleClose">取消</el-button>
|
<div class="footer-left">
|
||||||
<el-button type="primary" size="small" @click="handleSave">应用</el-button>
|
<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>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</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() {
|
function handleClose() {
|
||||||
window.electronAPI?.dialog.close()
|
window.electronAPI?.dialog.close()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
:deep(.el-dialog__footer) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
.question-tabs {
|
.question-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -136,4 +169,12 @@ function handleClose() {
|
|||||||
max-height: 480px;
|
max-height: 480px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
.footer-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.footer-right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -81,6 +81,8 @@ export interface ElectronAPI {
|
|||||||
rubric: {
|
rubric: {
|
||||||
storeData: (data: any) => Promise<void>
|
storeData: (data: any) => Promise<void>
|
||||||
getData: () => Promise<any>
|
getData: () => Promise<any>
|
||||||
|
exportJson: () => Promise<boolean>
|
||||||
|
importJson: () => Promise<{ questions: QuestionItem[]; currentIndex: number } | null>
|
||||||
}
|
}
|
||||||
template: {
|
template: {
|
||||||
list: () => Promise<unknown[]>
|
list: () => Promise<unknown[]>
|
||||||
|
|||||||
Reference in New Issue
Block a user