【新增】试卷任务前端

This commit is contained in:
YOHO\20373
2025-04-23 17:12:26 +08:00
parent 865ebd0a4d
commit 32df5e98a4
77 changed files with 13188 additions and 1 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>