feat:更新知识库对话框

This commit is contained in:
2026-07-28 19:50:21 +08:00
parent 0d196edd28
commit efa27f126d
10 changed files with 337 additions and 403 deletions

View File

@@ -7,4 +7,5 @@
<style lang="scss">
@use '@/styles/index.module.scss';
</style>
@use "@/styles/common.module.scss";
</style>

View File

@@ -1,7 +1,7 @@
<template>
<el-dialog v-model="libraryStore.fileDetailDialogVisible"
:title="libraryStore.currentFile.name" center
width="90%" destroy-on-close class="file-dialog">
width="90%" destroy-on-close class="file-detail-dialog">
<el-splitter>
<el-splitter-panel size="50%">
<div class="raw-file">
@@ -24,7 +24,7 @@
<span class="q-num">Q{{ i + 1 }}</span>
<el-tag size="small" type="primary" effect="dark">{{ q.type }}</el-tag>
</div>
<div class="q-body">{{ q.stem }}</div>
<div class="q-body">{{ q.question }}</div>
<ul v-if="q.options" class="q-options">
<li v-for="opt in q.options" :key="opt">{{ opt }}</li>
</ul>
@@ -51,7 +51,7 @@ const libraryStore = useLibraryStore()
</script>
<style lang="scss">
.file-dialog {
.file-detail-dialog {
.raw-file {
height: 100%;
padding: 10px;

View File

@@ -1,69 +1,98 @@
<template>
<el-dialog v-model="libraryStore.fileDialogVisible"
title="新增资料" width="80%" center destroy-on-close>
<el-splitter>
<el-splitter-panel>
<el-form ref="ruleFormRef" :model="libraryStore.currentFile"
:rules="rules" label-width="auto">
<el-form-item label="学科" prop="subject">
<el-select v-model="libraryStore.currentFile.subject"
multiple filterable allow-create
placeholder="请输入或选择学科名称" style="width: 240px">
<el-option v-for="(item, index) in subjectOptions"
:key="index" :label="item" :value="item"/>
</el-select>
</el-form-item>
title="新增资料" width="80%"
center destroy-on-close
class="file-dialog common-dialog">
<el-steps :active="activeStep" align-center>
<el-step title="上传资料" :icon="Upload" />
<el-step title="确认资料" :icon="Document " />
<el-step title="生成题库" :icon="MagicStick" />
</el-steps>
<el-form-item label="模块" prop="module">
<el-select v-model="libraryStore.currentFile.module"
multiple filterable allow-create
placeholder="请输入或选择模块名称" style="width: 240px">
<el-option v-for="(item, index) in subjectOptions"
:key="index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form ref="libraryFileFormRef" v-if="activeStep === 1"
:model="libraryStore.currentFile"
:rules="rules" label-width="auto">
<el-form-item label="学科" prop="subject">
<el-select v-model="libraryStore.currentFile.subject"
filterable allow-create placeholder="请输入或选择学科名称">
<el-option v-for="(item, index) in subjectOptions"
:key="index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item label="文件">
<el-upload class="upload-demo"
drag
:http-request="upload">
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
Drop file here or <em>click to upload</em>
</div>
<template #tip>
<div class="el-upload__tip">
jpg/png files with a size less than 500kb
</div>
</template>
</el-upload>
</el-form-item>
</el-form>
</el-splitter-panel>
<el-splitter-panel>
<el-input
v-model="fileContent"
style="width: 100%;padding: 10px;"
:rows="20"
type="textarea"
placeholder="Please input"
/>
</el-splitter-panel>
<el-splitter-panel>
<el-form-item label="模块" prop="module">
<el-select v-model="libraryStore.currentFile.module"
filterable allow-create placeholder="请输入或选择模块名称">
<el-option v-for="(item, index) in subjectOptions"
:key="index" :label="item" :value="item"/>
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="libraryStore.currentFile.name"
show-word-limit :maxlength="20"
placeholder="请输入资料名称">
</el-input>
</el-form-item>
<el-form-item label="文件">
<el-upload class="upload-demo" drag :http-request="upload">
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
拖拽文件至此或 <em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
请上传小于10M的图片
</div>
</template>
</el-upload>
</el-form-item>
</el-form>
<QuillEditor v-if="activeStep === 2" :content="fileContent" contentType="html" theme="snow"/>
<div v-if="activeStep === 3" class="generate-question">
<div>
<el-button type="primary" @click="generate">生成题库</el-button>
<el-input
v-model="questionContent"
style="width: 100%;padding: 10px;"
:rows="20"
type="textarea"
placeholder="Please input"
/>
</el-splitter-panel>
</el-splitter>
</div>
<div v-if="hasGenerate" class="question-list">
<el-card v-for="(q, index) in libraryStore.currentFile.questions"
:key="q.id" class="question-card">
<div class="question-title">
{{ index + 1 }}.
{{ q.type === 'single' ? '单选' : '多选' }}
{{ q.question }}
</div>
<div class="question-options">
<div v-for="(opt, i) in q.options" :key="i" style="line-height: 1.8">
{{ String.fromCharCode(65 + i) }}. {{ opt }}
</div>
</div>
<div class="question-answer">
正确答案{{ q.answer.join('、') }}
</div>
<div class="question-action">
<el-button type="danger" size="small"
@click="libraryStore.currentFile.questions.splice(index, 1)">
删除
</el-button>
</div>
</el-card>
</div>
<div v-else v-html="questionContent" />
</div>
<template #footer>
<el-button @click="libraryStore.fileDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm(ruleFormRef)">确认</el-button>
<el-button type="primary" @click="previousClick" :disabled="activeStep === 1">上一步</el-button>
<el-button type="primary" @click="nextClick" :disabled="activeStep === 3">下一步</el-button>
<el-button type="primary" @click="submitForm()" :disabled="activeStep !== 3">确认</el-button>
</template>
</el-dialog>
</template>
@@ -71,13 +100,17 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useLibraryStore } from '@/stores/library.ts'
import { ElMessage, type FormInstance, type UploadRequestOptions } from 'element-plus'
import { UploadFilled } from '@element-plus/icons-vue'
import { ElLoading, ElMessage, type FormInstance, type UploadRequestOptions } from 'element-plus'
import { Document, MagicStick, Upload, UploadFilled } from '@element-plus/icons-vue'
import axios from 'axios'
import { QuillEditor } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css'
const libraryStore = useLibraryStore()
const ruleFormRef = ref<FormInstance>()
const libraryFileFormRef = ref<FormInstance>()
const activeStep = ref(1)
const hasGenerate = ref(false)
const subjectOptions = ['中医学', '公务员']
@@ -90,15 +123,47 @@ const rules = {
],
module: [
{ required: true, message: '请输入模块名称', trigger: 'blur' }
],
name: [
{ required: true, message: '请输入资料名称', trigger: 'blur' }
]
}
const previousClick = () => {
if (activeStep.value > 1) {
activeStep.value--
}
}
const nextClick = async () => {
if (activeStep.value === 1) {
try {
await libraryFileFormRef.value?.validate()
} catch {
ElMessage.error('请先输入信息')
return
}
}
if (activeStep.value < 3) {
activeStep.value++
}
}
const upload = async (options: UploadRequestOptions) => {
const { file } = options
libraryStore.currentFile.size = file.size
libraryStore.currentFile.type = 'image'
const formData = new FormData()
formData.append('file', file)
const loading = ElLoading.service({
lock: true,
text: '文件上传中 请等待',
background: 'rgba(0, 0, 0, 0.7)'
})
try {
const res = await axios.post('http://127.0.0.1:8000/upload', formData, {
headers: {
@@ -107,9 +172,11 @@ const upload = async (options: UploadRequestOptions) => {
})
ElMessage.success('上传成功')
fileContent.value = res.data.text
} catch (err) {
ElMessage.error('上传失败')
console.error(err)
} catch (error) {
console.log(error)
ElMessage.error('上传失败 请联系管理员')
} finally {
loading.close()
}
}
@@ -120,6 +187,13 @@ const generate = async () => {
}
questionContent.value = ''
hasGenerate.value = false
const loading = ElLoading.service({
lock: true,
text: '题库生成中 请等待',
background: 'rgba(0, 0, 0, 0.7)'
})
try {
const response = await fetch('http://127.0.0.1:8000/generate', {
@@ -146,24 +220,77 @@ const generate = async () => {
}
ElMessage.success('生成完成')
} catch (err) {
ElMessage.error('生成失败')
console.error(err)
hasGenerate.value = true
libraryStore.currentFile.questions = JSON.parse(questionContent.value)
} catch (error) {
console.log(error)
ElMessage.error('生成失败 请联系管理员')
} finally {
loading.close()
}
}
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid, fields) => {
if (valid) {
libraryStore.fileDialogVisible = false
} else {
console.log('请先输入信息')
}
})
const submitForm = async () => {
if (libraryStore.currentFile.questions.length > 0) {
const res = await axios.post('http://127.0.0.1:8000/files', libraryStore.currentFile, {
headers: {
'Content-Type': 'application/json'
}
})
} else {
ElMessage.error('请先生成题库')
}
}
</script>
<style lang="scss">
.file-dialog {
.el-dialog__body {
display: flex;
flex-direction: column;
gap: 16px;
.ql-container {
height: auto;
}
.generate-question {
display: flex;
flex-direction: column;
gap: 10px;
}
.question-list {
display: flex;
flex-direction: column;
gap: 10px;
.question-card {
.el-card__body {
display: flex;
flex-direction: column;
gap: 10px;
}
.question-title {
font-weight: 600;
}
.question-options {
padding-left: 16px;
}
.question-answer {
color: #67c23a;
font-size: 13px;
}
.question-action {
text-align: right;
}
}
}
}
}
</style>

View File

@@ -9,7 +9,7 @@
</div>
<div class="file-text">
<div class="file-name">{{ file.name }}</div>
<div class="file-meta">{{ file.size }} · {{ file.time }}</div>
<div class="file-meta">{{ file.size }} · {{ file.uploadTime }}</div>
</div>
</div>

View File

@@ -21,4 +21,4 @@ app.use(createPinia())
// 使用Element UI Plus
app.use(ElementPlus, { locale: zhCn })
// 挂载到根组件上
app.mount('#app')
app.mount('#app')

View File

@@ -59,13 +59,11 @@ export const useLibraryStore = defineStore('library', {
subject: '中医学',
module: '中医基础理疗',
name: '向量代数.pdf',
size: '2.4 MB',
time: '2分钟前',
size: 2.4,
uploadTime: '2分钟前',
type: 'pdf',
iconClass: 'icon-pdf',
questions: [
{ type: '选择题', stem: '设 a=(1,2,3), b=(2,-1,1),求 a·b = ?', options: ['A. 3', 'B. 5', 'C. 7', 'D. 9'], answer: 'A' },
{ type: '计算题', stem: '求过点 P(1,0,2) 且法向量为 n=(2,1,-1) 的平面方程', answer: '2x + y - z = 0' }
{ id: 1, type: 'single', question: '设 a=(1,2,3), b=(2,-1,1),求 a·b = ?', options: ['A. 3', 'B. 5', 'C. 7', 'D. 9'], answer: ['A'] }
]
},
{
@@ -73,23 +71,23 @@ export const useLibraryStore = defineStore('library', {
subject: '中医学',
module: '中医基础理疗',
name: '向量代数精讲.png',
size: '156 MB',
time: '3小时前',
size: 156,
uploadTime: '3小时前',
type: 'image',
iconClass: 'icon-image'
questions: []
}
]
},
getEmptyFile(): ILibraryFile {
return {
iconClass: '',
id: 0,
module: '',
name: '',
size: '',
size: 0,
subject: '',
time: '',
type: ''
uploadTime: '',
type: '',
questions: []
}
}
}

View File

@@ -0,0 +1,17 @@
.common-view {
}
.common-dialog {
--el-dialog-margin-top: 0 !important;
margin: 0 auto !important;
height: 100vh;
display: flex;
flex-direction: column;
.el-dialog__body {
flex-grow: 1;
overflow: auto;
}
}

View File

@@ -5,10 +5,11 @@ export interface ITreeNode {
}
export interface ILibraryFileQuestion {
id: number;
type: string;
stem: string;
options?: string[];
answer: string;
question: string;
options: string[];
answer: string[];
}
export interface ILibraryFile {
@@ -16,9 +17,8 @@ export interface ILibraryFile {
subject: string;
module: string;
name: string;
size: string;
time: string;
size: number;
uploadTime: string;
type: string;
iconClass: string;
questions?: ILibraryFileQuestion[];
questions: ILibraryFileQuestion[];
}