Files
pengchen-exam-vue/src/views/paper/question/MysqlForm.vue

1234 lines
40 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="edit-dialog">
<Dialog v-model="dialogVisible" :title="dialogTitle" width="85%" top="10vh" class="custom-dialog">
<el-scrollbar>
<div class="main">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="80px"
>
<el-row>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="启用状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="'0'">启用</el-radio>
<el-radio :label="'1'">禁用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="专业" prop="specialtyName">
<el-input v-model="formData.specialtyName" placeholder="请输入专业" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="章节名称" prop="chapteridDictText">
<el-input v-model="formData.chapteridDictTextVo" placeholder="请输入章节名称" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="课程" prop="courseName">
<el-input v-model="formData.courseName" placeholder="请输入课程" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<!-- <el-form-item label="题型难度" prop="quLevel">
<el-input v-model="formData.quLevel" placeholder="请输入题型难度" />
</el-form-item> -->
<el-form-item label="题型难度" prop="quLevel">
<el-select
v-model="formData.quLevel"
placeholder="请选择题型难度"
clearable
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.EXAM_QUE_DIFF)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="题型" prop="subjectName">
<el-input v-model="formData.subjectName" placeholder="请输入题型" disabled/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="知识点" prop="pointNames">
<el-input
v-model="formData.pointNamesVo"
placeholder="请选择知识点"
readonly
@click="openPoints()"
/>
</el-form-item>
</el-col>
<el-dialog v-model="dialogVisiblePoints" title="选择知识点" width="30%">
<el-tree
ref="treeRef"
:data="deptList"
node-key="id"
:props="{ label: 'name', children: 'children' }"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-dialog>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="数据库名" prop="tname">
<el-input v-model="formData.tname" placeholder="请输入数据库名" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
<div class="edit-left bottom-common">
<el-tabs v-model="leftActiveName" class="demo-tabs">
<el-tab-pane label="试题描述" name="desc">
<div class="block">
<Editor v-model="formData.content" height="250px" />
</div>
</el-tab-pane>
</el-tabs>
</div>
<div class="edit-right bottom-common">
<el-tabs v-model="rightActiveName" class="demo-tabs" @tab-click="rightHandleClick">
<el-tab-pane label="试题解析" name="analysis">
<div class="block">
<Editor v-model="formData.analysis" height="150px" />
</div>
</el-tab-pane>
<el-tab-pane name="keyword">
<template #label>
<div class="custom-tabs-label">
<p>关键字</p>
<el-dropdown>
<span class="el-dropdown-link" @click.stop="false">
<div class="setting_icon"></div>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="editKeyword('create')">新建</el-dropdown-item>
<el-dropdown-item @click="editKeyword('update')">编辑</el-dropdown-item>
<el-dropdown-item @click="editKeyword('delete')">删除</el-dropdown-item>
<el-dropdown-item @click="editKeyword('deleteall')"
>删除全部</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<div class="block">
<el-table
:data="keywordList"
style="width: 100%"
@selection-change="handleKeywordSelectionChange"
>
<el-table-column type="index" width="50" />
<el-table-column type="selection" width="55" />
<el-table-column prop="keyword" label="关键字" />
</el-table>
<el-dialog
v-model="keyVisible"
title="编辑关键字"
width="50%"
:before-close="keyDialogClose"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<div class="main" style="width: 100%; height: 100%">
<el-input v-model="keyWord" placeholder="请输入关键字" size="large" />
<div class="dialog-footer" style="margin-top: 20px;">
<el-button @click="keyDialogClose">取消</el-button>
<el-button type="primary" @click="confirmKeyDialogVisible">
确定
</el-button>
</div>
</div>
</el-dialog>
</div>
</el-tab-pane>
<el-tab-pane v-if="formType === 'update'" name="point">
<template #label>
<div class="custom-tabs-label">
<p>考点设置</p>
<el-dropdown>
<span class="el-dropdown-link" @click.stop="false">
<div class="setting_icon"></div>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="setKao()">考点设置</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<div class="block" style="height: 300px;width: 100% ;">
<el-table
:data="kaodianList"
style="width: 100%"
>
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="scoreRate" label="权值" width="60" />
<el-table-column prop="content" label="考点语句" width="800" />
</el-table>
</div>
<!-- 弹框 -->
<Dialog
title="考点设置"
v-model="kaoDialogVisible"
width="60%"
:close-on-click-modal="false"
class="custom-dialog"
draggable
>
<!-- 新增题干展示区 -->
<div
style="border: 1px solid #ddd; padding: 10px; margin: 10px 0; border-radius: 6px; max-height: 100%; overflow: hidden;"
>
<h3 style="margin: 0 0 8px 0;">试题内容</h3>
<el-scrollbar height="200px">
<!-- 支持富文本渲染 -->
<div v-html="formData.content" style="white-space: pre-wrap;"></div>
</el-scrollbar>
</div>
<!-- 可滚动容器 -->
<div style="height: 400px; overflow-y: auto;width:100%;">
<el-table
:data="kaodianList"
style="width: 100%; "
row-key="answerId"
:default-expand-all="false"
@row-contextmenu="(row, column, event) => handleTextRightClick(event, row)"
>
<el-table-column type="index" label="序号" />
<el-table-column label="权值" width="80">
<template #default="{ row }">
<el-input
v-model="row.scoreRate"
size="small"
placeholder="请输入"
style="width: 100%;"
/>
</template>
</el-table-column>
<el-table-column prop="content" label="考点语句" />
<el-table-column label="操作" width="160">
<template #default="{ $index }">
<el-button
:icon="ArrowUp"
circle
size="small"
@click="moveUp($index)"
title="上移"
/>
<el-button
:icon="ArrowDown"
circle
size="small"
@click="moveDown($index)"
title="下移"
/>
</template>
</el-table-column>
<!-- 展开行 -->
<el-table-column type="expand">
<template #default="props">
<el-table
:data="props.row.examMysqlKeywordList"
style="width: 100%;"
border
size="small"
>
<el-table-column type="index" label="序号" width="50" />
<el-table-column prop="keyword" label="关键词" />
<el-table-column label="权值" width="80">
<template #default="scope">
<el-input
v-model="scope.row.scoreRate"
size="small"
style="width: 70px;"
min="0"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="60">
<template #default="scope">
<el-button type="text" size="small" @click="removeKeyword(props.row, scope.$index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
</el-table-column>
</el-table>
</div>
<!-- 自定义右键菜单 -->
<div
v-if="contextMenuVisible"
class="context-menu"
:style="contextMenuStyle"
>
<div class="context-menu-item" @click="addKaodian(rightClickRow)">
设置此考点
</div>
</div>
<!-- 按钮放容器外 -->
<div style="margin-top: 12px; text-align: center;">
<el-button type="primary" @click="setKaodianRow">导入考点</el-button>
</div>
<template #footer>
<!--
<el-upload
ref="uploadRef"
class="upload"
:action="uploadUrl"
:limit="1"
:before-upload="beforeUpload"
:on-success="handleUploadSuccessFile"
:http-request="httpRequest"
drag
>
<el-button type="primary" style="margin-bottom: 16px;">上传文件</el-button>
</el-upload> -->
<el-button @click="kaoDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmKao">确定</el-button>
</template>
</Dialog>
</el-tab-pane>
<el-tab-pane name="annex">
<template #label>
<div class="custom-tabs-label">
<p>试题附件</p>
</div>
</template>
<!-- 提示 -->
<el-alert type="warning" show-icon :closable="false">
<template #default>
<span>提示:文件名称可默认不设置</span>
</template>
</el-alert>
<div class="block">
<el-table :data="formData.fileUploads" style="width: 100%">
<el-table-column type="index" label="#" width="50" />
<el-table-column label="文件名称" width="250">
<template #default="scope">
<el-input
v-model="scope.row.fileName"
size="small"
placeholder="请输入文件名称"
/>
</template>
</el-table-column>
<el-table-column
prop="fileType"
label="类型"
:formatter="fileTypeFormatter"
/>
<el-table-column prop="url" label="地址" width="200">
<template #default="{ row }">
<div
style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
:title="row.url"
>
{{ row.url }}
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="320">
<template #default="scope">
<el-button
type="primary"
plain
@click="openForm(scope.row.fileType)"
size="small"
>
<Icon icon="ep:upload" class="mr-5px" /> 上传
</el-button>
<el-button
type="success"
plain
@click="downloadFile(scope.row.url)"
size="small"
>
<Icon icon="ep:download" class="mr-5px" /> 下载
</el-button>
<el-button
type="danger"
plain
@click="deleteUrl(scope.$index)"
size="small"
><Icon icon="ep:download" class="mr-5px" />删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
</el-scrollbar>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</div>
<!-- 表单弹窗:添加/修改 -->
<FileForm ref="FileRef" @success="handleUploadSuccess"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { FormRules } from 'element-plus'
import * as QuestionApi from '@/api/paper/question'
import * as SpecialtyApi from '@/api/points'
import { defaultProps, handleTree } from '@/utils/tree'
import FileForm from './components/FileForm.vue';
import { ArrowUp, ArrowDown, Delete } from '@element-plus/icons-vue'
defineOptions({ name: 'ChoiceForm' })
import type { TabPaneName } from 'element-plus'
import { useUpload } from '@/components/UploadFile/src/useUpload'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const { uploadUrl, httpRequest } = useUpload()
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const formData = ref({
pointNamesVo:'',
chapteridDictTextVo:'',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',
required: '',
chapteridDictText: '',
analysis: '',
quLevel: 0,
pointNames: '',
tname:'',
subjectName: '',
status: ' ',
keywords: '',
resourceValue: '',
fileUploads: [ {
quId: '',
url: '',
fileType: '1',
fileName: '原始'
},
{
quId: '',
url: '',
fileType: '2',
fileName: '结果'
}]
})
// 当前激活的 tab
const activeTab = ref('basic') // 初始 tab name根据实际设置
// formData.value.quId 中包含 quId假设它已有值
const kaodianData = ref({
quId: '', // 你打开编辑弹窗时会赋值
})
const formRules = reactive<FormRules>({
// specialtyName: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }]
tname: [{ required: true, message: '数据库名称不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
// 左侧试题描述
const leftActiveName = ref('desc')
// 右侧tab
const rightActiveName = ref('analysis')
const rightHandleClick = (tab, e) => {
rightActiveName.value = tab.paneName;
if (tab.paneName === 'point' ) {
// 发起考点请求
QuestionApi.getListByQuId(kaodianData.value.quId).then(res => {
// res 是接口返回的数据数组
kaodianList.value = res;
});
}
};
//右键菜单状态
const kaoDialogVisible = ref(false) // 测试时默认打开
const kaodianList = ref<any[]>([])
const contextMenuVisible = ref(false)
const contextMenuStyle = ref({ top: '0px', left: '0px' })
const rightClickRow = ref<any>(null)
function handleTextRightClick(event: MouseEvent, row: any) {
event.preventDefault()
const selection = window.getSelection()
const selectedText = selection?.toString().trim()
if (selectedText) {
rightClickRow.value = row // 记录当前行
rightClickRow.value.selectedText = selectedText // 保存选中的文本
contextMenuStyle.value = {
top: `${event.clientY}px`,
left: `${event.clientX}px`
}
contextMenuVisible.value = true
document.addEventListener('click', closeContextMenu)
} else {
contextMenuVisible.value = false
}
}
function closeContextMenu() {
contextMenuVisible.value = false
document.removeEventListener('click', closeContextMenu)
}
// 设置考点:添加到下方
function addKaodian(row: any) {
const targetRow = rightClickRow.value;
const keyword = targetRow.selectedText;
if (!targetRow.examMysqlKeywordList) {
targetRow.examMysqlKeywordList = [];
}
const exists = targetRow.examMysqlKeywordList.find((item: any) => item.keyword === keyword);
if (!exists) {
const newKeyword = {
keyword,
scoreRate: 1,
answerId: targetRow.answerId // 添加 answerId
};
targetRow.examMysqlKeywordList.push(newKeyword);
// 累加关键字权值到语句的总权值
if (!targetRow.score) {
targetRow.score = 0;
}
targetRow.score += newKeyword.scoreRate;
}
contextMenuVisible.value = false;
}
function confirmKao() {
// 构建 questionAnswerList
const questionAnswerList = kaodianList.value.map(item => ({
answerId: item.answerId,
content: item.content,
scoreRate:item.scoreRate,
examMysqlKeywordList: (item.examMysqlKeywordList || []).map(kw => ({
keywordId: kw.keywordId,
answerId: kw.answerId,
keyword: kw.keyword,
scoreRate: kw.scoreRate
}))
}));
const payload = {
quId:kaodianData.value.quId,
questionAnswerList
};
console.log('确认后的结果:', payload)
QuestionApi.saveSelectedKaodian(payload)
kaoDialogVisible.value= false;
}
const setKaodianRow =async () => {
const uploads = formData.value.fileUploads;
console.log(uploads[0].url+"formData.value.fileUploads")
if (!uploads[0].url || !uploads[1].url) {
ElMessage.error('请先上传两个文件再导入考点');
return;
}
const fileUrl1 = uploads[0].url;
const fileUrl2 = uploads[1].url;
console.log('导入考点的文件地址为:', fileUrl1, fileUrl2);
const params = {
shucaiPath: fileUrl1,
answerPath : fileUrl2 // 如果不传可以是空字符串,也可以删除这个字段(根据后端是否必填)
};
const res = await QuestionApi.getMysqlPoint(params);
// 根据返回更新 kaodianList 或弹出提示等
kaodianList.value = res || [];
const sqlList = res || [];
// 将 SQL 字符串数组转换为 kaodianList 所需结构
kaodianList.value = sqlList.map((sql, index) => ({
answerId: index + 1, // 可自定义唯一ID生成逻辑
quId: kaodianData.value.quId, // 如果有题目ID可以在这里设置
content: sql,
scoreRate:1,
examMysqlKeywordList: [] // 默认空关键词列表
}));
};
const beforeUpload = (file: File) => {
const isTxt = file.type === 'text/plain';
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isTxt) {
ElMessage.error('只能上传 TXT 文本文件');
return false;
}
if (!isLt5M) {
ElMessage.error('文件大小不能超过 5MB');
return false;
}
return true;
};
function removeKeyword(parentRow: any, keywordIndex: number) {
parentRow.examMysqlKeywordList.splice(keywordIndex, 1);
}
// 上移
function moveUp(index) {
if (index === 0) return;
const temp = this.kaodianList[index];
this.kaodianList.splice(index, 1);
this.kaodianList.splice(index - 1, 0, temp);
this.updateSort();
}
// 下移
function moveDown(index) {
if (index === this.kaodianList.length - 1) return;
const temp = this.kaodianList[index];
this.kaodianList.splice(index, 1);
this.kaodianList.splice(index + 1, 0, temp);
this.updateSort();
}
// 重新赋值 sort 字段(保持顺序)
function updateSort() {
this.kaodianList.forEach((item, index) => {
item.sort = index + 1;
});
}
const radio = ref('A')
// 关键字
const keywordList = ref([] as any)
const multipleKeywordSelection = ref([] as any)
const handleKeywordSelectionChange = (val: any) => {
multipleKeywordSelection.value = val
}
const keyVisible = ref(false)
const keyEditType = ref('')
const keyWord = ref('')
const editKeyword = (key: string) => {
keyEditType.value = key
if (key === 'create') {
keyWord.value = ''
keyVisible.value = true
} else if (key === 'update') {
if (multipleKeywordSelection.value.length === 0) {
ElMessage.warning('请先选择一个要编辑的关键字')
return
}
keyWord.value = multipleKeywordSelection.value[0].keyword
keyVisible.value = true
} else if (key === 'delete') {
if (multipleKeywordSelection.value.length === 0) {
ElMessage.warning('请先选择要删除的关键字')
return
}
keywordList.value = keywordList.value.filter(
item => !multipleKeywordSelection.value.includes(item)
)
ElMessage.success('已删除选中项')
} else if (key === 'deleteall') {
keywordList.value = []
ElMessage.success('已清空关键字列表')
}
updateKeywordsToForm()
}
const updateKeywordsToForm = () => {
const keywordStr = keywordList.value
.map(item => item.keyword)
.filter(k => k && k.trim() !== '')
.join(',')
formData.value.keywords = keywordStr
console.log(formData.value.keywords+"formData.value.keywords")
}
function setKao() {
kaoDialogVisible.value = true
}
const keyDialogClose = () => {
keyVisible.value = false
}
const confirmKeyDialogVisible = () => {
if (keyEditType.value === 'create') {
keywordList.value.push({
keyword: keyWord.value
})
} else if (keyEditType.value === 'update') {
multipleKeywordSelection.value.forEach(item => {
item.keyword = keyWord.value
})
} else if (keyEditType.value === 'delete') {
keywordList.value = keywordList.value.filter(
item => !multipleKeywordSelection.value.includes(item)
)
} else if (keyEditType.value === 'deleteall') {
keywordList.value = []
}
updateKeywordsToForm()
keyVisible.value = false
}
/** 添加/修改操作 */
const FileRef = ref()
const openForm = (type: string) => {
FileRef.value.open(type)
}
// 媒体文件
const mediumList = ref([] as any)
const multipleMediumSelection = ref([] as any)
const handleMediumSelectionChange = (val: any) => {
multipleMediumSelection.value = val
}
const fileList = []
const multipleDocumentSelection = ref([] as any)
const handleDocumentSelectionChange = (val: any) => {
multipleDocumentSelection.value = val
}
//文件
function fileTypeFormatter(row, column, cellValue) {
if (cellValue === '1') return '考试文件'
if (cellValue === '2') return '结果文件'
return '未知类型'
}
const handleUploadSuccess = ({ url, fileType }) => {
const index = formData.value.fileUploads.findIndex(item => item.fileType === fileType)
if (index !== -1) {
formData.value.fileUploads[index].url = url
}
}
const downloadFile = async (url: string) => {
if (!url) {
ElMessage.warning('暂无可下载的文件地址')
return
}
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error('下载失败')
}
const blob = await response.blob()
const blobUrl = window.URL.createObjectURL(blob)
// 提取文件名
const filename = url.substring(url.lastIndexOf('/') + 1).split('?')[0]
// 创建 a 标签并下载
const a = document.createElement('a')
a.href = blobUrl
a.download = filename
a.style.display = 'none'
document.body.appendChild(a)
a.click()
a.remove()
window.URL.revokeObjectURL(blobUrl)
} catch (err: any) {
ElMessage.error(`下载失败:${err.message}`)
}
}
const deleteUrl = (index: number) => {
formData.value.fileUploads[index].url = ''
formData.value.fileUploads[index].fileName = ''
}
/** 打开弹窗 */
const open = async (queryParams: any ,type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
rightActiveName.value = 'analysis'
// 修改时,设置数据
if (id) {
kaodianData.value.quId=id;
formLoading.value = true
try {
const res = await QuestionApi.getQuestion(id);
// 默认两个类型
const fileTypes = ['1', '2'];
// 后端返回的上传文件列表(可能为空)
const documentList = res.fileUploads ?? [];
// 遍历两种类型,找到对应的上传文件,如果没有就用默认值
const fileUploads = fileTypes.map(type => {
const match = documentList.find(file => file.fileType === type);
return {
quId: match?.quId ?? res.quId ?? '',
url: match?.url ?? '',
fileType: type,
fileName: match?.fileName ?? ''
};
});
formData.value = {
...res,
fileUploads,
};
keywordList.value = res.keywords
? res.keywords.split(',').filter(item => item.trim() !== '').map(item => ({ keyword: item.trim() }))
: []
} finally {
formLoading.value = false
}
} else {
resetForm()
formData.value.specialtyName = queryParams.specialtyName
formData.value.courseName = queryParams.courseName
formData.value.subjectName = queryParams.subjectName
formData.value.pointNames=queryParams.pointNamesVo
formData.value.pointNamesVo=queryParams.pointNames
formData.value.chapteridDictText=queryParams.chapteridDictTextVo
formData.value.chapteridDictTextVo=queryParams.chapteridDictText
}
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
// 转换函数:将大写字母 A-Z 映射为 0-25
const mappedNumber = computed(() => {
const char = radio.value.toUpperCase();
const code = char.charCodeAt(0);
if (code >= 65 && code <= 90) {
return code - 65;
} else {
return '请输入 A-Z 的字母';
}
});
// 选择答案数据
const answerData = ref([]);
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return;
const valid = await formRef.value.validate();
if (!valid) return;
formLoading.value = true;
try {
// 深拷贝一份 formData
const data = JSON.parse(JSON.stringify(formData.value));
// 过滤掉 url 为空的文件项
data.fileUploads = data.fileUploads?.filter(file => file.url && file.url.trim() !== '');
console.log(data, "提交的数据");
if (formType.value === 'create') {
await QuestionApi.addQuestion(data);
message.success(t('common.createSuccess'));
} else {
await QuestionApi.editQuestion(data);
message.success(t('common.updateSuccess'));
}
dialogVisible.value = false;
emit('success');
} finally {
formLoading.value = false;
}
};
/** 重置表单 */
const resetForm = () => {
formData.value = {
content: `<p style="text-align: left;"><span style="font-family: 宋体;">--------------------------------------------------------------------------- </span></p><p style="text-align: left;"><span style="font-family: 宋体;">注意事项:</span></p><p style="text-align: left;"><span style="font-family: 宋体;">请利用图形化管理界面或者MySQL命令行工具在指定的试题数据库中进行答题。</span></p><p style="text-align: left;"><span style="font-family: 宋体;">请先点击答题作答,否则所有试题不得分! </span></p><p style="text-align: left;"><span style="font-family: 宋体;">MySQL连接地址为localhost</span></p><p style="text-align: left;"><span style="font-family: 宋体;">MySQL登录用户名为root</span></p><p style="text-align: left;"><span style="font-family: 宋体;">MySQL端口号为48086</span></p><p style="text-align: left;"><span style="font-family: 宋体;">MySQL密码为空</span></p><p style="text-align: left;"><span style="font-family: 宋体;">---------------------------------------------------------------------------</span>
</p>`,
specialtyName: '',
courseName: '',
quBankName: '',
required: '',
chapteridDictText: '',
analysis: '',
quLevel: 0,
tname:'',
pointNames: '',
subjectName: '',
status: '0',
keywords: '',
resourceValue: '',
fileUploads: [
{
quId: '',
url: '',
fileType: '1',
fileName: '原始'
},
{
quId: '',
url: '',
fileType: '2',
fileName: '结果'
}
]
}
kaodianList.value=[],
keywordList.value=[],
formRef.value?.resetFields()
}
const selectedPointName = ref('')
const selectedchapterText = ref('')
const dialogVisiblePoints = ref(false)
// 添加层级信息
const handleTreeWithLevel = (list, level = 1) => {
return list.map(item => {
const node = { ...item, level }
if (item.children && item.children.length > 0) {
node.children = handleTreeWithLevel(item.children, level + 1)
}
return node
})
}
// 只允许点击第三级节点
const treeRef = ref() // 引用 el-tree
const handleNodeClick = (data, node) => {
if (data.level === 3) {
formData.value.pointNames = data.id
formData.value.pointNamesVo = data.name
// 获取父节点(章节名称)
const currentNode = treeRef.value.getNode(data)
const parentNode = currentNode.parent
if (parentNode && parentNode.data) {
formData.value.chapteridDictTextVo = parentNode.data.name
formData.value.chapteridDictText = parentNode.data.id
} else {
formData.value.chapteridDictText = ''
}
dialogVisiblePoints.value = false
} else {
}
}
const deptList = ref<Tree[]>([]) // 树形结构
/** 获得部门树 */
const getTree = async () => {
const res = await SpecialtyApi.listPoints()
const tree = handleTree(res)
deptList.value = []
deptList.value = handleTreeWithLevel(tree)
}
const openPoints = async () => {
await getTree();
dialogVisiblePoints.value = true;
}
onMounted(() => {
document.addEventListener('click', () => {
contextMenuVisible.value = false
})
})
</script>
<style lang="scss" scoped>
.context-menu {
position: fixed;
background-color: white;
border: 1px solid #ddd;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
z-index: 9999;
padding: 5px 0;
width: 120px;
}
.context-menu-item {
padding: 6px 12px;
cursor: pointer;
}
.context-menu-item:hover {
background-color: #f5f5f5;
}
.edit-dialog {
:deep(.el-dialog) {
display: flex;
flex-direction: column;
.el-dialog__header {
border-bottom: 1px solid #ededed;
}
.el-dialog__footer {
border-top: 1px solid #ededed;
}
.el-dialog__body {
flex: 1;
overflow: hidden;
.main {
.el-form {
padding: 10px;
.el-row {
justify-content: space-between;
margin-bottom: 10px;
}
.el-form-item {
width: 100%;
margin-bottom: 0;
align-items: center;
}
}
.edit-bottom {
display: flex;
justify-content: space-between;
padding-bottom: 10px;
.bottom-common {
width: calc(50% - 10px);
.el-tabs {
height: 100%;
.el-tabs__content {
flex: 1;
overflow-y: auto;
.block {
width: 100%;
height: 80%;
overflow: auto;
}
.answer {
.tip {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
padding: 10px 20px;
display: flex;
align-items: center;
> p {
flex-shrink: 0;
}
}
.el-radio-group {
width: 100%;
display: inline-flex;
align-items: flex-start;
font-size: 0;
flex-direction: column;
.options {
width: 100%;
margin-top: 15px;
.content {
display: flex;
.text {
width: 100%;
height: 70px;
border: 1px solid #ededed;
.el-textarea__inner {
resize: none;
}
}
}
.more-btn {
margin-top: 8px;
background-color: #ffffff;
border-color: #007bff;
color: #007bff;
width: auto;
height: auto;
padding: 10px 10px;
}
}
}
}
}
.custom-tabs-label {
display: flex;
align-items: center;
.setting_icon {
width: 16px;
height: 16px;
background: url('@/assets/icon/setting_blue.png') no-repeat center;
background-size: 100%;
margin-left: 3px;
display: none;
}
}
.is-active {
.custom-tabs-label {
.setting_icon {
display: block;
}
}
}
:deep(.ele-pro-table) {
flex: 1;
margin-top: 10px;
.el-table--fit {
height: 100%;
}
}
}
}
}
}
}
}
}
:deep(.tox-tinymce) {
.tox-statusbar {
display: none;
}
}
:deep(.el-table) {
.el-table__header-wrapper {
.el-table__header {
thead {
tr {
th {
background: #ebebeb;
}
}
}
}
}
}
</style>