【新增】监控管理试卷任务监控、细节优化

This commit is contained in:
YOHO\20373
2025-05-12 09:26:40 +08:00
committed by 陆光LG
parent 1278962dc2
commit 12cbdaa78a
38 changed files with 1139 additions and 360 deletions

View File

@@ -54,6 +54,8 @@ export const MonitorApi = {
updateMonitorStatus:async( data)=>{
return await request.post({ url: `/exam/monitor/updateMonitorStatus` , data })
}
},
getMonitorTaskPage: async (params: any) => {
return await request.get({ url: `/exam/monitorTask/page`, params })
},
}

View File

@@ -36,11 +36,7 @@ export async function addScheme(data) {
* 修改试卷方案
*/
export async function updateScheme(data) {
return await request.put({ url: '/exam/scheme' , data })
}
/**

View File

@@ -5,7 +5,7 @@
<el-form label-width="80px" class="px-4 pt-2">
<el-form-item label="考试状态">
<el-radio-group v-model="selectedStatus">
<el-radio :label="'0'"></el-radio>
<el-radio :label="'0'"></el-radio>
<el-radio :label="'1'">考试中</el-radio>
<el-radio :label="'2'">考试完成</el-radio>
</el-radio-group>
@@ -17,7 +17,23 @@
<el-button type="primary" @click="confirmChange">确认</el-button>
</template>
</el-dialog>
<el-dialog
v-model="initDialogVisible"
title="试卷任务监控列表"
width="90%"
top="5vh"
class="-mb-15px"
:close-on-click-modal="false"
:show-close="true"
@close="handleNextStepCancel"
>
<taskMonitor @row-clicked="handleTaskSelected" />
<template #footer>
<el-button @click="handleNextStepCancel">取消</el-button>
<el-button type="primary" @click="handleNextStep">下一步</el-button>
</template>
</el-dialog>
<ContentWrap>
<!-- 搜索工作栏 -->
@@ -226,10 +242,22 @@ import download from '@/utils/download'
import { MonitorApi, MonitorVO } from '@/api/monitor'
import MonitorForm from './MonitorForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import taskMonitor from './task-monitor.vue'
import * as ClassApi from '@/api/exam/class'
import { ref } from 'vue'
const selectedTaskId = ref<string | null>(null)
// 接收子组件传递的taskId
const handleTaskSelected = (taskId: string) => {
queryParams.taskId = taskId
console.log('父组件接收到的taskId:', taskId)
}
/** 监控管理 列表 */
defineOptions({ name: 'Monitor' })
// 新增:初始弹框开关
const initDialogVisible = ref(true);
// 弹窗开关
const dialogVisible = ref(false)
const message = useMessage() // 消息弹窗
@@ -242,7 +270,8 @@ const classNameList = ref();
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
monitorId:'',
monitorId:undefined,
taskId:'',
username: undefined,
nickname: undefined,
taskType:undefined,
@@ -264,6 +293,7 @@ const getList = async () => {
classNameList.value = await ClassApi.ClassApi.getClassName()
try {
const data = await MonitorApi.getMonitorPage(queryParams)
queryParams.taskId = ''
list.value = data.list
total.value = data.total
} finally {
@@ -342,6 +372,7 @@ const openForm = (type: string, id?: number) => {
ElMessage.success('考试状态更新成功')
dialogVisible.value = false
getList()
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
@@ -395,9 +426,20 @@ const fetchTaskList = async (taskType: string) => {
console.error('获取试卷任务失败', error)
}
}
const handleNextStep = () => {
getList()
initDialogVisible.value = false
}
const handleNextStepCancel = () => {
queryParams.taskId = ''
getList()
initDialogVisible.value = false
}
/** 初始化 **/
onMounted(() => {
getList()
initDialogVisible.value = true; // 显示初始弹框
})
</script>

View File

@@ -0,0 +1,284 @@
<template>
<!-- 状态选择弹窗 -->
<el-dialog v-model="dialogVisible" title="请选择考试状态" width="400px">
<el-form label-width="80px" class="px-4 pt-2">
<el-form-item label="考试状态">
<el-radio-group v-model="selectedStatus">
<el-radio :label="'0'">待考</el-radio>
<el-radio :label="'1'">考试中</el-radio>
<el-radio :label="'2'">考试完成</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmChange">确认</el-button>
</template>
</el-dialog>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="任务编号" prop="taskNum">
<el-input
v-model="queryParams.taskNum"
placeholder="请输入任务编号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
v-model="queryParams.taskName"
placeholder="请输入任务名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="考试状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择考试状态"
clearable
class="!w-240px"
>
<el-option label="启用" :value="0" />
<el-option label="停用" :value="1" />
</el-select>
</el-form-item>
<el-form-item label="任务类别" prop="taskType">
<el-select v-model="queryParams.taskType" placeholder="任务类别" class="!w-240px" @change="fetchTaskList">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.TASK_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<!-- <el-button type="primary" @click="openDialog"><Icon icon="ep:edit" class="mr-5px" />考试状态改变</el-button> -->
<!-- <el-button
type="primary"
plain
@click="openForm('create')"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<!-- <el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button> -->
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" highlight-current-row ref="tableRef" @row-click="handleRowClick">
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="任务模式" align="center" prop="taskType">
<template #default="scope">
<dict-tag :type="DICT_TYPE.TASK_TYPE" :value="scope.row.taskType" />
</template>
</el-table-column>
<el-table-column label="考试状态" align="center" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYS_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷设置" align="center">
<template #default="{row}">
{{ row.paperCount == 0 ? '未设置': `试卷${row.paperCount}张/${row.paperScore}分/${row.queNum}` }}
</template>
</el-table-column>
<el-table-column label="参与人数" align="center" prop="joinCount" />
<el-table-column label="未考人数" align="center" prop="notReadCount" />
<el-table-column label="考试人数" align="center" prop="haveReadCount" />
<el-table-column label="完成人数" align="center" prop="passCount" />
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<!-- <MonitorForm ref="formRef" @success="getList" /> -->
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { MonitorApi, MonitorVO } from '@/api/monitor'
import MonitorForm from './MonitorForm.vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as ClassApi from '@/api/exam/class'
import { defineEmits } from 'vue'
/** 监控管理 列表 */
defineOptions({ name: 'Monitor' })
const emit = defineEmits(['row-clicked'])
// 弹窗开关
const tableRef = ref()
const dialogVisible = ref(false)
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const selectedStatus = ref<string | null>(null);
const loading = ref(true) // 列表的加载中
const list = ref<MonitorVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const classNameList = ref();
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
monitorId:undefined,
taskNum: undefined,
nickname: undefined,
taskType:undefined,
className: undefined,
status: undefined,
score: undefined,
paperNum: undefined,
taskName: undefined,
ip: undefined,
remainingTime: [],
createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
loading.value = true
classNameList.value = await ClassApi.ClassApi.getClassName()
try {
console.log("进入考试")
const data = await MonitorApi.getMonitorTaskPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
// 打开弹窗
const currentRow = ref<any>(null) // 存储当前选中行
const handleRowClick = (row: any, column: any, event: Event) => {
// 如果点击的是操作按钮区域,则不触发选中
currentRow.value = row
tableRef.value!.setCurrentRow(row) // 高亮选中行
selections.value = row.taskId// 将selections设置为只包含当前行的数组
emit('row-clicked', row.taskId) // 触发自定义事件
}
/** 表格选中数据 */
const selections = ref([]);
const taskList = ref([]);
const handleSelectionChange = (rows) => {
selections.value = rows;
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
// 提交状态变更
const confirmChange = async () => {
if (selectedStatus.value === null) {
ElMessage.error('请选择考试状态');
return;
}
const rows = selections.value;
const monitorIds = rows.map((row: any) => row.monitorId);
await MonitorApi.updateMonitorStatus({
monitorIds, // 这是数组
status: selectedStatus.value // 这是 0,1,2
});
ElMessage.success('考试状态更新成功')
dialogVisible.value = false
getList()
}
const selectedRows = ref<string[]>([]);
/** 导出按钮操作 */
const fetchTaskList = async (taskType: string) => {
try {
const res = await MonitorApi.getPaperTaskList( taskType )
taskList.value = res
} catch (error) {
taskList.value = []
console.error('获取试卷任务失败', error)
}
}
// 暴露方法供父组件获取选中行
defineExpose({
getSelectedTask: () => currentRow.value?.taskId
})
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -168,7 +168,6 @@ const form = ref<{
const rules = reactive({
spName: [{ required: true, message: '请选择题型', trigger: 'change' }],
quLevel: [{ required: true, message: '请选择难度', trigger: 'change' }],
sort: [{ required: true, message: '请设置排序', trigger: 'change' }],
quTitle: [{ required: true, message: '请输入别名', trigger: 'change' }],
pointName: [{ required: true, message: '请选择知识点', trigger: 'change' }],
quNumbers: [{ required: true, message: '请输入试题数量', trigger: 'blur' }],
@@ -223,11 +222,29 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/

View File

@@ -36,7 +36,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="taskId" />
<!-- <el-table-column label="方案ID" align="center" prop="taskId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -47,13 +47,25 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
@@ -76,6 +88,11 @@
</template>
</el-table-column>
</el-table>
<!-- 总分展示区域 -->
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -153,7 +170,40 @@ const queryParams = reactive({
taskId: props.taskId,
spName:undefined
})
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 查询列表 */
const getList = async () => {
// 1. 并行拉下拉所需的选项

View File

@@ -1,211 +0,0 @@
<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['system:sms-channel:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增</el-button
>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="试卷编号" align="center" prop="paperId" />
<el-table-column label="使用次数" align="center" prop="counts" />
<el-table-column label="抽卷方式" align="center" prop="rollUp">
<template #default="scope">
<span v-if="scope.row.quLevel === '0'">固定</span>
<span v-else-if="scope.row.quLevel === '1'">AB卷</span>
<span v-else-if="scope.row.quLevel === '2'">随机</span>
<span v-else-if="scope.row.quLevel === '3'">自选</span>
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="AB卷" align="center" prop="isAb">
<template #default="scope">
<span v-if="scope.row.quLevel === '0'">A卷</span>
<span v-else-if="scope.row.quLevel === '1'">B卷</span>
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="是否启用" align="center" prop="status" >
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYS_YES_NO" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openEdit('update', scope.row)"
v-hasPermi="['system:sms-channel:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['system:sms-channel:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<paper-add v-model="showAdd" :task-Id="taskId" :task-specialty="taskSpecialty" />
<paper-edit v-model="showEdit" :data="current" />
<!-- <paper-look v-model="showLook" :paper-id="paperId" /> -->
<paper-set v-model="showSet" :task-Id="taskId" />
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import * as SmsChannelApi from '@/api/system/paper';
import PaperEdit from './components/step-edit.vue';
import PaperAdd from './components/step-add.vue';
import PaperLook from './components/step-look.vue';
import PaperSet from './components/step-set.vue';
import PaperSearch from './components/step-search.vue';
import { pagePapers, removePapers, exportPapers } from '@/api/system/paper';
defineOptions({ name: 'SystemPaper' });
const props = defineProps({
taskSpecialty: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
}
})
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
/** 当前编辑数据 */
const current = ref<object>();
/** 是否显示编辑弹窗 */
const showEdit = ref(false);
const showLook = ref(false);
const showAdd = ref(false);
const showSet = ref(false);
const smsChannelFormRef = ref()
const taskEditRef = ref()
const taskAddRef = ref()
const taskTempRef = ref()
const loading = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref([]) // 列表的数据
const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
status: undefined,
createTime: []
})
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const res = await SmsChannelApi.pagePapers(queryParams)
console.log(res)
list.value = res
total.value = res.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await SmsChannelApi.removePaper(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@@ -74,7 +74,7 @@
<!-- 多选列 -->
<el-table-column type="selection" width="55" />
<el-table-column label="试卷id" align="center" prop="paperId" />
<!-- <el-table-column label="试卷id" align="center" prop="paperId" /> -->
<el-table-column label="试卷编号" align="center" prop="num" />
<el-table-column label="使用次数" align="center" prop="counts" />
<el-table-column label="抽卷方式" align="center" prop="rollUp">

View File

@@ -8,6 +8,9 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +127,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '1',
isTemplate: '',
@@ -203,6 +207,23 @@ const fetchSpecialtyOptions = async () => {
resetFields();
isUpdate.value = false;
}
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +233,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ handleOpen })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -240,14 +241,19 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -223,11 +223,29 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/

View File

@@ -47,14 +47,24 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -76,6 +86,10 @@
</template>
</el-table-column>
</el-table>
<!-- 总分展示区域 -->
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -153,7 +167,39 @@ const queryParams = reactive({
taskId: props.taskId ,
spName:undefined
})
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 查询列表 */
const getList = async () => {
// 1. 并行拉下拉所需的选项

View File

@@ -8,6 +8,10 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +128,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '6',
isTemplate: '',
@@ -195,14 +200,32 @@ const fetchSpecialtyOptions = async () => {
};
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
const open = () => {
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +235,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ open })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -222,7 +223,9 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
/** 状态切换处理 */
@@ -248,7 +251,9 @@ const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -223,13 +223,30 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/
async function loadPointTree() {
try {

View File

@@ -36,7 +36,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="taskId" />
<!-- <el-table-column label="方案ID" align="center" prop="taskId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -47,14 +47,23 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -76,6 +85,10 @@
</template>
</el-table-column>
</el-table>
<!-- 总分展示区域 -->
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -199,8 +212,38 @@ const openEdit = (type: string, row?: object) => {
})
}
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {

View File

@@ -8,6 +8,9 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +127,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '2',
isTemplate: '',
@@ -196,13 +200,30 @@ const fetchSpecialtyOptions = async () => {
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +233,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ handleOpen })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -222,7 +223,11 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
/** 状态切换处理 */
@@ -248,7 +253,10 @@ const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -223,11 +223,29 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/

View File

@@ -36,7 +36,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="taskId" />
<!-- <el-table-column label="方案ID" align="center" prop="taskId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -47,14 +47,25 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -76,6 +87,10 @@
</template>
</el-table-column>
</el-table>
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -198,8 +213,38 @@ const openEdit = (type: string, row?: object) => {
taskEditRef.value?.open(type, row)
})
}
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {

View File

@@ -8,6 +8,9 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +127,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '5',
isTemplate: '',
@@ -196,13 +200,31 @@ const fetchSpecialtyOptions = async () => {
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +234,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ handleOpen })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -222,7 +223,10 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
/** 状态切换处理 */
@@ -248,7 +252,10 @@ const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -223,11 +223,27 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/

View File

@@ -36,7 +36,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="taskId" />
<!-- <el-table-column label="方案ID" align="center" prop="taskId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -47,14 +47,23 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -76,6 +85,10 @@
</template>
</el-table-column>
</el-table>
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -172,7 +185,38 @@ const getList = async () => {
loading.value = false
}
}
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1

View File

@@ -8,6 +8,9 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +127,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '4',
isTemplate: '',
@@ -196,13 +200,31 @@ const fetchSpecialtyOptions = async () => {
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +234,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ handleOpen })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -223,6 +224,10 @@ const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
/** 状态切换处理 */
@@ -248,7 +253,10 @@ const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -168,7 +168,7 @@ const form = ref<{
const rules = reactive({
spName: [{ required: true, message: '请选择题型', trigger: 'change' }],
quLevel: [{ required: true, message: '请选择难度', trigger: 'change' }],
sort: [{ required: true, message: '请设置排序', trigger: 'change' }],
// sort: [{ required: true, message: '请设置排序', trigger: 'change' }],
quTitle: [{ required: true, message: '请输入别名', trigger: 'change' }],
pointName: [{ required: true, message: '请选择知识点', trigger: 'change' }],
quNumbers: [{ required: true, message: '请输入试题数量', trigger: 'blur' }],
@@ -223,11 +223,29 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/
@@ -329,7 +347,7 @@ const save = async () => {
const fn = isUpdate.value ? updateScheme : addScheme
await fn(payload)
emit('done')
visible.value = false
} finally {
loading.value = false
}

View File

@@ -36,7 +36,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="taskId" />
<!-- <el-table-column label="方案ID" align="center" prop="taskId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -47,13 +47,23 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
@@ -76,6 +86,12 @@
</template>
</el-table-column>
</el-table>
<!-- 总分展示区域 -->
<div style="display: flex; justify-content: flex-end; padding: 5px; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -179,6 +195,40 @@ const handleQuery = () => {
getList()
}
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()

View File

@@ -74,7 +74,7 @@
<!-- 多选列 -->
<el-table-column type="selection" width="55" />
<el-table-column label="试卷id" align="center" prop="paperId" />
<!-- <el-table-column label="试卷id" align="center" prop="paperId" /> -->
<el-table-column label="试卷编号" align="center" prop="num" />
<el-table-column label="使用次数" align="center" prop="counts" />
<el-table-column label="抽卷方式" align="center" prop="rollUp">

View File

@@ -19,6 +19,15 @@
<el-form-item label="批次" prop="batch">
<el-input clearable v-model="form.batch" placeholder="请输入批次" disabled/>
</el-form-item>
<el-form-item label="考试时长" prop="answerTime">
<el-time-picker
v-model="form.answerTime"
value-format="HH:mm:ss"
placeholder="请选择考试时长"
class="ele-fluid"
/>
</el-form-item>
<el-form-item label="考试开始时间" prop="startTime">
<el-date-picker
@@ -37,6 +46,7 @@
value-format="YYYY-MM-DD HH:mm:ss"
placeholder="请选择考试结束时间"
class="ele-fluid"
disabled
/>
</el-form-item>
@@ -99,6 +109,8 @@
import { ref, reactive, nextTick } from 'vue'
import { addSession, updateSession } from '@/api/system/session'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { watch } from 'vue'
import dayjs from 'dayjs'
const emit = defineEmits(['done'])
const props = defineProps({
data: Object,
@@ -116,6 +128,7 @@ const form = ref({
taskId: props.taskId ?? '',
batch: '',
startTime: '',
answerTime:'',
endTime: '',
allowEntry: '',
endAllowEntry: '',
@@ -149,6 +162,7 @@ const open = async (type: 'create' | 'update', data?: any) => {
taskId: props.taskId ?? '',
batch: '',
startTime: '',
answerTime:'01:00:00',
endTime: '',
allowEntry: '',
endAllowEntry: '',
@@ -178,7 +192,20 @@ const open = async (type: 'create' | 'update', data?: any) => {
await nextTick()
formRef.value?.clearValidate?.()
}
// 监听考试开始时间和考试时长,自动计算考试结束时间
watch(() => [form.value.startTime, form.value.answerTime], ([startTime, answerTime]) => {
if (startTime && answerTime) {
const [hours, minutes, seconds] = answerTime.split(':').map(Number)
const end = dayjs(startTime)
.add(hours, 'hour')
.add(minutes, 'minute')
.add(seconds, 'second')
.format('YYYY-MM-DD HH:mm:ss')
form.value.endTime = end
} else {
form.value.endTime = ''
}
})
// 保存按钮
const save = async () => {
await formRef.value.validate()
@@ -192,6 +219,6 @@ const save = async () => {
loading.value = false
}
}
defineExpose({ open })
</script>

View File

@@ -8,6 +8,9 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -123,6 +126,7 @@ const specialtyOptions = ref([]);
/** 表单数据 */
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskNum:'',
taskName: '',
taskSpecialty: '',
taskType: '3',
@@ -195,14 +199,31 @@ const fetchSpecialtyOptions = async () => {
};
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
const open = () => {
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -212,5 +233,5 @@ const fetchSpecialtyOptions = async () => {
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ open })
</script>

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -240,14 +241,17 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
nextTick(() => {
taskEditRef.value?.open(type, row)
});
}
const openBank = () => {

View File

@@ -87,7 +87,7 @@
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-form-item label="别名排序" prop="sort">
<el-input-number v-model="form.sort" />
</el-form-item>
</el-form>
@@ -223,11 +223,28 @@ const open = async (type: 'create' | 'update', data?: any) => {
sort: undefined,
taskSpecialty: props.taskSpecialty
}
// 清除校验
await nextTick()
const allKeys = getAllTreeKeys(pointOptions.value).map(String)
form.value.pointName = allKeys
treeRef.value?.setCheckedKeys(allKeys)
}
// 清除校验
await nextTick()
formRef.value?.clearValidate?.()
}
function getAllTreeKeys(tree: TreeNode[]): number[] {
const keys: number[] = []
const traverse = (nodes: TreeNode[]) => {
for (const node of nodes) {
keys.push(node.value)
if (node.children?.length) {
traverse(node.children)
}
}
}
traverse(tree)
return keys
}
/** --------- 拉取扁平知识点并建树 --------- **/

View File

@@ -37,7 +37,7 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list">
<el-table-column label="方案ID" align="center" prop="schemeId" />
<!-- <el-table-column label="方案ID" align="center" prop="schemeId" /> -->
<el-table-column label="题型" align="center" prop="spName" />
<el-table-column label="难度" align="center" prop="quLevel">
<template #default="scope">
@@ -48,14 +48,24 @@
<span v-else>未知</span>
</template>
</el-table-column>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<!-- <el-table-column label="关键字" align="center" prop="keywords" /> -->
<el-table-column label="试题数量" align="center" prop="quNumbers"/>
<el-table-column label="每题分数" align="center" prop="quScores"/>
<el-table-column label="小计分数" align="center" prop="subtotalScore"/>
<el-table-column label="试题别名" align="center" prop="quTitle"/>
<el-table-column label="别名排序" align="center">
<template #default="scope">
<el-input-number
v-model="scope.row.sort"
placeholder="请输入排序"
size="small"
:min="0"
style="width: 100px"
@change="(val) => handleSortChange(scope.row, val)"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
@@ -77,6 +87,10 @@
</template>
</el-table-column>
</el-table>
<!-- 总分展示区域 -->
<div style="text-align: right; margin: 10px 0; font-weight: bold;">
总分{{ totalScore }}
</div>
<!-- 分页 -->
<Pagination
:total="total"
@@ -136,7 +150,38 @@ const taskEditRef = ref()
const taskAddRef = ref()
const taskTempRef = ref()
const totalScore = computed(() => {
return list.value.reduce((sum, item) => {
// 确保字段存在且为数字
const score = parseFloat(item.subtotalScore) || 0;
return sum + score;
}, 0);
});
// 排序值修改后自动调用接口
const handleSortChange = async (row, newValue) => {
if (newValue < 0) {
message.error('排序不能小于 0')
row.sort = 0
return
}
// 1. 获取所有 quTitle 相同的项
const sameTitleItems = list.value.filter(item => item.quTitle === row.quTitle)
// 2. 批量更新 sort 值
const updateList = sameTitleItems.map(item => ({
...item,
sort: newValue
}))
try {
// 假设后端支持批量更新接口
await Promise.all(updateList.map(item => SmsChannelApi.updateScheme(item)))
message.success('同别名方案的排序已同步更新')
getList()
} catch (error) {
message.error('排序更新失败')
}
}
const loading = ref(false) // 列表的加载中
const total = ref(0) // 列表的总页数

View File

@@ -74,7 +74,7 @@
<!-- 多选列 -->
<el-table-column type="selection" width="55" />
<el-table-column label="试卷id" align="center" prop="paperId" />
<!-- <el-table-column label="试卷id" align="center" prop="paperId" /> -->
<el-table-column label="试卷编号" align="center" prop="num" />
<el-table-column label="使用次数" align="center" prop="counts" />
<el-table-column label="抽卷方式" align="center" prop="rollUp">

View File

@@ -1,6 +1,6 @@
<!-- 编辑弹窗 -->
<template>
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" @open="handleOpen" center>
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" center>
<el-form
ref="formRef"
:model="form"
@@ -8,6 +8,10 @@
label-width="80px"
@submit.prevent=""
>
<el-form-item label="任务编号" prop="taskNum">
<el-input clearable v-model="form.taskNum" placeholder="请输入编号" disabled/>
</el-form-item>
<el-form-item label="任务名称" prop="taskName">
<el-input
clearable
@@ -124,6 +128,7 @@ const specialtyOptions = ref([]);
const [form, resetFields, assignFields] = useFormData({
taskId: void 0,
taskName: '',
taskNum:'',
taskSpecialty: '',
taskType: '0',
isTemplate: '',
@@ -178,6 +183,7 @@ const fetchSpecialtyOptions = async () => {
if (!valid) {
return;
}
visible.value = true;
loading.value = true;
const saveOrUpdate = isUpdate.value ? updateTask : addTask;
saveOrUpdate(form)
@@ -195,14 +201,31 @@ const fetchSpecialtyOptions = async () => {
};
/** 弹窗打开事件 */
const handleOpen = () => {
if (props.data) {
assignFields(props.data);
isUpdate.value = true;
} else {
resetFields();
isUpdate.value = false;
}
const open = async (type, row) => {
// if (props.data) {
// assignFields(props.data);
// isUpdate.value = true;
// } else {
// resetFields();
// isUpdate.value = false;
// }
// 设置批次为 当前时间 + 5位随机数
const now = new Date();
const pad = (n) => n.toString().padStart(2, '0');
const datetimeStr = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds())
].join('');
const randomNum = Math.floor(Math.random() * 100000).toString().padStart(5, '0');
//taskNum 赋值 randomNum
// 把 taskNum 设置为当前时间+随机数
form.taskNum = `${datetimeStr}${randomNum}`;
nextTick(() => {
nextTick(() => {
formRef.value?.clearValidate?.();
@@ -210,7 +233,8 @@ const fetchSpecialtyOptions = async () => {
});
};
onMounted(() => {
fetchSpecialtyOptions();
});
defineExpose({ open })
</script>

View File

@@ -71,7 +71,6 @@ const props = defineProps({
})
const isVisible = defineModel({ type: Boolean })
const isUpdate = ref(false)
const activeStep = ref(0)
const stepTitles = ['试卷方案', '试卷管理', '参数设置', '考场设置', '人员设置']
@@ -97,9 +96,7 @@ async function handleStepClick(index) {
const open = async (type, row) => {
isVisible.value = true
assignFields(row)
isUpdate.value = true
activeStep.value = 0
nextTick(() => {
formRef.value?.clearValidate?.()
})

View File

@@ -73,6 +73,7 @@
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="任务编号" align="center" prop="taskNum" />
<el-table-column label="任务名称" align="center" prop="taskName" />
<el-table-column label="专业" align="center" prop="taskSpecialty" />
<el-table-column label="试卷任务模式" align="center" prop="taskType">
@@ -222,15 +223,20 @@ const handleSelectionChange = (rows) => {
const formRef = ref();
const openForm = (type: string, id?: number) => {
showAdd.value = true
taskAddRef.value?.open(type, id)
nextTick(() => {
taskAddRef.value?.open(type, id)
});
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true
current.value = row
console.log( current.value )
taskEditRef.value?.open(type, row)
}
showEdit.value = true;
current.value = row;
nextTick(() => {
taskEditRef.value?.open(type, row);
});
};
const openBank = () => {
showOpenTemplate.value = true