【新增】试卷任务训练、考试、高考模拟前端
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="visible"
|
||||
:title="isUpdate ? '修改试卷方案' : '添加试卷方案'"
|
||||
width="460"
|
||||
center
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
@submit.prevent=""
|
||||
>
|
||||
<!-- 知识点 -->
|
||||
<el-form-item label="知识点" prop="pointName">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:data="pointOptions"
|
||||
show-checkbox
|
||||
node-key="value"
|
||||
default-expand-all
|
||||
:props="{ label: 'label', children: 'children' }"
|
||||
highlight-current
|
||||
:disabled="isUpdate"
|
||||
@check="handleCheckChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 题型 -->
|
||||
<el-form-item label="题型" prop="spName">
|
||||
<el-select v-model="form.spName" placeholder="请选择题型" clearable :disabled="isUpdate">
|
||||
<el-option
|
||||
v-for="item in CourseOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 难度 -->
|
||||
<el-form-item label="难度" prop="quLevel">
|
||||
<el-select v-model="form.quLevel" placeholder="请选择难度" :disabled="isUpdate">
|
||||
<el-option label="全部" :value="3" />
|
||||
<el-option label="简单" :value="0" />
|
||||
<el-option label="一般" :value="1" />
|
||||
<el-option label="困难" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 关键字 -->
|
||||
<el-form-item label="关键字" prop="keyword">
|
||||
<el-select
|
||||
v-model="form.keyword"
|
||||
placeholder="请选择关键字"
|
||||
multiple
|
||||
clearable
|
||||
:disabled="isUpdate"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in keyOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 数量、分数 -->
|
||||
<el-form-item label="试题数量" prop="quNumbers">
|
||||
<el-input-number v-model="form.quNumbers" placeholder="请输入试题数量" :disabled="isUpdate" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="每题分数" prop="quScores">
|
||||
<el-input-number v-model="form.quScores" placeholder="请输入每题分数" :disabled="isUpdate" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="小计分数" prop="subtotalScore">
|
||||
<el-input-number v-model="form.subtotalScore" :disabled="true" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="可用数量">
|
||||
<el-text>{{ availableCount }}</el-text>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="save" :disabled="isUpdate">保存</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import {
|
||||
addScheme,
|
||||
updateScheme,
|
||||
getCourseList,
|
||||
getKeyList,
|
||||
getPoints,
|
||||
fetchQuestionCount
|
||||
} from '@/api/system/scheme'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const emit = defineEmits(['done'])
|
||||
const props = defineProps({
|
||||
taskSpecialty: String,
|
||||
taskId: String
|
||||
})
|
||||
|
||||
/** --------- 状态定义 --------- **/
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
const isUpdate = ref(false)
|
||||
const loading = ref(false)
|
||||
const formRef = ref()
|
||||
const treeRef = ref()
|
||||
const availableCount = ref<number | string>('')
|
||||
|
||||
const CourseOptions = ref<string[]>([])
|
||||
const keyOptions = ref<string[]>([])
|
||||
|
||||
// 扁平接口返回的原始结构
|
||||
interface PointRaw {
|
||||
spId: number
|
||||
spName: string
|
||||
parentId: number
|
||||
orderNum?: number
|
||||
ancestors?: string
|
||||
[key: string]: any
|
||||
}
|
||||
// 构造给 el-tree 用的节点结构
|
||||
interface TreeNode extends PointRaw {
|
||||
label: string
|
||||
value: number
|
||||
children: TreeNode[]
|
||||
}
|
||||
|
||||
const pointOptions = ref<TreeNode[]>([])
|
||||
|
||||
const form = ref<{
|
||||
schemeId?: string
|
||||
taskId?: string
|
||||
spName?: string
|
||||
quLevel?: number
|
||||
keyword: string[]
|
||||
pointName: string[]
|
||||
quNumbers?: number
|
||||
quScores?: number
|
||||
subtotalScore?: number
|
||||
taskSpecialty?: string
|
||||
}>({
|
||||
keyword: [],
|
||||
pointName: []
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
spName: [{ required: true, message: '请选择题型', trigger: 'change' }],
|
||||
quLevel: [{ required: true, message: '请选择难度', trigger: 'change' }],
|
||||
pointName: [{ required: true, message: '请选择知识点', trigger: 'change' }],
|
||||
quNumbers: [{ required: true, message: '请输入试题数量', trigger: 'blur' }],
|
||||
quScores: [{ required: true, message: '请输入每题分数', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/** --------- 打开弹窗 --------- **/
|
||||
const open = async (type: 'create' | 'update', data?: any) => {
|
||||
visible.value = true
|
||||
isUpdate.value = type === 'update'
|
||||
|
||||
// 1. 并行拉下拉所需的选项
|
||||
const [courses, keywords] = await Promise.all([
|
||||
getCourseList(),
|
||||
getKeyList()
|
||||
])
|
||||
CourseOptions.value = courses
|
||||
keyOptions.value = keywords
|
||||
|
||||
// 2. 拉知识点并建树
|
||||
await loadPointTree()
|
||||
|
||||
// 3. 填表单
|
||||
if (isUpdate.value && data) {
|
||||
form.value = {
|
||||
...data,
|
||||
keyword: data.keywords?.split(',') || [],
|
||||
pointName: data.pointNames?.split(',') || [],
|
||||
taskId: props.taskId,
|
||||
taskSpecialty: props.taskSpecialty
|
||||
}
|
||||
|
||||
// 4. 等树节点数据都生效了,再设置默认选中
|
||||
await nextTick()
|
||||
treeRef.value?.setCheckedKeys(form.value.pointName)
|
||||
|
||||
calcSubtotal()
|
||||
fetchCount()
|
||||
} else {
|
||||
// 新增时重置
|
||||
form.value = {
|
||||
schemeId: undefined,
|
||||
taskId: props.taskId,
|
||||
spName: undefined,
|
||||
quLevel: undefined,
|
||||
keyword: [],
|
||||
pointName: [],
|
||||
quNumbers: undefined,
|
||||
quScores: undefined,
|
||||
subtotalScore: undefined,
|
||||
taskSpecialty: props.taskSpecialty
|
||||
}
|
||||
}
|
||||
|
||||
// 清除校验
|
||||
await nextTick()
|
||||
formRef.value?.clearValidate?.()
|
||||
}
|
||||
|
||||
/** --------- 拉取扁平知识点并建树 --------- **/
|
||||
async function loadPointTree() {
|
||||
try {
|
||||
const res = await getPoints(props.taskSpecialty)
|
||||
// 兼容:getPoints 有可能直接返回数组,也可能返回 { data: [] }
|
||||
const raw: PointRaw[] = Array.isArray(res)
|
||||
? res
|
||||
: Array.isArray((res as any).data)
|
||||
? (res as any).data
|
||||
: []
|
||||
|
||||
pointOptions.value = buildTree(raw)
|
||||
} catch (e: any) {
|
||||
message.error('获取知识点失败:' + (e.message || e))
|
||||
pointOptions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 扁平 -> 树 */
|
||||
function buildTree(data: PointRaw[]): TreeNode[] {
|
||||
const idMap = new Map<number, TreeNode>()
|
||||
data.forEach(item => {
|
||||
idMap.set(item.spId, {
|
||||
...item,
|
||||
label: item.spName,
|
||||
value: item.spId,
|
||||
children: []
|
||||
})
|
||||
})
|
||||
|
||||
const tree: TreeNode[] = []
|
||||
data.forEach(item => {
|
||||
const node = idMap.get(item.spId)!
|
||||
const parent = idMap.get(item.parentId)
|
||||
if (parent) {
|
||||
parent.children.push(node)
|
||||
} else {
|
||||
tree.push(node)
|
||||
}
|
||||
})
|
||||
|
||||
// (可选)按 orderNum 排序
|
||||
const sortRecursively = (nodes: TreeNode[]) => {
|
||||
nodes.sort((a, b) => (a.orderNum ?? 0) - (b.orderNum ?? 0))
|
||||
nodes.forEach(n => sortRecursively(n.children))
|
||||
}
|
||||
sortRecursively(tree)
|
||||
|
||||
return tree
|
||||
}
|
||||
|
||||
/** 计算小计 */
|
||||
const calcSubtotal = () => {
|
||||
const n = Number(form.value.quNumbers ?? 0)
|
||||
const s = Number(form.value.quScores ?? 0)
|
||||
form.value.subtotalScore = n * s
|
||||
}
|
||||
|
||||
/** 选中回调 */
|
||||
const handleCheckChange = () => {
|
||||
form.value.pointName = treeRef.value.getCheckedKeys()
|
||||
|
||||
}
|
||||
|
||||
/** 拉取可用题目数量 */
|
||||
const fetchCount = async () => {
|
||||
const res = await fetchQuestionCount({
|
||||
taskSpecialty: props.taskSpecialty,
|
||||
pointIds: form.value.pointName, // 知识点
|
||||
spName: form.value.spName, // 题型
|
||||
quLevel: form.value.quLevel, // 难度
|
||||
keyword: form.value.keyword // **关键字**,加上这一行
|
||||
|
||||
})
|
||||
availableCount.value = res ?? 0
|
||||
}
|
||||
|
||||
/** 保存 */
|
||||
const save = async () => {
|
||||
// 判断试题数量是否小于等于可用题目数量
|
||||
if (Number(form.value.quNumbers) > Number(availableCount.value)) {
|
||||
message.warning('试题数量不能超过可用题目数量');
|
||||
return;
|
||||
}
|
||||
if (Number(availableCount.value) === 0) {
|
||||
message.warning('当前条件下没有可用的试题,请调整条件后再试');
|
||||
return;
|
||||
}
|
||||
await formRef.value.validate()
|
||||
loading.value = true
|
||||
try {
|
||||
const payload = {
|
||||
...form.value,
|
||||
keywords: form.value.keyword.join(','),
|
||||
pointNames: form.value.pointName.join(',')
|
||||
}
|
||||
const fn = isUpdate.value ? updateScheme : addScheme
|
||||
await fn(payload)
|
||||
emit('done')
|
||||
visible.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => [
|
||||
form.value.spName,
|
||||
form.value.quLevel,
|
||||
form.value.pointName,
|
||||
form.value.keyword,
|
||||
props.taskSpecialty
|
||||
],
|
||||
([spName, quLevel, pointName, keyword, taskSpecialty]) => {
|
||||
// 当题型、难度、专业、知识点、关键字都有值时,才去请求可用题目数
|
||||
if (
|
||||
spName &&
|
||||
quLevel !== null &&
|
||||
quLevel !== undefined &&
|
||||
taskSpecialty &&
|
||||
pointName!== null &&
|
||||
keyword!== null // **关键字也要有选项**
|
||||
// 如果你也想加关键字限制:&& keyword.length > 0
|
||||
) {
|
||||
fetchCount()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
watch(
|
||||
() => [form.value.quNumbers, form.value.quScores],
|
||||
() => {
|
||||
calcSubtotal()
|
||||
}
|
||||
)
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
@@ -0,0 +1,82 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<ele-card :body-style="{ paddingBottom: '2px' }">
|
||||
<el-form label-width="72px" @keyup.enter="search" @submit.prevent="">
|
||||
<el-row :gutter="8">
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="题型">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.spName"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="难度">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.quLevel"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.keywords"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="知识点">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.pointNames"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label-width="16px">
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</ele-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields] = useFormData({
|
||||
taskId: '',
|
||||
spName: '',
|
||||
quLevel: '',
|
||||
keywords: '',
|
||||
pointNames: '',
|
||||
quNumbers: '',
|
||||
quScores: '',
|
||||
subtotalScore: ''
|
||||
});
|
||||
|
||||
/** 搜索 */
|
||||
const search = () => {
|
||||
emit('search', { ...form });
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
</script>
|
||||
|
223
src/views/task/collegeexam/components/steps/step1/index.vue
Normal file
223
src/views/task/collegeexam/components/steps/step1/index.vue
Normal file
@@ -0,0 +1,223 @@
|
||||
<template>
|
||||
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="题型" prop="spName">
|
||||
<el-select v-model="queryParams.spName" placeholder="请选择题型" clearable class="!w-240px">
|
||||
<el-option
|
||||
v-for="item in CourseOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</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"
|
||||
plain
|
||||
@click="openEdit('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="方案ID" align="center" prop="taskId" />
|
||||
<el-table-column label="题型" align="center" prop="spName" />
|
||||
<el-table-column label="难度" align="center" prop="quLevel">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.quLevel === '0'">简单</span>
|
||||
<span v-else-if="scope.row.quLevel === '1'">一般</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="关键字" 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">
|
||||
<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.schemeId)"
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
<scheme-edit v-model="showEdit" v-if="showEdit" :form-data="props.formData" :task-specialty="props.taskSpecialty" :task-Id="props.taskId" ref="taskEditRef" @done="reload"/>
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/scheme';
|
||||
|
||||
import SchemeEdit from './components/step-edit.vue';
|
||||
import SchemeSearch from './components/step-search.vue';
|
||||
const CourseOptions = ref<string[]>([])
|
||||
import {
|
||||
getCourseList,
|
||||
} from '@/api/system/scheme'
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
formData:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
const showOpenTemplate = ref(false);
|
||||
|
||||
const showAdd = 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: [],
|
||||
taskId: props.taskId ,
|
||||
spName:undefined
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
// 1. 并行拉下拉所需的选项
|
||||
const [courses] = await Promise.all([
|
||||
getCourseList(),
|
||||
|
||||
])
|
||||
CourseOptions.value = courses
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await SmsChannelApi.pageSchemes(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 reload = () => {
|
||||
getList()
|
||||
}
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref();
|
||||
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
console.log(id+"idididid")
|
||||
await SmsChannelApi.removeScheme(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
</script>
|
@@ -0,0 +1,106 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="isVisible" :title="'添加试卷'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="试卷数目" prop="num">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="form.num"
|
||||
placeholder="请输入试卷数目"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="save">
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { addPaper } from '@/api/system/paper';
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
/** 修改回显的数据 */
|
||||
data: Object,
|
||||
taskId: String ,
|
||||
taskSpecialty: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['done']);
|
||||
|
||||
/** 弹窗是否打开 */
|
||||
const isVisible = defineModel({ type: Boolean });
|
||||
|
||||
/** 提交状态 */
|
||||
const loading = ref(false);
|
||||
|
||||
/** 表单实例 */
|
||||
const formRef = ref(null);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
num: '',
|
||||
taskId: ''
|
||||
});
|
||||
|
||||
/** 表单验证规则 */
|
||||
const rules = reactive({
|
||||
num: [
|
||||
{ required: true, message: '请输入试卷数目', trigger: 'blur' },
|
||||
{ pattern: /^[1-9]\d*$/, message: '请输入正整数', trigger: 'blur' }
|
||||
]
|
||||
});
|
||||
|
||||
/** 关闭弹窗 */
|
||||
const handleCancel = () => {
|
||||
isVisible.value = false;
|
||||
};
|
||||
|
||||
/** 保存编辑 */
|
||||
const save = () => {
|
||||
formRef.value?.validate?.((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
addPaper({ num: form.num,taskid: props.taskId ,taskSpecialty:form.taskSpecialty})
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
handleCancel();
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/** 弹窗打开事件 */
|
||||
const handleOpen = () => {
|
||||
resetFields();
|
||||
form.taskId = props.taskId; // 新增时赋值 taskId
|
||||
form.taskSpecialty = props.taskSpecialty; // 新增时赋值 taskSpecialty
|
||||
nextTick(() => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
});
|
||||
};
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
|
@@ -0,0 +1,181 @@
|
||||
<template>
|
||||
|
||||
|
||||
<Dialog v-model="visible" :title="'修改试卷'" width="460" @open="handleOpen" center>
|
||||
|
||||
|
||||
<div style="margin-bottom: 16px">
|
||||
<strong>试卷ID:</strong>{{ paperId }}
|
||||
</div>
|
||||
|
||||
<!-- 滚动区域 -->
|
||||
<div style="max-height: 400px; overflow-y: auto;">
|
||||
<!-- 分组展示题目 -->
|
||||
<div
|
||||
v-for="(items, subject) in sortedGroupedQuestions"
|
||||
:key="subject"
|
||||
>
|
||||
<h4 class="mb-2 text-base text-gray-700 font-medium">
|
||||
{{ subject }}
|
||||
<span v-if="schemeMap[subject]" class="text-sm text-gray-500 font-normal">
|
||||
(每小题 {{ parseFloat(schemeMap[subject].quScores).toFixed(1) }} 分,共 {{ parseFloat(schemeMap[subject].subtotalScore).toFixed(1) }} 分)
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
|
||||
|
||||
<el-card
|
||||
v-for="(item, index) in items"
|
||||
:key="item.quId"
|
||||
class="mb-3"
|
||||
shadow="never"
|
||||
>
|
||||
<div class="text-gray-500 mb-2">题目 {{ index + 1 }}</div>
|
||||
<div v-html="item.content" class="mb-2"></div>
|
||||
|
||||
<!-- 🎯 判断是否为编程题 -->
|
||||
<div v-if="item.subjectName === '编程题'">
|
||||
<!-- 编程题解析 -->
|
||||
<div class="mt-2">
|
||||
<div class="font-bold text-blue-600">解析</div>
|
||||
<pre
|
||||
style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto;"
|
||||
>{{ item.analysis }}</pre>
|
||||
</div>
|
||||
<div
|
||||
v-for="(answer, aIndex) in item.answerList"
|
||||
:key="answer.answerId"
|
||||
class="mb-1"
|
||||
>
|
||||
测试用例 {{ aIndex + 1 }}:输入: {{ answer.contentIn }} | 输出: {{ answer.content }} | 占比: {{ answer.scoreRate }}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 📝 普通题目的选项显示 -->
|
||||
<div v-else-if="item.answerList && item.answerList.length">
|
||||
<div
|
||||
v-for="(answer, aIndex) in item.answerList.slice().sort((a, b) => a.sort - b.sort)"
|
||||
:key="answer.answerId"
|
||||
:style="{
|
||||
color: answer.isRight === '0' ? 'green' : 'inherit',
|
||||
fontWeight: answer.isRight === '0' ? 'bold' : 'normal'
|
||||
}"
|
||||
class="mb-1"
|
||||
>
|
||||
{{ String.fromCharCode(65 + aIndex) }}. {{ answer.content }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, computed } from 'vue';
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { getPaperDetailByTaskId } from '@/api/system/paper';
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps({
|
||||
paperId: String
|
||||
});
|
||||
|
||||
const emit = defineEmits(['done']);
|
||||
const visible = defineModel({ type: Boolean });
|
||||
const loading = ref(false);
|
||||
const formRef = ref(null);
|
||||
const questionList = ref([]); // 用于存放试题列表
|
||||
const schemeMap = ref({});
|
||||
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
paperId: void 0,
|
||||
taskId: '',
|
||||
counts: '',
|
||||
rollUp: '',
|
||||
isAb: '',
|
||||
status: ''
|
||||
});
|
||||
|
||||
const rules = reactive({});
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
const groupedQuestions = ref({});
|
||||
|
||||
const subjectPriority = {
|
||||
'选择题': 1,
|
||||
'多选题': 2,
|
||||
'判断题': 3,
|
||||
'编程题': 4,
|
||||
'其他': 5
|
||||
};
|
||||
|
||||
const groupBySubjectName = (list) => {
|
||||
const group = {};
|
||||
|
||||
list.forEach((item) => {
|
||||
const subject = item.subjectName || '其他'; // 默认为'其他'
|
||||
if (!group[subject]) {
|
||||
group[subject] = [];
|
||||
}
|
||||
group[subject].push(item);
|
||||
});
|
||||
|
||||
groupedQuestions.value = group;
|
||||
};
|
||||
|
||||
const sortedGroupedQuestions = computed(() => {
|
||||
return Object.keys(groupedQuestions.value)
|
||||
.sort((a, b) => (subjectPriority[a] || subjectPriority['其他']) - (subjectPriority[b] || subjectPriority['其他']))
|
||||
.reduce((acc, subject) => {
|
||||
acc[subject] = groupedQuestions.value[subject];
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
|
||||
const handleOpen = () => {
|
||||
resetFields();
|
||||
questionList.value = [];
|
||||
|
||||
if (props.paperId) {
|
||||
loading.value = true;
|
||||
getPaperDetailByTaskId(props.paperId)
|
||||
.then((res) => {
|
||||
const { educationPaperSchemeList, examQuestionList } = res.data || {};
|
||||
if (Array.isArray(examQuestionList)) {
|
||||
questionList.value = examQuestionList;
|
||||
groupBySubjectName(examQuestionList); // 分类
|
||||
}
|
||||
|
||||
if (Array.isArray(educationPaperSchemeList)) {
|
||||
// 构建以题型名为 key 的映射表(spName 与 scheme 对应)
|
||||
schemeMap.value = educationPaperSchemeList.reduce((acc, item) => {
|
||||
acc[item.spName] = item;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
assignFields(res);
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(`获取试卷详情失败: ${e.message}`);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
|
||||
|
||||
<Dialog v-model="visible" :title="'查看试卷'" width="860" @open="handleOpen" center>
|
||||
|
||||
|
||||
<div style="margin-bottom: 16px">
|
||||
<strong>试卷ID:</strong>
|
||||
{{ paperId ? paperId : '未提供试卷ID' }}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 滚动区域 -->
|
||||
<div style="max-height: 400px; overflow-y: auto;">
|
||||
<!-- 分组展示题目 -->
|
||||
<div
|
||||
v-for="(items, subject) in sortedGroupedQuestions"
|
||||
:key="subject"
|
||||
>
|
||||
<h4 class="mb-2 text-base text-gray-700 font-medium">
|
||||
{{ subject }}
|
||||
<span v-if="schemeMap[subject]" class="text-sm text-gray-500 font-normal">
|
||||
(每小题 {{ parseFloat(schemeMap[subject].quScores).toFixed(1) }} 分,共 {{ parseFloat(schemeMap[subject].subtotalScore).toFixed(1) }} 分)
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
|
||||
|
||||
<el-card
|
||||
v-for="(item, index) in items"
|
||||
:key="item.quId"
|
||||
class="mb-3"
|
||||
shadow="never"
|
||||
>
|
||||
<div class="text-gray-500 mb-2">题目 {{ index + 1 }}</div>
|
||||
<div v-html="item.content" class="mb-2"></div>
|
||||
|
||||
<!-- 🎯 判断是否为编程题 -->
|
||||
<div v-if="item.subjectName === '编程题'">
|
||||
<!-- 编程题解析 -->
|
||||
<div class="mt-2">
|
||||
<div class="font-bold text-blue-600">解析</div>
|
||||
<pre
|
||||
style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto;"
|
||||
>{{ item.analysis }}</pre>
|
||||
</div>
|
||||
<div
|
||||
v-for="(answer, aIndex) in item.answerList"
|
||||
:key="answer.answerId"
|
||||
class="mb-1"
|
||||
>
|
||||
测试用例 {{ aIndex + 1 }}:输入: {{ answer.contentIn }} | 输出: {{ answer.content }} | 占比: {{ answer.scoreRate }}%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 📝 普通题目的选项显示 -->
|
||||
<div v-else-if="item.answerList && item.answerList.length">
|
||||
<div
|
||||
v-for="(answer, aIndex) in item.answerList.slice().sort((a, b) => a.sort - b.sort)"
|
||||
:key="answer.answerId"
|
||||
:style="{
|
||||
color: answer.isRight === '0' ? 'green' : 'inherit',
|
||||
fontWeight: answer.isRight === '0' ? 'bold' : 'normal'
|
||||
}"
|
||||
class="mb-1"
|
||||
>
|
||||
{{ String.fromCharCode(65 + aIndex) }}. {{ answer.content }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, computed } from 'vue';
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { getPaperDetailByTaskId } from '@/api/system/paper';
|
||||
const message = useMessage(); // 消息弹窗
|
||||
|
||||
const props = defineProps({
|
||||
paperId: String // 接收父组件传来的 paperId
|
||||
});
|
||||
|
||||
const emit = defineEmits(['done']);
|
||||
const visible = defineModel({ type: Boolean });
|
||||
const loading = ref(false);
|
||||
const formRef = ref(null);
|
||||
const questionList = ref([]); // 用于存放试题列表
|
||||
const schemeMap = ref({});
|
||||
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
paperId: void 0,
|
||||
taskId: '',
|
||||
counts: '',
|
||||
rollUp: '',
|
||||
isAb: '',
|
||||
status: ''
|
||||
});
|
||||
|
||||
const rules = reactive({});
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const groupedQuestions = ref({});
|
||||
|
||||
const subjectPriority = {
|
||||
'选择题': 1,
|
||||
'多选题': 2,
|
||||
'判断题': 3,
|
||||
'编程题': 4,
|
||||
'其他': 5
|
||||
};
|
||||
|
||||
// 根据试题的类型进行分组
|
||||
const groupBySubjectName = (list) => {
|
||||
const group = {};
|
||||
list.forEach((item) => {
|
||||
const subject = item.subjectName || '其他'; // 默认为'其他'
|
||||
if (!group[subject]) {
|
||||
group[subject] = [];
|
||||
}
|
||||
group[subject].push(item);
|
||||
});
|
||||
groupedQuestions.value = group;
|
||||
};
|
||||
|
||||
// 按题型排序
|
||||
const sortedGroupedQuestions = computed(() => {
|
||||
return Object.keys(groupedQuestions.value)
|
||||
.sort((a, b) => (subjectPriority[a] || subjectPriority['其他']) - (subjectPriority[b] || subjectPriority['其他']))
|
||||
.reduce((acc, subject) => {
|
||||
acc[subject] = groupedQuestions.value[subject];
|
||||
return acc;
|
||||
}, {});
|
||||
});
|
||||
|
||||
// 打开弹窗时的处理逻辑
|
||||
const handleOpen = (type, paperId) => {
|
||||
resetFields();
|
||||
questionList.value = [];
|
||||
console.log(paperId+"paperIdpaperId")
|
||||
if (props.paperId) {
|
||||
loading.value = true;
|
||||
getPaperDetailByTaskId(props.paperId)
|
||||
.then((res) => {
|
||||
const { educationPaperSchemeList, examQuestionList } = res || {};
|
||||
if (Array.isArray(examQuestionList)) {
|
||||
questionList.value = examQuestionList;
|
||||
groupBySubjectName(examQuestionList); // 分类
|
||||
}
|
||||
|
||||
if (Array.isArray(educationPaperSchemeList)) {
|
||||
// 构建以题型名为 key 的映射表(spName 与 scheme 对应)
|
||||
schemeMap.value = educationPaperSchemeList.reduce((acc, item) => {
|
||||
acc[item.spName] = item;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
assignFields(res);
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(`获取试卷详情失败: ${e.message}`);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
@@ -0,0 +1,72 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<ele-card :body-style="{ paddingBottom: '2px' }">
|
||||
<el-form label-width="72px" @keyup.enter="search" @submit.prevent="">
|
||||
<el-row :gutter="8">
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="试卷编号">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.paperId"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="抽卷方式">
|
||||
<el-select v-model="form.rollUp" placeholder="请选择抽卷方式">
|
||||
<el-option :label="'固定'" :value="0" />
|
||||
<el-option :label="'AB卷'" :value="1" />
|
||||
<el-option :label="'随机'" :value="2" />
|
||||
<el-option :label="'自选'" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="状态">
|
||||
<dict-data
|
||||
code="sys_common_status_other"
|
||||
type="select"
|
||||
v-model="form.status"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label-width="16px">
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</ele-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields] = useFormData({
|
||||
paperId: '',
|
||||
counts: '',
|
||||
rollUp: '',
|
||||
isAb: '',
|
||||
status: '',
|
||||
});
|
||||
|
||||
/** 搜索 */
|
||||
const search = () => {
|
||||
emit('search', { ...form });
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
</script>
|
||||
|
@@ -0,0 +1,336 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="visible" :title="'试卷调整'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
@submit.prevent=""
|
||||
>
|
||||
|
||||
<!-- 抽卷方式 -->
|
||||
<el-form-item label="抽卷方式" prop="type">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio :label="0">固定</el-radio>
|
||||
<el-radio :label="1">AB卷</el-radio>
|
||||
<el-radio :label="2">随机</el-radio>
|
||||
<el-radio :label="3">自选</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 固定卷:显示一个下拉 -->
|
||||
<el-form-item label="选择试卷" v-if="form.type === 0" prop="paper">
|
||||
<el-select v-model="form.paper" placeholder="请选择试卷">
|
||||
<el-option
|
||||
v-for="item in PaperOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<!-- AB卷:显示两个下拉 -->
|
||||
<el-form-item label="试卷A" v-if="form.type === 1" prop="paperA">
|
||||
<el-select v-model="form.paperA" placeholder="请选择试卷A">
|
||||
<el-option
|
||||
v-for="item in PaperOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="试卷B" v-if="form.type === 1" prop="paperB">
|
||||
<el-select v-model="form.paperB" placeholder="请选择试卷B">
|
||||
<el-option
|
||||
v-for="item in PaperOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="save">保存</el-button>
|
||||
</template>
|
||||
|
||||
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick,onMounted,watch} from 'vue';
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { updatePaper ,getPaperList} from '@/api/system/paper';
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const props = defineProps({
|
||||
/** 修改回显的数据 */
|
||||
data: Object,
|
||||
taskId: String,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['done']);
|
||||
|
||||
/** 弹窗是否打开 */
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
/** 提交状态 */
|
||||
const loading = ref(false);
|
||||
|
||||
/** 表单实例 */
|
||||
const formRef = ref(null);
|
||||
|
||||
/** 试卷下拉*/
|
||||
const PaperOptions = ref([]);
|
||||
/** 模拟试卷列表(建议替换为真实接口) */
|
||||
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
num: '',
|
||||
taskId: '',
|
||||
type: 0, // 抽卷方式:0固定 1AB卷 2随机
|
||||
paper: '', // 固定使用
|
||||
paperA: '', // AB卷使用
|
||||
paperB: '' // AB卷使用
|
||||
});
|
||||
|
||||
/** 表单验证规则 */
|
||||
const rules = reactive({
|
||||
type: [{ required: true, message: '请选择抽卷方式', trigger: 'change' }],
|
||||
paper: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.type === 0 && !value) {
|
||||
callback(new Error('请选择试卷'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
paperA: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.type === 1 && !value) {
|
||||
callback(new Error('请选择试卷A'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
paperB: [
|
||||
{
|
||||
required: true,
|
||||
trigger: 'change',
|
||||
validator: (rule, value, callback) => {
|
||||
if (form.type === 1 && !value) {
|
||||
callback(new Error('请选择试卷B'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
/** 取消关闭弹窗 */
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
/** 保存编辑(不关闭弹窗) */
|
||||
const save = () => {
|
||||
formRef.value?.validate?.((valid) => {
|
||||
if (!valid) return;
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const type = form.type;
|
||||
|
||||
let payload = {
|
||||
rollUp: 0, // 默认值
|
||||
};
|
||||
|
||||
if (type === 0) {
|
||||
console.log("0放大请求")
|
||||
// 固定卷
|
||||
if (form.paper) {
|
||||
payload = {
|
||||
paperId: form.paper, // 修改为字符串,不是数组
|
||||
rollUp: 0
|
||||
};
|
||||
send(payload)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('请选择试卷');
|
||||
loading.value = false;
|
||||
}
|
||||
} else if (type === 1) {
|
||||
console.log("1放大请求")
|
||||
// AB卷,分别发送 A卷 和 B卷
|
||||
if (form.paperA === form.paperB) {
|
||||
message.error('试卷A和试卷B不能相同');
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.paperA && form.paperB) {
|
||||
const payloadA = {
|
||||
paperId: form.paperA, // 修改为字符串,不是数组
|
||||
rollUp: 1,
|
||||
isAb: 0
|
||||
};
|
||||
const payloadB = {
|
||||
paperId: form.paperB, // 修改为字符串,不是数组
|
||||
rollUp: 1,
|
||||
isAb: 1
|
||||
};
|
||||
|
||||
Promise.all([send(payloadA), send(payloadB)])
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
message.error('请选择试卷A和试卷B');
|
||||
loading.value = false;
|
||||
}
|
||||
} else if (type === 2) {
|
||||
console.log("2放大请求")
|
||||
// 随机卷
|
||||
payload = {
|
||||
|
||||
rollUp: 2
|
||||
};
|
||||
send(payload)
|
||||
.then(() => {
|
||||
message.success('保存成功');
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}else if(type === 3)// 自选卷
|
||||
{
|
||||
payload = {
|
||||
|
||||
rollUp: 3
|
||||
};
|
||||
send(payload)
|
||||
.then(() => {
|
||||
console.log("3放大请求")
|
||||
message.success('保存成功');
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
const send = async (payload) => {
|
||||
|
||||
console.log("12312312"+payload)
|
||||
try {
|
||||
await updatePaper({
|
||||
...payload,
|
||||
taskId: props.taskId,
|
||||
});
|
||||
} catch (e) {
|
||||
message.error('保存失败:' + e.message); // 提示错误
|
||||
throw e; // 确保错误能被捕获
|
||||
} finally {
|
||||
loading.value = false; // 确保最终关闭loading
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 获取题型下拉数据
|
||||
const fetchpaperOptions = async () => {
|
||||
try {
|
||||
console.log(props.taskId+"props.taskIdprops.taskIdprops.taskId")
|
||||
const res = await getPaperList(props.taskId);
|
||||
PaperOptions.value = res
|
||||
.filter(item => item)
|
||||
.map(item => ({ label: item, value: item }));
|
||||
} catch (e) {
|
||||
message.error('获取题型失败:' + e.message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** 弹窗打开时初始化 */
|
||||
const handleOpen = () => {
|
||||
resetFields();
|
||||
form.taskId = props.taskId;
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log("taskId:", props.taskId);
|
||||
if (props.taskId) {
|
||||
fetchpaperOptions();
|
||||
} else {
|
||||
console.error("taskId is not defined");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
watch(() => form.type, (val) => {
|
||||
if (val === 0) {
|
||||
form.paperA = '';
|
||||
form.paperB = '';
|
||||
} else if (val === 1) {
|
||||
form.paper = '';
|
||||
} else {
|
||||
form.paper = '';
|
||||
form.paperA = '';
|
||||
form.paperB = '';
|
||||
}
|
||||
});
|
||||
</script>
|
211
src/views/task/collegeexam/components/steps/step2/index copy.vue
Normal file
211
src/views/task/collegeexam/components/steps/step2/index copy.vue
Normal file
@@ -0,0 +1,211 @@
|
||||
<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>
|
332
src/views/task/collegeexam/components/steps/step2/index.vue
Normal file
332
src/views/task/collegeexam/components/steps/step2/index.vue
Normal file
@@ -0,0 +1,332 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<!-- 搜索表单 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
|
||||
<el-form-item label="试卷编号" prop="paperId">
|
||||
<el-input
|
||||
v-model="queryParams.paperId"
|
||||
placeholder="请输入试卷编号"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="启用状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择启用状态"
|
||||
class="!w-240px"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYS_STATUS)"
|
||||
: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"
|
||||
plain
|
||||
@click="openAddForm"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openSet"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 试卷调整
|
||||
</el-button>
|
||||
<!-- 批量删除按钮 -->
|
||||
<!-- <el-button
|
||||
type="danger"
|
||||
:disabled="selectedRows.length === 0"
|
||||
@click="batchDelete"
|
||||
>
|
||||
批量删除
|
||||
</el-button> -->
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表展示 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" :row-key="row => row.id" ref="tableRef" :selected-rows="selectedRows">
|
||||
|
||||
<!-- 多选列 -->
|
||||
<el-table-column type="selection" width="55" />
|
||||
<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.rollUp === '0'">固定</span>
|
||||
<span v-else-if="scope.row.rollUp === '1'">AB卷</span>
|
||||
<span v-else-if="scope.row.rollUp === '2'">随机</span>
|
||||
<span v-else-if="scope.row.rollUp === '3'">自选</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="AB卷" align="center" prop="isAb">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.isAb === '0'">A卷</span>
|
||||
<span v-else-if="scope.row.isAb === '1'">B卷</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="openLook('look', scope.row.paperId)"
|
||||
v-hasPermi="['system:sms-channel:update']"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<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.paperId)"
|
||||
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="props.taskId"
|
||||
:task-specialty="props.taskSpecialty"
|
||||
ref="taskAddRef"
|
||||
@done="reload"
|
||||
/>
|
||||
<paper-edit
|
||||
v-model="showEdit"
|
||||
:data="current"
|
||||
ref="taskEditRef"
|
||||
@done="reload"
|
||||
/>
|
||||
<paper-look
|
||||
v-model="showLook"
|
||||
ref="taskLookRef"
|
||||
:paper-Id="currentPaperId"
|
||||
/>
|
||||
<paper-set
|
||||
v-model="showSet"
|
||||
:task-Id="taskId"
|
||||
ref="taskSetRef"
|
||||
@done="reload"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, nextTick,onMounted,watch} from 'vue';
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import * as PaperApi 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 { useI18n } from 'vue-i18n';
|
||||
|
||||
defineOptions({ name: 'SystemPaper' });
|
||||
|
||||
const { t } = useI18n();
|
||||
const message = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
// 定义数据项的类型
|
||||
interface PaperItem {
|
||||
paperId: number; // 假设每个项目都有 paperId
|
||||
counts: number;
|
||||
rollUp: string;
|
||||
isAb: string;
|
||||
status: string;
|
||||
// 你可以根据需要扩展更多字段
|
||||
}
|
||||
|
||||
// 当前编辑的数据
|
||||
const current = ref<object>();
|
||||
|
||||
// 弹窗显示状态
|
||||
const showEdit = ref(false);
|
||||
const showLook = ref(false);
|
||||
const showAdd = ref(false);
|
||||
const showSet = ref(false);
|
||||
|
||||
// ref 对应的组件实例
|
||||
const taskAddRef = ref();
|
||||
const taskEditRef = ref();
|
||||
const taskLookRef = ref();
|
||||
const taskSetRef = ref();
|
||||
|
||||
const loading = ref(false); // 列表的加载状态
|
||||
const total = ref(0); // 列表的总页数
|
||||
const list = ref<PaperItem[]>([]); // 列表的数据,明确指定类型
|
||||
const queryFormRef = ref(); // 搜索表单的 ref
|
||||
|
||||
// 多选框绑定的变量,指定为 PaperItem 类型的数组
|
||||
const selectedRows = ref<PaperItem[]>([]);
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
paperId: undefined,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
taskId:props.taskId
|
||||
});
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await PaperApi.pagePapers(queryParams);
|
||||
list.value = res.list;
|
||||
total.value = res.total;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
const reload = () => {
|
||||
|
||||
getList();
|
||||
};
|
||||
|
||||
|
||||
// 搜索操作
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置操作
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 打开新增表单
|
||||
const openAddForm = () => {
|
||||
showAdd.value = true;
|
||||
nextTick(() => {
|
||||
taskAddRef.value?.open();
|
||||
});
|
||||
};
|
||||
|
||||
const openSet = () => {
|
||||
showSet.value = true;
|
||||
nextTick(() => {
|
||||
taskSetRef.value?.open();
|
||||
});
|
||||
};
|
||||
|
||||
// 打开编辑弹窗
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true;
|
||||
current.value = row;
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row);
|
||||
});
|
||||
};
|
||||
const currentPaperId = ref<number | null>(null);
|
||||
|
||||
const openLook = (type: string, paperId?: number) => {
|
||||
showLook.value = true;
|
||||
currentPaperId.value = paperId ?? null; // 设置当前选中的 paperId
|
||||
nextTick(() => {
|
||||
taskLookRef.value?.open(type, paperId);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
await message.delConfirm();
|
||||
await PaperApi.removePaper(id);
|
||||
message.success(t('common.delSuccess'));
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 批量删除操作
|
||||
const batchDelete = async () => {
|
||||
try {
|
||||
const ids = selectedRows.value.map(item => item.paperId); // 使用 paperId 属性
|
||||
if (ids.length > 0) {
|
||||
await message.delConfirm();
|
||||
await PaperApi.removePapers(ids); // 调用批量删除接口
|
||||
message.success(t('common.delSuccess'));
|
||||
await getList();
|
||||
selectedRows.value = []; // 清空选择的行
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化操作
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
watch(selectedRows, (newVal) => {
|
||||
console.log('选中的行:', newVal);
|
||||
});
|
||||
|
||||
</script>
|
193
src/views/task/collegeexam/components/steps/step3/index.vue
Normal file
193
src/views/task/collegeexam/components/steps/step3/index.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 使用 el-tabs 组件创建可切换的选项卡 -->
|
||||
<el-tabs v-model="activeTab" style="margin-top: 20px;">
|
||||
<!-- 第一个 tab - 通用参数 -->
|
||||
<el-tab-pane label="通用参数" name="tab1">
|
||||
<el-form :model="form" label-width="200px" style="margin-top: 20px;">
|
||||
<!-- 是否使用监考密码验证 -->
|
||||
<el-form-item label="是否使用监考密码验证">
|
||||
<el-switch
|
||||
v-model="form.isExamPassword"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 是否使用监考密码验证 -->
|
||||
<el-form-item label="是否启用考场设置">
|
||||
<el-switch
|
||||
v-model="form.isSession"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
<!-- 监考密码 -->
|
||||
<el-form-item label="监考密码" v-if="form.isExamPassword === '0'">
|
||||
<el-input v-model="form.examPassword" placeholder="请输入监考密码" @input="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 禁止学生使用U盘 -->
|
||||
<el-form-item label="禁止学生使用U盘">
|
||||
<el-switch
|
||||
v-model="form.usb"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 练习成绩保存 -->
|
||||
<el-form-item label="练习成绩保存">
|
||||
<el-radio-group v-model="form.saveGrades" @change="handleFormChange">
|
||||
<el-radio label="0">最高成绩</el-radio>
|
||||
<el-radio label="1">最新成绩</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否显示重答按钮">
|
||||
<el-switch
|
||||
v-model="form.isRepeat"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<el-form-item label="是否显示答案按钮">
|
||||
<el-switch
|
||||
v-model="form.isAnswer"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学生是否可以查看试卷">
|
||||
<el-switch
|
||||
v-model="form.isLook"
|
||||
active-value="0"
|
||||
inactive-value="1"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
@change="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="定时检查与学生端联通性,每">
|
||||
<el-input-number v-model="form.isConnect" label="分钟" @change="handleFormChange" />
|
||||
<span>分钟传一次,断联直接交卷</span>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 第二个 tab - 试卷答题文件 -->
|
||||
<el-tab-pane label="试卷答题文件" name="tab2">
|
||||
<el-form :model="form" label-width="200px" style="margin-top: 20px;">
|
||||
<!-- 学生文件存放系统盘 -->
|
||||
<el-form-item label="驱动器为学生文件存放系统盘">
|
||||
<el-radio-group v-model="form.driver" @change="handleFormChange">
|
||||
<el-radio label="C">C</el-radio>
|
||||
<el-radio label="D">D</el-radio>
|
||||
<el-radio label="E">E</el-radio>
|
||||
<el-radio label="F">F</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 考试目录名称 -->
|
||||
<el-form-item label="考试目录名称">
|
||||
<el-input v-model="form.directory" placeholder="请输入考试目录名称" @input="handleFormChange" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 定时上传考试目录 -->
|
||||
<el-form-item label="定时上传考试目录每">
|
||||
<el-input-number v-model="form.uploadTime" :min="1" label="分钟" @change="handleFormChange" />
|
||||
<span>分钟传一次</span>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 完成考试后是否删除考试目录 -->
|
||||
<el-form-item label="完成考试后是否删除考试目录">
|
||||
<el-radio-group v-model="form.isDel" @change="handleFormChange">
|
||||
<el-radio label="0">是</el-radio>
|
||||
<el-radio label="1">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { getParam, updateParam } from '@/api/system/param' // 引入后台请求方法
|
||||
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
// 字段初始值设置为空或空字符串
|
||||
const form = ref({
|
||||
isExamPassword: '', // 是否使用监考密码验证
|
||||
examPassword: '', // 监考密码
|
||||
usb: '', // 是否禁止U盘
|
||||
saveGrades: '', // 成绩保存方式
|
||||
driver: '', // 存放系统盘
|
||||
directory: '', // 考试目录名称
|
||||
uploadTime: '', // 上传间隔时间
|
||||
isDel: '',
|
||||
isRepeat:'', // 是否删除目录
|
||||
isAnswer:'',
|
||||
isLook:'',
|
||||
isConnect:''
|
||||
})
|
||||
|
||||
const activeTab = ref('tab1')
|
||||
|
||||
// 页面加载时用 taskId 请求后端并赋值
|
||||
onMounted(async () => {
|
||||
if (props.taskId) {
|
||||
try {
|
||||
const res = await getParam({ taskId: props.taskId })
|
||||
const data = res || {}
|
||||
console.log('任务详情:', data)
|
||||
|
||||
// 用 Object.assign 将后端数据合并到 form
|
||||
Object.assign(form.value, data)
|
||||
} catch (error) {
|
||||
console.error('获取任务详情失败', error)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 表单变化时触发请求
|
||||
const handleFormChange = async () => {
|
||||
if (form.value.isExamPassword === '1') {
|
||||
form.value.examPassword = ''
|
||||
}
|
||||
try {
|
||||
// 发送请求以更新数据
|
||||
const response = await updateParam(form.value)
|
||||
console.log('更新结果:', response)
|
||||
} catch (error) {
|
||||
console.error('更新数据失败', error)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可以为 tab 设置额外的样式,具体根据需求定制 */
|
||||
</style>
|
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="visible"
|
||||
:title="isUpdate ? '修改考场设置' : '添加考场设置'"
|
||||
width="460"
|
||||
center
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="试卷ID" prop="taskId">
|
||||
<el-input clearable v-model="form.taskId" placeholder="请输入试卷ID" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="批次" prop="batch">
|
||||
<el-input clearable v-model="form.batch" placeholder="请输入批次" disabled/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="考试开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择考试开始时间"
|
||||
class="ele-fluid"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="考试结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择考试结束时间"
|
||||
class="ele-fluid"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开始前(分钟)允许入场" prop="allowEntry">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="form.allowEntry"
|
||||
placeholder="请输入允许提前入场的分钟数"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开始后(分钟)禁止入场" prop="endAllowEntry">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="form.endAllowEntry"
|
||||
placeholder="请输入开始后禁止入场的分钟数"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="开始后(分钟)允许交卷" prop="allowSubmission">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="form.allowSubmission"
|
||||
placeholder="请输入允许交卷的分钟数"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否启用" prop="status">
|
||||
|
||||
|
||||
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYS_YES_NO)"
|
||||
:key="dict.value"
|
||||
:label="String(dict.value)"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="save"
|
||||
:disabled="false"
|
||||
>
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { addSession, updateSession } from '@/api/system/session'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
const emit = defineEmits(['done'])
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
taskId: String,
|
||||
})
|
||||
|
||||
const visible = defineModel({ type: Boolean })
|
||||
const isUpdate = ref(false)
|
||||
const loading = ref(false)
|
||||
const formRef = ref()
|
||||
|
||||
// 表单模型
|
||||
const form = ref({
|
||||
sessionId: undefined,
|
||||
taskId: props.taskId ?? '',
|
||||
batch: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
allowEntry: '',
|
||||
endAllowEntry: '',
|
||||
allowSubmission: '',
|
||||
status: '', // 状态(0禁用,1启用)
|
||||
})
|
||||
|
||||
// 表单校验规则
|
||||
const rules = reactive({
|
||||
taskId: [{ required: true, message: '请输入试卷ID', trigger: 'blur' }],
|
||||
batch: [{ required: true, message: '请输入批次', trigger: 'blur' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
|
||||
allowEntry: [{ required: true, message: '请输入允许入场时间', trigger: 'blur' }],
|
||||
endAllowEntry: [{ required: true, message: '请输入禁止入场时间', trigger: 'blur' }],
|
||||
allowSubmission: [{ required: true, message: '请输入允许交卷时间', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||||
})
|
||||
|
||||
// 打开弹窗方法
|
||||
const open = async (type: 'create' | 'update', data?: any) => {
|
||||
console.log(type+"isUpdate.valueisUpdate.valueisUpdate.value")
|
||||
visible.value = true
|
||||
isUpdate.value = type === 'update'
|
||||
|
||||
if (isUpdate.value && data) {
|
||||
form.value = { ...data }
|
||||
} else {
|
||||
form.value = {
|
||||
sessionId: undefined,
|
||||
taskId: props.taskId ?? '',
|
||||
batch: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
allowEntry: '',
|
||||
endAllowEntry: '',
|
||||
allowSubmission: '',
|
||||
status: '',
|
||||
}
|
||||
|
||||
// 设置批次为 当前时间 + 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');
|
||||
//把batch 赋值 randomNum
|
||||
|
||||
// 把 batch 设置为当前时间+随机数
|
||||
form.value.batch = `${datetimeStr}${randomNum}`;
|
||||
}
|
||||
|
||||
await nextTick()
|
||||
formRef.value?.clearValidate?.()
|
||||
}
|
||||
|
||||
// 保存按钮
|
||||
const save = async () => {
|
||||
await formRef.value.validate()
|
||||
loading.value = true
|
||||
try {
|
||||
const fn = isUpdate.value ? updateSession : addSession
|
||||
await fn({ ...form.value })
|
||||
emit('done')
|
||||
visible.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
@@ -0,0 +1,64 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<ele-card :body-style="{ paddingBottom: '2px' }">
|
||||
<el-form label-width="72px" @keyup.enter="search" @submit.prevent="">
|
||||
<el-row :gutter="8">
|
||||
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="批次">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.batch"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="状态">
|
||||
<dict-data
|
||||
code="sys_common_status_other"
|
||||
type="select"
|
||||
v-model="form.status"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label-width="16px">
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</ele-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields] = useFormData({
|
||||
startTime: '',
|
||||
batch: '',
|
||||
endTime: '',
|
||||
allowEntry: '',
|
||||
endAllowEntry: '',
|
||||
allowSubmission: '',
|
||||
status: '',
|
||||
|
||||
});
|
||||
|
||||
/** 搜索 */
|
||||
const search = () => {
|
||||
emit('search', { ...form });
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
</script>
|
||||
|
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
|
||||
<Dialog v-model="visible" :title="'试卷调整'" width="860" @open="handleOpen" center>
|
||||
|
||||
|
||||
<div>{{rows}}</div>
|
||||
<div>{{taskId}}</div>
|
||||
<el-transfer v-model="value" :data="data" />
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="save">保存</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { getPaperListByType } from '@/api/system/paper';
|
||||
import { setSessions } from '@/api/system/session';
|
||||
import type { FormInstance } from 'element-plus' // ✅ 引入 FormInstance 类型
|
||||
const message = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
taskId: String,
|
||||
rows: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
interface Option {
|
||||
key: string;
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const data = ref<Option[]>([]);
|
||||
const value = ref<string[]>([]);
|
||||
|
||||
const emit = defineEmits(['done']);
|
||||
const visible = defineModel({ type: Boolean });
|
||||
const loading = ref(false);
|
||||
|
||||
// ✅ 给 formRef 添加类型注解
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
const [form, resetFields] = useFormData({
|
||||
taskId: '',
|
||||
});
|
||||
|
||||
const handleOpen = async () => {
|
||||
resetFields();
|
||||
form.taskId = props.taskId;
|
||||
value.value = [];
|
||||
|
||||
try {
|
||||
const res = await getPaperListByType({
|
||||
taskId: props.taskId,
|
||||
rows: props.rows
|
||||
});
|
||||
|
||||
data.value = (res?.paperId || []).map((item: string) => ({
|
||||
key: item,
|
||||
label: item,
|
||||
disabled: false
|
||||
}));
|
||||
|
||||
value.value = res?.selectedIds || [];
|
||||
|
||||
} catch (e) {
|
||||
message.error('获取试卷列表失败');
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
// ✅ 类型安全地调用 clearValidate
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
if (!value.value.length) {
|
||||
message.warning('请至少选择一个试卷');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const params = {
|
||||
selectedIds: value.value,
|
||||
rows: props.rows
|
||||
};
|
||||
await setSessions(params);
|
||||
message.success('保存成功');
|
||||
emit('done');
|
||||
visible.value = false;
|
||||
} catch (error) {
|
||||
message.error('保存失败');
|
||||
}
|
||||
};
|
||||
</script>
|
227
src/views/task/collegeexam/components/steps/step4/index.vue
Normal file
227
src/views/task/collegeexam/components/steps/step4/index.vue
Normal file
@@ -0,0 +1,227 @@
|
||||
<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-button
|
||||
type="warning"
|
||||
class="ele-btn-icon"
|
||||
:disabled="!selections.length"
|
||||
@click="papreSet()"
|
||||
>
|
||||
批量分配
|
||||
</el-button>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<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="batch" />
|
||||
<el-table-column label="考试开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="考试结束时间" align="center" prop="endTime" />
|
||||
<el-table-column label="开始考试前分钟,允许入场" align="center" prop="allowEntry" />
|
||||
<el-table-column label="开始考试后分钟,禁止入场" align="center" prop="endAllowEntry" />
|
||||
<el-table-column label="开始考试后分钟,允许交卷" align="center" prop="allowSubmission" />
|
||||
<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.sessionId)"
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
<session-edit v-model="showEdit" :data="current" :task-Id="taskId" @done="reload" ref="taskEditRef" />
|
||||
<paper-set v-model="showSet" :task-Id="taskId" :rows="selectedRows" @done="reload"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
|
||||
import SessionSearch from './components/step-search.vue';
|
||||
import SessionEdit from './components/step-edit.vue';
|
||||
import PaperSet from './components/step-set.vue';
|
||||
import { DICT_TYPE } from '@/utils/dict';
|
||||
|
||||
defineOptions({ name: 'SystemPaper' });
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const sessionAddRef = ref();
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
|
||||
const showLook = ref(false);
|
||||
|
||||
const showAdd = ref(false);
|
||||
|
||||
const showSet = ref(false);
|
||||
|
||||
|
||||
const taskEditRef = 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: [],
|
||||
taskId: props.taskId
|
||||
})
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await SmsChannelApi.pageSessions(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 selections = ref([]);
|
||||
|
||||
const selectedRows = ref<string[]>([]);
|
||||
|
||||
const papreSet = () => {
|
||||
const rows = selections.value;
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRows.value = rows.map((d: any) => d.sessionId); // 保存选中的行数据
|
||||
showSet.value = true; // 打开弹窗
|
||||
};
|
||||
|
||||
const handleSelectionChange = (rows) => {
|
||||
selections.value = rows;
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref();
|
||||
const openForm = (type: string, ) => {
|
||||
showEdit.value = true
|
||||
|
||||
console.log( current.value )
|
||||
taskEditRef.value?.open(type)
|
||||
}
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
console.log( current.value )
|
||||
taskEditRef.value?.open(type, row)
|
||||
|
||||
}
|
||||
|
||||
const reload = (where) => {
|
||||
getList()
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await SmsChannelApi.removeSession(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
||||
<ele-page style="height: 65vh;">
|
||||
<el-row :gutter="16" style="height: 100%;">
|
||||
<!-- 左侧任务列表 -->
|
||||
<el-col :span="10" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card title="试卷任务列表" :body-style="{ paddingTop: '8px' }">
|
||||
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="taskTableRef"
|
||||
:data="mockTaskList"
|
||||
highlight-current-row
|
||||
@row-click="handleTaskRowClick"
|
||||
style="min-width: 1000px; width: 100%; "
|
||||
>
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="考试批次" align="center" prop="batch" />
|
||||
<el-table-column label="考试开始时间" align="center" prop="startTime" />
|
||||
<el-table-column label="考试结束时间" align="center" prop="endTime" />
|
||||
<el-table-column label="开始考试前分钟,允许入场" align="center" prop="allowEntry" />
|
||||
<el-table-column label="开始考试后分钟,禁止入场" align="center" prop="endAllowEntry" />
|
||||
<el-table-column label="开始考试后分钟,允许交卷" align="center" prop="allowSubmission" />
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="taskTotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧人员分配列表 -->
|
||||
<el-col :span="14" style="height: 100%;">
|
||||
<div style="height: 100%; overflow: auto;">
|
||||
<ele-card :body-style="{ paddingTop: '8px' }">
|
||||
<div class="mb-2">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="openAssignDialog()"
|
||||
>
|
||||
分配人员
|
||||
</el-button>
|
||||
<el-button type="danger" @click="removeBatch()">删除</el-button>
|
||||
<el-button @click="exportData()">导出</el-button>
|
||||
</div>
|
||||
<ContentWrap style="overflow-x: auto;">
|
||||
<el-table
|
||||
table-layout="fixed"
|
||||
ref="personTableRef"
|
||||
:data="filteredPersonList"
|
||||
style="min-width: 1000px; width: 100%"
|
||||
highlight-current-row
|
||||
@selection-change="selections = $event"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column type="index" label="#" width="50" align="center" />
|
||||
<el-table-column label="用户名称" align="center" prop="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
<!-- <el-table-column label="操作" align="center" width="180">
|
||||
<template #default="{ row }">
|
||||
<el-link type="primary" @click="openEdit(row)">修改</el-link>
|
||||
<el-divider direction="vertical" />
|
||||
<el-link type="danger" @click="removeBatch(row)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<Pagination
|
||||
:stutotal="stutotal"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<el-empty
|
||||
v-if="filteredPersonList.length === 0"
|
||||
description="暂无分配人员"
|
||||
/>
|
||||
</ele-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ele-page>
|
||||
|
||||
|
||||
|
||||
<PersonSearch :session-id="selectedSessionId" :session-batch="selectedSessionBanch" :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
|
||||
|
||||
</el-form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed } from 'vue';
|
||||
import PersonSearch from './person-serach.vue'
|
||||
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
const visible = ref(true);
|
||||
const selectedSessionId = ref(null);
|
||||
|
||||
const selectedSessionBanch = ref(null);
|
||||
|
||||
const selections = ref([]);
|
||||
const showEdit = ref(false);
|
||||
const current = ref(null);
|
||||
|
||||
const taskTableRef = ref();
|
||||
const personTableRef = ref();
|
||||
const props = defineProps({
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
taskId: props.taskId,
|
||||
sessionId:''
|
||||
})
|
||||
const stuAddRef = ref();
|
||||
|
||||
const showPersonEdit = ref(false)
|
||||
const emit = defineEmits(['done'])
|
||||
const openAssignDialog = () => {
|
||||
if (!selectedSessionId.value) {
|
||||
ElMessage.warning('请先选择左侧任务!')
|
||||
return
|
||||
}
|
||||
showPersonEdit.value = true
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const submitAssign = () => {
|
||||
// 提交逻辑
|
||||
console.log('提交分配人员')
|
||||
assignVisible.value = false
|
||||
}
|
||||
|
||||
|
||||
const mockTaskList = ref([]) ;
|
||||
|
||||
const filteredPersonList = ref([]) ;
|
||||
|
||||
|
||||
const handleTaskRowClick = async (row) => {
|
||||
selectedSessionId.value = row.sessionId;
|
||||
selectedSessionBanch.value=row.batch;
|
||||
|
||||
queryParams.sessionId = row.sessionId;
|
||||
const res = await SmsChannelApi.getSessionStu(queryParams);
|
||||
filteredPersonList.value = res.list;
|
||||
stutotal.value=res.total;
|
||||
};
|
||||
|
||||
|
||||
const openEdit = (row = null) => {
|
||||
current.value = row;
|
||||
showEdit.value = true;
|
||||
};
|
||||
|
||||
const removeBatch = async (row) => {
|
||||
const selectedIds = selections.value.map(item => item.id);
|
||||
console.log('选中的 ID 列表:', selectedIds);
|
||||
console.log('选中的 考场 id:' + selectedSessionId.value);
|
||||
console.log('选中的 试卷任务id:', props.taskId);
|
||||
|
||||
// 弹出确认提示框
|
||||
ElMessageBox.confirm(
|
||||
'确定要删除选中的人员吗?',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
).then(async () => {
|
||||
// 赋值参数
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: selectedSessionId.value,
|
||||
taskId: props.taskId,
|
||||
batch: selectedSessionBanch.value,
|
||||
};
|
||||
|
||||
await PersonApi.removeSessionStu(data);
|
||||
|
||||
// 删除选中的人员
|
||||
for (const sel of selections.value) {
|
||||
const index = filteredPersonList.value.findIndex(p => p.id === sel.id);
|
||||
if (index !== -1) filteredPersonList.value.splice(index, 1);
|
||||
}
|
||||
emit('done')
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
|
||||
console.log('删除操作已取消');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const getList = async () => {
|
||||
const res = await SmsChannelApi.pageSessions(queryParams)
|
||||
mockTaskList.value = res
|
||||
total.value = res.total
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
emit('done')
|
||||
}
|
||||
|
||||
const exportData = () => {
|
||||
console.log('导出功能触发');
|
||||
};
|
||||
|
||||
const reloadPerson = () => {
|
||||
// 模拟刷新
|
||||
console.log('刷新人员列表');
|
||||
};
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
@@ -0,0 +1,331 @@
|
||||
<template>
|
||||
<!-- 搜索 -->
|
||||
<Dialog
|
||||
title="人员分配"
|
||||
v-model="visible"
|
||||
width="80%"
|
||||
append-to-body
|
||||
lock-scroll
|
||||
:destroy-on-close="false"
|
||||
:draggable="false"
|
||||
@close="handleDialogClose"
|
||||
>
|
||||
<ContentWrap>
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="学生名称" prop="username">
|
||||
<el-input
|
||||
v-model="queryParams.username"
|
||||
placeholder="请输入学生名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-select
|
||||
v-model="queryParams.className"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
placeholder="请选择或输入"
|
||||
style="width: 300px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classNameList"
|
||||
:key="item.className"
|
||||
:label="item.className"
|
||||
:value="item.className"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择学生状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
|
||||
<!-- <el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
>
|
||||
<Icon icon="ep:plus" /> 新增
|
||||
</el-button> -->
|
||||
<!-- <el-button
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleImport"
|
||||
>
|
||||
<Icon icon="ep:upload" /> 导入
|
||||
</el-button> -->
|
||||
<el-button
|
||||
type="success"
|
||||
plain
|
||||
@click="handleExport"
|
||||
:loading="exportLoading"
|
||||
>
|
||||
<Icon icon="ep:download" />导出
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" ref="tableRef" :data="list" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="用户编号" align="center" key="id" prop="id" />
|
||||
<el-table-column
|
||||
label="用户名称"
|
||||
align="center"
|
||||
prop="username"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="用户昵称"
|
||||
align="center"
|
||||
prop="nickname"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column
|
||||
label="班级名称"
|
||||
align="center"
|
||||
prop="className"
|
||||
:show-overflow-tooltip="true"
|
||||
/>
|
||||
<el-table-column label="状态" key="status" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
align="center"
|
||||
prop="createTime"
|
||||
:formatter="dateFormatter"
|
||||
width="180"
|
||||
/>
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="open"
|
||||
/>
|
||||
</ContentWrap>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleDialogClose">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- 用户导入对话框 -->
|
||||
<!-- <StudentImportForm ref="importFormRef" @success="open" /> -->
|
||||
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import download from '@/utils/download'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as ClassApi from '@/api/exam/class'
|
||||
import * as PersonApi from '@/api/system/person'
|
||||
defineOptions({ name: 'SystemUser' })
|
||||
const props = defineProps({
|
||||
sessionId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
sessionBatch:{
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const tableRef = ref()
|
||||
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
const classNameList = ref();
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
className: undefined,
|
||||
nickname: undefined,
|
||||
username: undefined,
|
||||
mobile: undefined,
|
||||
status: undefined,
|
||||
deptId: undefined,
|
||||
userType: "2",
|
||||
createTime: [],
|
||||
taskId:'',
|
||||
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
/** 查询列表 */
|
||||
const open = async () => {
|
||||
loading.value = true
|
||||
classNameList.value = await ClassApi.ClassApi.getClassName()
|
||||
queryParams.taskId=props.taskId
|
||||
|
||||
console.log(props.taskId+"props.taskId")
|
||||
console.log(props.sessionId+"props.sessionId")
|
||||
try {
|
||||
const params: any = {
|
||||
...queryParams,
|
||||
sessionId:props.sessionId,
|
||||
startTime: queryParams.createTime?.[0] || null,
|
||||
endTime: queryParams.createTime?.[1] || null,
|
||||
}
|
||||
|
||||
const data = await PersonApi.getSessionStuBySearch(params)
|
||||
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
const emit = defineEmits(['done'])
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
open()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 用户导入 */
|
||||
const importFormRef = ref()
|
||||
const handleImport = () => {
|
||||
importFormRef.value.open()
|
||||
}
|
||||
const multipleSelection = ref<UserApi.UserVO[]>([])
|
||||
|
||||
const handleSelectionChange = (val: UserApi.UserVO[]) => {
|
||||
multipleSelection.value = val
|
||||
}
|
||||
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const exportLoading = ref(false)
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
// 导出的二次确认
|
||||
await message.exportConfirm()
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await UserApi.exportUser(queryParams)
|
||||
download.excel(data, '用户数据.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDialogClose = () => {
|
||||
visible.value = false;
|
||||
multipleSelection.value = [] // 清空选中的数据
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
};
|
||||
const confirmSelection = async () => {
|
||||
const selectedIds = multipleSelection.value.map(item => item.id)
|
||||
const count = selectedIds.length
|
||||
|
||||
if (count === 0) {
|
||||
ElMessage.warning('请先选择学生')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`已选中 ${count} 个学生,是否确认分配?`,
|
||||
'确认分配',
|
||||
{
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
|
||||
const data = {
|
||||
studentIds: selectedIds,
|
||||
sessionId: props.sessionId,
|
||||
taskId: props.taskId,
|
||||
batch:props.sessionBatch
|
||||
}
|
||||
await PersonApi.setSessionStu(data)
|
||||
|
||||
ElMessage.success(`成功分配 ${count} 个学生`)
|
||||
emit('done')
|
||||
visible.value = false
|
||||
multipleSelection.value = []
|
||||
tableRef.value?.clearSelection()
|
||||
|
||||
} catch (err) {
|
||||
// 用户取消时会进入这里,不需要处理
|
||||
console.log('用户取消了分配')
|
||||
}
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
open()
|
||||
})
|
||||
</script>
|
250
src/views/task/collegeexam/components/steps/step5/index.vue
Normal file
250
src/views/task/collegeexam/components/steps/step5/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<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="openSearch('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 添加学生</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="showAssignButton"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openEdit('create')"
|
||||
v-hasPermi="['system:sms-channel:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 分配场次</el-button
|
||||
>
|
||||
<el-button
|
||||
type="danger"
|
||||
class="ele-btn-del"
|
||||
:disabled="!selections.length"
|
||||
@click="handleDeletes()"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<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="username" />
|
||||
<el-table-column label="用户昵称" align="center" prop="nickname" />
|
||||
<el-table-column label="班级名称" align="center" prop="className"/>
|
||||
<el-table-column label="分配考场" align="center" prop="batch"/>
|
||||
|
||||
<!-- <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.schemeId)"
|
||||
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>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
|
||||
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
|
||||
import * as SmsPersonlApi from '@/api/system/person';
|
||||
import PersonEdit from './components/person-edit.vue';
|
||||
import PersonSearch from './components/person-serach.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
import { fa } from 'element-plus/es/locale';
|
||||
|
||||
defineOptions({ name: 'SystemSmsChannel' })
|
||||
const showAssignButton = ref(true) // 控制按钮显示
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
taskSpecialty: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
formData:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
const showPersonEdit = ref(false)
|
||||
/** 当前编辑数据 */
|
||||
const current = ref<object>();
|
||||
|
||||
/** 是否显示编辑弹窗 */
|
||||
const showEdit = ref(false);
|
||||
|
||||
const stuAddRef = ref();
|
||||
|
||||
|
||||
|
||||
const taskEditRef = 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: [],
|
||||
taskId: props.taskId
|
||||
})
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
|
||||
try {
|
||||
const resButton = await SmsChannelApi.fetchNoMes(props.taskId)
|
||||
|
||||
if (resButton==='200') {
|
||||
showAssignButton.value = true
|
||||
} else {
|
||||
showAssignButton.value = false
|
||||
}
|
||||
|
||||
|
||||
|
||||
const res = await SmsPersonlApi.pagePersons(queryParams)
|
||||
console.log(res)
|
||||
list.value = res.list
|
||||
total.value = res.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
const reload = () => {
|
||||
getList()
|
||||
}
|
||||
/** 表格选中数据 */
|
||||
const selections = ref([]);
|
||||
const handleSelectionChange = (rows) => {
|
||||
selections.value = rows;
|
||||
}
|
||||
|
||||
|
||||
const selectedRows = ref<string[]>([]);
|
||||
|
||||
const handleDeletes = async () => {
|
||||
try {
|
||||
const rows = selections.value;
|
||||
if (!rows.length) {
|
||||
message.error('请至少选择一条数据');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRows.value = rows.map((d: any) => d.id); // 保存选中的行数据
|
||||
|
||||
const deleteData = {
|
||||
studentIds: selectedRows.value, // 选中的 ID 列表
|
||||
taskId: props.taskId // 任务 ID
|
||||
}
|
||||
await SmsPersonlApi.removePersons(deleteData)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
|
||||
const openEdit = (type: string, row?: object) => {
|
||||
showEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
taskEditRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
const openSearch = (type: string, row?: object) => {
|
||||
showPersonEdit.value = true
|
||||
current.value = row
|
||||
nextTick(() => {
|
||||
stuAddRef.value?.open(type, row)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id: number) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
console.log(id+"idididid")
|
||||
await SmsPersonlApi.removePerson(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
</script>
|
216
src/views/task/collegeexam/components/task-add.vue
Normal file
216
src/views/task/collegeexam/components/task-add.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<!-- 编辑弹窗 -->
|
||||
<template>
|
||||
<Dialog v-model="visible" :title="'添加试卷任务'" width="460" @open="handleOpen" center>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
@submit.prevent=""
|
||||
>
|
||||
<el-form-item label="任务名称" prop="taskName">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="form.taskName"
|
||||
placeholder="请输入任务名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="专业" prop="taskSpecialty">
|
||||
<el-select
|
||||
v-model="form.taskSpecialty"
|
||||
placeholder="请选择专业"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in specialtyOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="试卷任务模式" prop="taskType">
|
||||
|
||||
<el-radio-group v-model="form.taskType">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.TASK_TYPE)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
</el-form-item> -->
|
||||
|
||||
|
||||
<el-form-item label="是否为模板" prop="isTemplate">
|
||||
|
||||
|
||||
|
||||
<el-radio-group v-model="form.isTemplate">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYS_YES_NO)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
<el-form-item label="是否启用" prop="status">
|
||||
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYS_STATUS)"
|
||||
:key="dict.value"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
|
||||
|
||||
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="save">
|
||||
保存
|
||||
</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
import { addTask, updateTask,getSpecialtyList } from '@/api/system/task';
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
const props = defineProps({
|
||||
/** 修改回显的数据 */
|
||||
data: Object
|
||||
});
|
||||
// 专业选项列表
|
||||
const specialtyOptions = ref([]);
|
||||
const emit = defineEmits(['done']);
|
||||
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
/** 弹窗是否打开 */
|
||||
const visible = defineModel({ type: Boolean });
|
||||
|
||||
/** 是否是修改 */
|
||||
const isUpdate = ref(false);
|
||||
|
||||
/** 提交状态 */
|
||||
const loading = ref(false);
|
||||
|
||||
/** 表单实例 */
|
||||
const formRef = ref(null);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
taskId: void 0,
|
||||
taskName: '',
|
||||
taskSpecialty: '',
|
||||
taskType: '2',
|
||||
isTemplate: '',
|
||||
status: '',
|
||||
updateTime: '',
|
||||
updateBy: '',
|
||||
deptId: '',
|
||||
userId: ''
|
||||
});
|
||||
|
||||
/** 表单验证规则 */
|
||||
const rules = reactive({
|
||||
taskName: [
|
||||
{ required: true, message: '任务名称不能为空', trigger: 'blur' }
|
||||
],
|
||||
// taskSpecialty: [
|
||||
// { required: true, message: '请选择专业', trigger: 'change' }
|
||||
// ],
|
||||
|
||||
isTemplate: [
|
||||
{ required: true, message: '请选择是否为模板', trigger: 'change' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: '请选择是否启用', trigger: 'change' }
|
||||
]
|
||||
});
|
||||
|
||||
/** 关闭弹窗 */
|
||||
const handleCancel = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
// 获取专业下拉列表
|
||||
const fetchSpecialtyOptions = async () => {
|
||||
try {
|
||||
const data = await getSpecialtyList();
|
||||
// 假设返回格式为 [{ label: '计算机', value: 'computer' }, ...]
|
||||
// 过滤空字符串并转成下拉格式
|
||||
console.log(data)
|
||||
specialtyOptions.value = data
|
||||
.filter(item => item) // 过滤空字符串
|
||||
.map(item => ({
|
||||
label: item,
|
||||
value: item
|
||||
}));
|
||||
} catch (e) {
|
||||
message.error('获取专业列表失败:' + e.message);
|
||||
}
|
||||
};
|
||||
/** 保存编辑 */
|
||||
const save = () => {
|
||||
formRef.value?.validate?.((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
const saveOrUpdate = isUpdate.value ? updateTask : addTask;
|
||||
saveOrUpdate(form)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
handleCancel();
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/** 弹窗打开事件 */
|
||||
const handleOpen = () => {
|
||||
if (props.data) {
|
||||
assignFields(props.data);
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
resetFields();
|
||||
isUpdate.value = false;
|
||||
}
|
||||
nextTick(() => {
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.();
|
||||
});
|
||||
});
|
||||
};
|
||||
onMounted(() => {
|
||||
fetchSpecialtyOptions();
|
||||
});
|
||||
|
||||
</script>
|
119
src/views/task/collegeexam/components/task-edit.vue
Normal file
119
src/views/task/collegeexam/components/task-edit.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
|
||||
<Dialog v-model="isVisible" :title="'修改试卷任务'" width="1460" >
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="80px"
|
||||
|
||||
>
|
||||
<div>
|
||||
<!-- 步骤条 -->
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<template v-for="(title, index) in stepTitles" :key="index">
|
||||
<el-step>
|
||||
<template #title>
|
||||
<div @click="handleStepClick(index)" style="cursor: pointer;">
|
||||
{{ title }}
|
||||
</div>
|
||||
</template>
|
||||
</el-step>
|
||||
</template>
|
||||
</el-steps>
|
||||
|
||||
<div style="margin-top: 30px;">
|
||||
<component
|
||||
:is="currentComponent"
|
||||
:form-data="props.data"
|
||||
:task-specialty="props.data.taskSpecialty"
|
||||
:task-id="props.data.taskId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
|
||||
|
||||
|
||||
<template #footer>
|
||||
|
||||
<el-button @click="handleCancel">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useFormData } from '@/utils/use-form-data'
|
||||
import StepOne from './steps/step1/index.vue'
|
||||
import StepTwo from './steps/step2/index.vue'
|
||||
import StepThree from './steps/step3/index.vue'
|
||||
import StepFour from './steps/step4/index.vue'
|
||||
import StepFive from './steps/step5/index.vue'
|
||||
import * as SmsChannelApi from '@/api/system/session';
|
||||
// 模拟接口(你需要改成实际请求)
|
||||
async function validateBeforeStepFour(taskId) {
|
||||
// 示例:替换为你实际的 API 请求
|
||||
const res = await SmsChannelApi.fetch(taskId)
|
||||
console.log(res+"resres")
|
||||
if (res==='200') {
|
||||
return true
|
||||
} else {
|
||||
ElMessage.error(res || '无法进入考场设置步骤')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
data: Object
|
||||
})
|
||||
const isVisible = defineModel({ type: Boolean })
|
||||
|
||||
const isUpdate = ref(false)
|
||||
const activeStep = ref(0)
|
||||
|
||||
const stepTitles = ['试卷方案', '试卷管理', '参数设置', '考场设置', '人员设置']
|
||||
const stepComponents = [StepOne, StepTwo, StepThree, StepFour, StepFive]
|
||||
const currentComponent = computed(() => stepComponents[activeStep.value])
|
||||
|
||||
const formRef = ref(null)
|
||||
const [form, resetFields, assignFields] = useFormData({
|
||||
taskId: '',
|
||||
taskSpecialty: ''
|
||||
})
|
||||
|
||||
async function handleStepClick(index) {
|
||||
// 点击第四步(index=3)时先校验
|
||||
if (index === 3) {
|
||||
const pass = await validateBeforeStepFour(props.data.taskId)
|
||||
if (!pass) return
|
||||
}
|
||||
activeStep.value = index
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type, row) => {
|
||||
isVisible.value = true
|
||||
assignFields(row)
|
||||
isUpdate.value = true
|
||||
activeStep.value = 0
|
||||
|
||||
nextTick(() => {
|
||||
formRef.value?.clearValidate?.()
|
||||
})
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
const handleCancel = () => {
|
||||
isVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 可选:让步骤标题更像按钮 */
|
||||
.el-step__title:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
88
src/views/task/collegeexam/components/task-search.vue
Normal file
88
src/views/task/collegeexam/components/task-search.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<!-- 搜索表单 -->
|
||||
<template>
|
||||
<ele-card :body-style="{ paddingBottom: '2px' }">
|
||||
<el-form label-width="72px" @keyup.enter="search" @submit.prevent="">
|
||||
<el-row :gutter="8">
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="试卷名称">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.taskName"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="试卷专业">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.taskSpecialty"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="试卷任务模式">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.taskType"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="模板">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.isTemplate"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label="是否启用">
|
||||
<el-input
|
||||
clearable
|
||||
v-model.trim="form.status"
|
||||
placeholder="请输入"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="6" :md="12" :sm="12" :xs="24">
|
||||
<el-form-item label-width="16px">
|
||||
<el-button type="primary" @click="search">查询</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</ele-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFormData } from '@/utils/use-form-data';
|
||||
|
||||
const emit = defineEmits(['search']);
|
||||
|
||||
/** 表单数据 */
|
||||
const [form, resetFields] = useFormData({
|
||||
taskName: '',
|
||||
taskSpecialty: '',
|
||||
taskType: '',
|
||||
isTemplate: '',
|
||||
status: '',
|
||||
deptId: '',
|
||||
userId: ''
|
||||
});
|
||||
|
||||
/** 搜索 */
|
||||
const search = () => {
|
||||
emit('search', { ...form });
|
||||
};
|
||||
|
||||
/** 重置 */
|
||||
const reset = () => {
|
||||
resetFields();
|
||||
search();
|
||||
};
|
||||
</script>
|
151
src/views/task/collegeexam/components/task-temp.vue
Normal file
151
src/views/task/collegeexam/components/task-temp.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<!-- 模板库对话框 -->
|
||||
<Dialog v-model="dialogVisible" title="模板库" width="80%" :destroy-on-close="true">
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="任务名称" align="center" prop="taskName" />
|
||||
<el-table-column label="专业" align="center" prop="taskSpecialty" />
|
||||
<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>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</Dialog>
|
||||
|
||||
<!-- 选择参数的对话框 -->
|
||||
<Dialog v-model="showDialog" title="选择所需参数" width="50%" :destroy-on-close="true">
|
||||
<el-checkbox-group v-model="selectedOptions">
|
||||
<el-checkbox label="1">试卷方案</el-checkbox>
|
||||
<el-checkbox label="2">试卷管理</el-checkbox>
|
||||
<el-checkbox label="3">参数设置</el-checkbox>
|
||||
<el-checkbox label="4">考场设置</el-checkbox>
|
||||
<!-- <el-checkbox label="5">人员设置</el-checkbox> -->
|
||||
</el-checkbox-group>
|
||||
<!-- 全选、取消全选按钮 -->
|
||||
<el-button @click="selectAll">全选</el-button>
|
||||
<el-button @click="deselectAll">取消全选</el-button>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<template #footer>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确认</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from 'vue';
|
||||
import { pageTasks, submitSelection } from '@/api/system/task';
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
data: Object
|
||||
});
|
||||
const emit = defineEmits(['update:modelValue', 'done']);
|
||||
|
||||
// 处理模板库对话框的显隐
|
||||
const dialogVisible = defineModel({ type: Boolean });// 控制模板库对话框
|
||||
const showDialog = ref(false); // 控制选择参数对话框
|
||||
const selectedOptions = ref([]); // 存储用户选中的复选框内容
|
||||
const selectedTaskId = ref(null); // 用来存储选中行的 taskId
|
||||
|
||||
// 加载中状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 表格数据和分页参数
|
||||
const list = ref([]); // 表格数据
|
||||
const total = ref(0); // 总记录数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
status: '0',
|
||||
isTemplate: '0',
|
||||
taskType:'2'
|
||||
});
|
||||
|
||||
// 获取数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await pageTasks(queryParams);
|
||||
list.value = res.list; // 假设 res.data 是你需要的数据
|
||||
total.value = res.total; // 假设 res.total 是总记录数
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听父组件传递的 modelValue
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
dialogVisible.value = val;
|
||||
}
|
||||
);
|
||||
watch(dialogVisible, (val) => {
|
||||
emit('update:modelValue', val); // 更新父组件的 modelValue
|
||||
});
|
||||
|
||||
// 打开编辑表单,弹出选择参数对话框
|
||||
const openEdit = (action, row) => {
|
||||
selectedTaskId.value = row.taskId; // 记录选中的 taskId
|
||||
showDialog.value = true; // 弹出选择参数对话框
|
||||
};
|
||||
|
||||
// 全选按钮的处理方法
|
||||
const selectAll = () => {
|
||||
selectedOptions.value = ['1', '2', '3', '4']; // 选择所有复选框
|
||||
};
|
||||
|
||||
// 取消全选按钮的处理方法
|
||||
const deselectAll = () => {
|
||||
selectedOptions.value = []; // 清空选中的复选框
|
||||
};
|
||||
|
||||
// 确认选择的操作
|
||||
const confirmSelection = () => {
|
||||
// 发送请求,包含 taskId 和选中的选项
|
||||
const requestData = {
|
||||
taskId: selectedTaskId.value, // 选中的 taskId
|
||||
options: selectedOptions.value // 选中的复选框内容
|
||||
};
|
||||
|
||||
submitSelection(requestData)
|
||||
.then((msg) => {
|
||||
showDialog.value = false; // 关闭选择参数对话框
|
||||
message.success(msg); // 提示成功
|
||||
emit('done'); // 通知父组件
|
||||
})
|
||||
.catch((e) => {
|
||||
showDialog.value = false; // 关闭选择参数对话框
|
||||
message.error(e.message); // 提示错误
|
||||
});
|
||||
};
|
||||
|
||||
// 关闭选择参数对话框
|
||||
const handleCancel = () => {
|
||||
showDialog.value = false;
|
||||
};
|
||||
|
||||
// 初始化获取数据
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
Reference in New Issue
Block a user