【修改】前端提交代码

This commit is contained in:
huababa1
2025-11-08 18:16:41 +08:00
parent e225586dbc
commit 47ca453401
59 changed files with 1724 additions and 221 deletions

View File

@@ -0,0 +1,19 @@
import request from '@/config/axios'
// 通用参数实体
export interface TranParam {
id?: number
examSet: string // 0 是开启1 否关闭
}
// 获取列表
export const getSet = () => {
return request.get({ url: '/exam/tranparam/getSetList' })
}
// 修改
export const updateSet = (data) => {
return request.put({ url: '/exam/tranparam/updateSet', data })
}

View File

@@ -87,6 +87,11 @@ export const importUserTemplate = () => {
export const importUserTemplateStu = () => {
return request.download({ url: '/system/user/get-import-template-stu' })
}
// 下载学生试卷任务导入模板
export const importUserTaskStu = () => {
return request.download({ url: '/system/user/get-import-task-stu' })
}
// 下载教师导入模板
export const importUserTemplateTeacher = () => {
return request.download({ url: '/system/user/get-import-template-teacher' })

View File

@@ -1,6 +1,7 @@
import { service } from './service'
import { config } from './config'
import { TranParam } from '@/api/tranparam'
const { default_headers } = config
@@ -31,7 +32,7 @@ export default {
const res = await request({ method: 'DELETE', ...option })
return res.data as unknown as T
},
put: async <T = any>(option: any) => {
put: async <T = any>(option: any, data: TranParam) => {
const res = await request({ method: 'PUT', ...option })
return res.data as unknown as T
},

View File

@@ -114,7 +114,7 @@ export default {
small: '小'
},
login: {
welcome: '湖北省技能高考万维考试平台',
welcome: '万维智能实训平台',
message: '',
tenantname: '租户名称',
username: '用户名',
@@ -128,7 +128,7 @@ export default {
remember: '记住我',
hasUser: '已有账号?去登录',
forgetPassword: '忘记密码?',
tenantNamePlaceholder: '请输入租户名称',
tenantNamePlaceholder: '请输入学校名称',
usernamePlaceholder: '请输入用户名',
passwordPlaceholder: '请输入密码',
codePlaceholder: '请输入验证码',

View File

@@ -110,6 +110,7 @@ export const getDictLabel = (dictType: string, value: any): string => {
export enum DICT_TYPE {
USER_TYPE = 'user_type',
COMMON_STATUS = 'common_status',
C = 'c',
TERMINAL = 'terminal', // 终端
DATE_INTERVAL = 'date_interval', // 数据间隔

View File

@@ -56,7 +56,7 @@
v-if="refreshTable"
>
<el-table-column prop="name" label="专业名称" />
<el-table-column prop="roles" label="软件环境" />
<!-- <el-table-column prop="roles" label="软件环境" /> -->
<el-table-column prop="status" label="状态">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
@@ -75,7 +75,7 @@
修改
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
<!-- 第三级才显示规则按钮 -->
<!-- 第三级才显示规则按钮
<el-button
v-if="scope.row.level === 3"
link
@@ -83,7 +83,7 @@
@click="handleRule(scope.row)"
>
软件环境
</el-button>
</el-button>-->
</template>
</el-table-column>
</el-table>

View File

@@ -119,7 +119,15 @@
/>
</el-select>
</el-form-item>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</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>
@@ -171,6 +179,7 @@
</template>
</el-table-column>
<el-table-column label="试卷任务" align="center" prop="taskName" />
<el-table-column label="场次" align="center" prop="batch" />
<el-table-column label="机器ip" align="center" prop="ip" width="180px"/>
<el-table-column
label="更新时间"
@@ -361,7 +370,8 @@ const queryParams = reactive({
ip: undefined,
remainingTime: [],
startTime: undefined,
endTime: undefined
endTime: undefined,
batch: undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中

View File

@@ -96,8 +96,12 @@
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -446,6 +450,7 @@ const formData = ref({
pointNamesVo:'',
chapteridDictTextVo:'',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -106,13 +106,11 @@
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>
<el-col :span="12">
<el-form-item label="来源" prop="resourceValue">
<el-input v-model="formData.resourceValue" placeholder="请输入来源" />
</el-form-item>
</el-col>
</el-row> -->
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
</el-form>
<div class="edit-bottom">
<div class="edit-left bottom-common">
@@ -326,6 +324,7 @@ const formData = ref({
pointNamesVo:'',
chapteridDictTextVo:'',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -84,18 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit">
<el-select v-model="formData.audit" placeholder="请选择审核状态">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.QUESTION_AUDIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col> -->
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -381,6 +374,7 @@ const formData = ref<any>({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -96,7 +96,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
</el-row>
</el-form>
@@ -506,6 +510,7 @@ const formData = ref({
pointNamesVo:'',
chapteridDictTextVo:'',
content: '<p>---------------------------------------------------------------------</p><p> 请在打开的窗口中,进行下列操作,完成所有操作后,请关闭窗口。</p><p>---------------------------------------------------------------------</p><p>',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

File diff suppressed because one or more lines are too long

View File

@@ -96,9 +96,12 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
<div class="edit-left bottom-common">
@@ -487,6 +490,7 @@ const formData = ref({
pointNamesVo:'',
chapteridDictTextVo:'',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',
@@ -671,7 +675,7 @@ function flattenPsVoList(list, parentPath = '') {
if (item.children && item.children.length > 0) {
result.push(...flattenPsVoList(item.children, currentPath));
} else {
const symbol = item.value === 'true' ? '✅' : item.value === 'false' ? '❌' : item.value;
const symbol = item.value ;
result.push({
key: item.key, // 一定要有 key
label: `${currentPath}: ${symbol}`,
@@ -697,11 +701,7 @@ function flattenPsVoListOther(list, parentPath = '') {
: item.key;
// 处理值显示类似原函数的symbol逻辑
const valueDisplay = item.value === 'true'
? '✅'
: item.value === 'false'
? '❌'
: item.value ;
const valueDisplay = item.value ;
// 只有叶子节点没有children属性才添加到结果
// 根据您的数据结构type=1可能是叶子节点的标识
@@ -765,7 +765,7 @@ console.log(formData.value.fileUploads+"formData.value.fileUploads")
const formRules = reactive<FormRules>({
// specialtyName: [{ required: true, message: '用户名称不能为空', trigger: 'blur' }]
status: [{ required: true, message: '启用状态必填', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
// 左侧试题描述
@@ -1113,7 +1113,7 @@ const handleNodeClick = (data, node) => {
}
// 构造一个 label用 key 和 value 显示
const symbol = data.value === true ? '✅' : data.value === false ? '❌' : data.value;
const symbol = data.value ;
const label = `${data.key}: ${symbol}`;
// 判断是否已存在,避免重复添加
@@ -1154,8 +1154,12 @@ const updateSort = () => {
item.sort = index + 1
})
}
const removePoint = (index: number) => {
flatPointList.value.splice(index, 1);
const removePoint = (row) => {
for (let i = 0; i < flatPointList.value.length; i++) {
if (row.label == flatPointList.value[i].label) {
flatPointList.value.splice(i, 1)
}
}
};

View File

@@ -84,18 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit">
<el-select v-model="formData.audit" placeholder="请选择审核状态">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.QUESTION_AUDIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col> -->
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -313,6 +306,7 @@ const formData = ref({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -84,6 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -252,6 +257,7 @@ const formData = ref({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -84,18 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit">
<el-select v-model="formData.audit" placeholder="请选择审核状态">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.QUESTION_AUDIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col> -->
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -394,6 +387,7 @@ const formData = ref({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -84,18 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit">
<el-select v-model="formData.audit" placeholder="请选择审核状态">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.QUESTION_AUDIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col> -->
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -406,6 +399,7 @@ const formData = ref({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText: '',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -84,18 +84,11 @@
</el-radio-group>
</el-form-item>
</el-col>
<!-- <el-col :span="12">
<el-form-item label="审核状态" prop="audit">
<el-select v-model="formData.audit" placeholder="请选择审核状态">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.QUESTION_AUDIT)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
<el-col :span="12">
<el-form-item label="标题" prop="contentText">
<el-input v-model="formData.contentText" placeholder="请输入标题" />
</el-form-item>
</el-col> -->
</el-col>
</el-row>
</el-form>
<div class="edit-bottom">
@@ -487,6 +480,7 @@ const formData = ref({
pointNamesVo: '',
chapteridDictTextVo: '',
content: '',
contentText:'',
specialtyName: '',
courseName: '',
quBankName: '',

View File

@@ -50,15 +50,15 @@
</el-select>
</el-form-item>
<!-- <el-form-item label="手机号码" prop="mobile">-->
<!-- <el-input-->
<!-- v-model="queryParams.mobile"-->
<!-- placeholder="请输入手机号码"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- class="!w-240px"-->
<!-- />-->
<!-- </el-form-item>-->
<el-form-item label="标题" prop="contentText">
<el-input
v-model="queryParams.contentText"
placeholder="请输入标题"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<!-- <el-form-item label="审核状态" prop="audit">
<el-select
v-model="queryParams.audit"
@@ -179,6 +179,11 @@
prop="quNum"
:show-overflow-tooltip="true"
/>
<el-table-column label="标题" align="center" prop="contentText">
<template #default="{ row }">
{{ getFirstNChineseChars(row.contentText, 10) }}
</template>
</el-table-column>
<el-table-column label="题干" align="center" prop="content" width="120">
<template #default="{ row }">
{{ getFirstNChineseChars(row.content, 5) }}
@@ -428,6 +433,7 @@ const queryParams = reactive({
specialtyName: '',
courseName: '',
subjectName: '',
contentText:'',
pointNames: '',
chapteridDictText: '',
pageNo: 1,
@@ -829,20 +835,19 @@ const importFormRef = ref()
/** 修改用户状态 */
const handleStatusChange = async (row: any) => {
const oldStatus = row.status
console.log(oldStatus)
try {
// 修改状态的二次确认
await message.confirm('确认要修改' + row.quNum + '"试题状态吗?')
// 发起修改状态
QuestionApi.updateQuStatus(row.quId, row.status)
// 刷新列表
await message.confirm(`确认要修改「${row.quNum}」的试题状态吗?`)
await QuestionApi.updateQuStatus(row.quId, row.status)
await getList()
} catch {
// 取消后,进行恢复按钮
row.status =
row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
row.status = oldStatus
await getList()
}
}
/** 导出按钮操作 */
const exportLoading = ref(false)
@@ -931,23 +936,37 @@ const handleRole = (row: UserApi.UserVO) => {
}
function getFirstNChineseChars(htmlStr) {
function getFirstNChineseChars(htmlStr,limit ) {
if (!htmlStr) return ''
// 将 HTML 转为纯文本
const tempDiv = document.createElement('div')
tempDiv.innerHTML = htmlStr
let text = tempDiv.textContent || tempDiv.innerText || ''
// 尝试反转义(处理 &lt; &gt; 等)
const txt = document.createElement('textarea')
txt.innerHTML = htmlStr
htmlStr = txt.value // => <p>test</p>
// 去掉空格和换行
// 判断是否包含 HTML 标签
const hasHtmlTag = /<[^>]+>/.test(htmlStr)
// 提取纯文本
let text = hasHtmlTag
? (() => {
const tempDiv = document.createElement('div')
tempDiv.innerHTML = htmlStr
return tempDiv.textContent || tempDiv.innerText || ''
})()
: htmlStr
// 去空格和换行
text = text.replace(/\s+/g, '')
// 匹配汉字
const match = text.match(/[\u4e00-\u9fa5]/g)
// 这里改成提取“中英文混合”而非仅中文
const match = text.match(/[\u4e00-\u9fa5a-zA-Z0-9]/g)
if (!match) return ''
return match.slice(0, 6).join('')
return match.slice(0, limit).join('')+ '...';
}
/** 初始化 */
onMounted(() => {
getList()

View File

@@ -0,0 +1,58 @@
<template>
<ContentWrap title="考试模式设置">
<el-card>
<div class="flex items-center gap-4">
<span>是否开启考试模式</span>
<el-switch
v-model="examSet"
active-value="0"
inactive-value="1"
active-text="开启"
inactive-text="关闭"
@change="handleSwitchChange"
/>
</div>
</el-card>
</ContentWrap>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessageBox, ElMessage } from 'element-plus'
import * as TranParamApi from '@/api/system/tranparam'
import type { TranParam } from '@/api/system/tranparam'
const examSet = ref<'0' | '1'>('1') // 默认关闭
const examId = ref<number | null>(null) // 👈 存储ID
// 页面加载时获取当前状态
const getExamSet = async () => {
const res = await TranParamApi.getSet()
const record = res.list?.[0]
examSet.value = record?.examSet ?? '1'
examId.value = record?.id ?? null
}
onMounted(() => {
getExamSet()
})
// 修改考试模式
const handleSwitchChange = async (val: string) => {
try {
await ElMessageBox.confirm(`确定要${val === '0' ? '开启' : '关闭'}考试模式吗?`, '提示', {
type: 'warning'
})
const data: TranParam = {
id: examId.value!,
examSet: val
}
await TranParamApi.updateSet(data)
getExamSet();
ElMessage.success('修改成功')
} catch {
examSet.value = val === '0' ? '1' : '0'
}
}
</script>

View File

@@ -0,0 +1,150 @@
<template>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<el-form
ref="formRef"
v-loading="formLoading"
:model="formData"
:rules="formRules"
label-width="80px"
>
<el-form-item label="上级专业" prop="parentId">
<el-tree-select
v-model="formData.parentId"
:data="specialtyTree"
:props="defaultProps"
check-strictly
default-expand-all
placeholder="请选择上级专业"
value-key="spId"
/>
</el-form-item>
<el-form-item label="专业名称" prop="spName">
<el-input v-model="formData.spName" placeholder="请输入专业名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="formData.status" clearable placeholder="请选择状态">
<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>
<template #footer>
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { defaultProps, handleTree } from '@/utils/tree'
import * as SpecialtyApi from '@/api/exam/specialty'
import * as UserApi from '@/api/system/user'
import { CommonStatusEnum } from '@/utils/constants'
import { FormRules } from 'element-plus'
defineOptions({ name: 'SpecialtyForm' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中1修改时的数据加载2提交的按钮禁用
const formType = ref('') // 表单的类型create - 新增update - 修改
const formData = ref({
id: undefined,
title: '',
parentId: undefined,
name: undefined,
sort: undefined,
leaderUserId: undefined,
phone: undefined,
email: undefined,
status: CommonStatusEnum.ENABLE
})
const formRules = reactive<FormRules>({
parentId: [{ required: true, message: '上级专业不能为空', trigger: 'blur' }],
name: [{ required: true, message: '专业名称不能为空', trigger: 'blur' }],
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref
const specialtyTree = ref() // 树形结构
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
// 修改时,设置数据
if (id) {
formLoading.value = true
try {
formData.value = await SpecialtyApi.getSpecialty(id)
} finally {
formLoading.value = false
}
}
// 获得用户列表
userList.value = await UserApi.getSimpleUserList()
// 获得专业树
await getTree()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 校验表单
if (!formRef) return
const valid = await formRef.value.validate()
if (!valid) return
// 提交请求
formLoading.value = true
try {
const data = formData.value
if (formType.value === 'create') {
await SpecialtyApi.createSpecialty(data)
message.success(t('common.createSuccess'))
} else {
await SpecialtyApi.updateSpecialty(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
// 发送操作成功的事件
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
title: '',
parentId: undefined,
name: undefined,
sort: undefined,
leaderUserId: undefined,
phone: undefined,
email: undefined,
status: CommonStatusEnum.ENABLE
}
formRef.value?.resetFields()
}
/** 获得专业-课程-题型树 */
const getTree = async () => {
specialtyTree.value = []
const data = await SpecialtyApi.getSpecialtyPage()
let dept: Tree = { id: 0, name: '专业-课程-题型', children: [] }
dept.children = handleTree(data)
specialtyTree.value.push(dept)
}
</script>

View File

@@ -0,0 +1,260 @@
<template>
<!-- 搜索工作栏 -->
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="专业名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入专业名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="专业状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择专业状态"
clearable
class="!w-240px"
>
<el-option
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>
<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')">
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button> -->
<el-button type="danger" plain @click="toggleExpandAll">
<Icon icon="ep:sort" class="mr-5px" /> 展开/折叠
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table
v-loading="loading"
:data="list"
row-key="id"
:default-expand-all="isExpandAll"
v-if="refreshTable"
>
<el-table-column prop="name" label="专业名称" />
<el-table-column prop="roles" label="软件环境" />
<el-table-column prop="status" label="状态">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
align="center"
prop="createTime"
width="180"
:formatter="dateFormatter"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<!-- <el-button link type="primary" @click="openForm('update', scope.row.id)">
修改
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button> -->
<!-- 第三级才显示规则按钮 -->
<el-button
v-if="scope.row.level === 3"
link
type="warning"
@click="handleRule(scope.row)"
>
软件环境
</el-button>
</template>
</el-table-column>
</el-table>
</ContentWrap>
<el-dialog v-model="ruleDialogVisible" title="软件环境" width="600px">
<!-- 弹窗内容 -->
<el-form :model="formData" label-width="100px">
<el-form-item label="软件环境" prop="roles">
<!-- 当专业名称为编程题时显示下拉框否则显示输入框 -->
<template v-if="formData.name === '编程题'">
<el-select
v-model="formData.roles"
placeholder="请选择软件环境"
clearable
style="width: 240px"
>
<el-option
v-for="item in softwareOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
<template v-else>
<el-input
v-model="formData.roles"
style="width: 240px"
placeholder="输入对应需要软件名称"
/>
</template>
</el-form-item>
</el-form>
<!-- 弹窗底部按钮 -->
<template #footer>
<span class="dialog-footer">
<el-button @click="ruleDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleRuleSave">保存</el-button>
</span>
</template>
</el-dialog>
<!-- 表单弹窗添加/修改 -->
<SpecialtyForm ref="formRef" @success="getList" />
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { handleTree } from '@/utils/tree'
import * as SpecialtyApi from '@/api/exam/specialty'
import SpecialtyForm from './SpecialtyForm.vue'
import * as UserApi from '@/api/system/user'
defineOptions({ name: 'SystemDept' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref() // 列表的数据
const queryParams = reactive({
pageNo: 1,
pageSize: 100,
name: undefined,
status: undefined
})
const queryFormRef = ref() // 搜索的表单
const isExpandAll = ref(true) // 是否展开,默认全部展开
const refreshTable = ref(true) // 重新渲染表格状态
const userList = ref<UserApi.UserVO[]>([]) // 用户列表
const ruleDialogVisible = ref(false) // 控制弹窗显示
const currentRuleRow = ref<any>(null) // 当前点击的那一行数据
const formData = ref({
roles: '',
id: '',
name:''
})
const handleRule = async (row: any) => {
currentRuleRow.value = row.id
const res = await SpecialtyApi.getSpecialtyRole(currentRuleRow.value)
console.log(res)
formData.value.roles = res
formData.value.name = row.name
ruleDialogVisible.value = true
}
// 本地自定义下拉数据
const softwareOptions = ref([
{ label: 'Visual C++', value: 'Visual C++' },
{ label: 'VC++ 2010 Express', value: 'VC++ 2010 Express' },
{ label: 'CFree', value: 'CFree' },
{ label: 'Dev-c', value: 'Dev-c' },
{ label: 'Code::Blocks', value: 'Code::Blocks' },
{ label: 'VS2008', value: 'VS2008' },
{ label: 'VS2010', value: 'VS2010' },
{ label: 'VS2019', value: 'VS2019' },
])
const handleRuleSave = async () => {
formData.value.id = currentRuleRow.value
const data = formData.value
const res = await SpecialtyApi.setSpecialtyRole(data)
message.success(t(res))
handleQuery()
ruleDialogVisible.value = false
}
/** 查询部门列表 */
const getList = async () => {
loading.value = true
try {
const data = await SpecialtyApi.getSpecialtyPage(queryParams)
console.log(data)
const treeData = handleTree(data)
list.value = addLevelToTree(treeData)
} finally {
loading.value = false
}
}
/** 展开/折叠操作 */
const toggleExpandAll = () => {
refreshTable.value = false
isExpandAll.value = !isExpandAll.value
nextTick(() => {
refreshTable.value = true
})
}
/** 搜索按钮操作 */
const handleQuery = () => {
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.pageNo = 1
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await SpecialtyApi.deleteSpecialty(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
// 递归添加 level 字段
const addLevelToTree = (nodes, level = 1) => {
return nodes.map((node) => {
const newNode = { ...node, level }
if (newNode.children && newNode.children.length > 0) {
newNode.children = addLevelToTree(newNode.children, level + 1)
}
return newNode
})
}
/** 初始化 **/
onMounted(async () => {
await getList()
// 获取用户列表
userList.value = await UserApi.getSimpleUserList()
})
</script>

View File

@@ -33,32 +33,34 @@
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<!-- 新增/修改弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<el-form ref="formRef" :model="form" label-width="80px">
<el-form-item label="名称" prop="name" required>
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
<!-- 新增/修改弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px">
<el-form ref="formRef" :model="form" label-width="80px">
<el-form-item label="名称" prop="name" required>
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm">确定</el-button>
</template>
</el-dialog>
</ContentWrap> </template> <script lang="ts" setup> import { ref, reactive } from 'vue'
</ContentWrap>
</template>
<script lang="ts" setup> import { ref, reactive } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import * as WhiteApi from '@/api/system/whiteList'
// API //
const loading = ref(false)
const loading = ref(false)
const list = ref([])
const total = ref(0)
const queryParams = reactive({ pageNo: 1, pageSize: 10, name: '' })
@@ -68,35 +70,36 @@ const dialogTitle = ref('')
const formRef = ref()
const form = reactive({ id: '', name: '', path: '', remark: '' })
//
const getList = async () => { loading.value = true
const params = {
...queryParams,
}
try { const res = await WhiteApi.getAppWhiteListCenter(params)
list.value = res.list
total.value = res.total
} finally
{
loading.value = false
const getList = async () => { loading.value = true
const params = {
...queryParams,
}
try { const res = await WhiteApi.getAppWhiteListCenter(params)
list.value = res.list
total.value = res.total
} finally
{
loading.value = false
} }
//
const resetQuery = () => {
queryParams.name = ''
getList()
}
//
const openForm = async (type: string, id?: string) => {
dialogVisible.value = true
if (type === 'create') {
dialogTitle.value = '新增白名单'
Object.assign(form, { id: '', name: '', path: '', remark: '' })
} else {
dialogTitle.value = '修改白名单'
const data = await WhiteApi.getWhite(id)
Object.assign(form, data) }
} //
//
const openForm = async (type: string, id?: string) => {
dialogVisible.value = true
if (type === 'create') {
dialogTitle.value = '新增白名单'
Object.assign(form, { id: '', name: '', path: '', remark: '' })
} else {
dialogTitle.value = '修改白名单'
const data = await WhiteApi.getWhite(id)
Object.assign(form, data) }
}
//
const submitForm = async () => {
if (!form.name) {
ElMessage.warning('请输入名称')
@@ -105,21 +108,26 @@ const form = reactive({ id: '', name: '', path: '', remark: '' })
if (form.id) {
await WhiteApi.updateAppWhite(form)
ElMessage.success('修改成功')
} else {
await WhiteApi.addAppWhite(form)
ElMessage.success('新增成功')
}
dialogVisible.value = false
getList()
}
}
else {
await WhiteApi.addAppWhite(form)
ElMessage.success('新增成功')
}
dialogVisible.value = false
getList()
}
//
const handleDelete = async (id: string) => {
ElMessageBox.confirm('确认删除该白名单?', '提示', {
type: 'warning'
}).then(async () => {
})
.then(async () => {
await WhiteApi.delWhite(id)
ElMessage.success('删除成功')
getList() }) }
//
getList()
getList()
}
)
}
//
getList()
</script>

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,13 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功')
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,25 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +98,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -90,6 +106,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
@@ -133,7 +150,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
batch: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
@@ -220,6 +237,10 @@ if(res =='删除成功'){
await getList()
} catch {}
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const openEdit = (type: string, row?: object) => {

View File

@@ -79,7 +79,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage() // 消息弹窗
const props = defineProps({
@@ -149,7 +149,7 @@
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res.data || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -79,7 +79,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage() // 消息弹窗
const props = defineProps({
@@ -149,7 +149,7 @@
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res.data || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,13 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功');
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,25 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +98,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -91,6 +107,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import { fa } from 'element-plus/es/locale';
defineOptions({ name: 'SystemSmsChannel' })
@@ -133,7 +150,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
batch: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
@@ -221,6 +238,10 @@ if(res =='删除成功'){
} catch {}
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true

View File

@@ -79,7 +79,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage() // 消息弹窗
const props = defineProps({
@@ -149,7 +149,7 @@
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res.data || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,13 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功');
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,25 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +98,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -90,6 +106,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
@@ -133,7 +150,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
batch: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
@@ -220,6 +237,10 @@ if(res =='删除成功'){
await getList()
} catch {}
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const openEdit = (type: string, row?: object) => {

View File

@@ -79,7 +79,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage() // 消息弹窗
const props = defineProps({
@@ -149,7 +149,7 @@
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res.data || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,12 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功');
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,25 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +98,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -90,6 +106,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
@@ -133,7 +150,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
batch: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
@@ -220,7 +237,10 @@ if(res =='删除成功'){
await getList()
} catch {}
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
const openEdit = (type: string, row?: object) => {
showEdit.value = true

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,13 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功');
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,25 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +98,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -90,6 +106,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
@@ -133,7 +150,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
batch: undefined,
status: undefined,
createTime: [],
taskId: props.taskId
@@ -237,6 +254,10 @@ const openSearch = (type: string, row?: object) => {
})
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
/** 删除按钮操作 */

View File

@@ -81,7 +81,7 @@
<script setup>
import { ref, reactive, nextTick, computed } from 'vue';
import { useFormData } from '@/utils/use-form-data';
import { getPaperDetailByTaskId } from '@/api/system/paper';
import { getPaperDetailCenterByTaskId } from '@/api/system/paper';
const message = useMessage(); // 消息弹窗
const props = defineProps({
@@ -150,7 +150,7 @@ const handleOpen = (type, paperId) => {
console.log(paperId+"paperIdpaperId")
if (props.paperId) {
loading.value = true;
getPaperDetailByTaskId(props.paperId)
getPaperDetailCenterByTaskId(props.paperId)
.then((res) => {
const { educationPaperSchemeList, examQuestionList } = res || {};
if (Array.isArray(examQuestionList)) {

View File

@@ -306,6 +306,7 @@ const fetchpaperOptions = async () => {
const handleOpen = () => {
resetFields();
form.taskId = props.taskId;
fetchpaperOptions();
nextTick(() => {
formRef.value?.clearValidate?.();
});

View File

@@ -0,0 +1,142 @@
<template>
<Dialog v-model="dialogVisible" title="学生导入" width="400">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="importUrl + '?updateSupport=' + updateSupport + '&taskId=' + props.taskId"
:auto-upload="false"
:disabled="formLoading"
:headers="uploadHeaders"
:limit="1"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
accept=".xlsx, .xls"
drag
>
<Icon icon="ep:upload" />
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="updateSupport" />
是否更新已经存在的学生数据
</div>
<span>仅允许导入 xlsxlsx 格式文件</span>
<el-link
:underline="false"
style="font-size: 12px; vertical-align: baseline"
type="primary"
@click="importTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script lang="ts" setup>
import * as UserApi from '@/api/system/user'
import { getAccessToken, getTenantId } from '@/utils/auth'
import download from '@/utils/download'
defineOptions({ name: 'SystemUserImportForm' })
const props = defineProps({
taskId: {
type: String,
default: ''
}
});
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const formLoading = ref(false) // 表单的加载中
const uploadRef = ref()
const importUrl =
import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/system/user/importTaskStu'
const uploadHeaders = ref() // 上传 Header 头
const fileList = ref([]) // 文件列表
const updateSupport = ref(0) // 是否更新已经存在的用户数据
/** 打开弹窗 */
const open = () => {
dialogVisible.value = true
updateSupport.value = 0
fileList.value = []
resetForm()
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const submitForm = async () => {
if (fileList.value.length == 0) {
message.error('请上传文件')
return
}
// 提交请求
uploadHeaders.value = {
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
}
formLoading.value = true
uploadRef.value!.submit()
}
/** 文件上传成功 */
const emits = defineEmits(['success'])
const submitFormSuccess = (response: any) => {
if (response.code !== 0) {
message.error(response.msg)
formLoading.value = false
return
}
// 拼接提示语
const data = response.data
let text = '上传成功数量:' + data.createUsernames.length + ';'
for (let username of data.createUsernames) {
text += '< ' + username + ' >'
}
text += '更新成功数量:' + data.updateUsernames.length + ';'
for (const username of data.updateUsernames) {
text += '< ' + username + ' >'
}
text += '更新失败数量:' + Object.keys(data.failureUsernames).length + ';'
for (const username in data.failureUsernames) {
text += '< ' + username + ': ' + data.failureUsernames[username] + ' >'
}
message.alert(text)
formLoading.value = false
dialogVisible.value = false
// 发送操作成功的事件
emits('success')
}
/** 上传错误提示 */
const submitFormError = (): void => {
message.error('上传失败,请您重新上传!')
formLoading.value = false
}
/** 重置表单 */
const resetForm = async (): Promise<void> => {
// 重置上传状态和文件
formLoading.value = false
await nextTick()
uploadRef.value?.clearFiles()
}
/** 文件数超出提示 */
const handleExceed = (): void => {
message.error('最多只能上传一个文件!')
}
/** 下载模板操作 */
const importTemplate = async () => {
const res = await UserApi.importUserTaskStu()
download.excel(res, '任务学生导入模版.xls')
}
</script>

View File

@@ -205,14 +205,12 @@ const removeBatch = async () => {
const res=await PersonApi.removeSessionStu(data);
if(res =='删除成功'){
message.success(t('common.delSuccess'))
message.success('删除成功');
}else{
message.error(res)
}
await getPersonList(); // 删除后刷新右侧列表
emit('done');
ElMessage.success('删除成功');
};
const exportData = () => {

View File

@@ -8,10 +8,26 @@
:inline="true"
label-width="68px"
>
<el-form-item label="场次" prop="batch">
<el-input
v-model="queryParams.batch"
placeholder="请输入场次"
clearable
class="!w-240px"
@keyup.enter="handleQuery"
/>
</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="warning"
plain
@click="handleImport"
>
<Icon icon="ep:upload" /> 导入
</el-button>
<el-button
type="primary"
@@ -83,6 +99,7 @@
<!-- 编辑弹窗 -->
<person-edit v-model="showEdit" :task-Id="props.taskId" @done="reload" />
<PersonSearch :task-id="props.taskId" v-model="showPersonEdit" ref="stuAddRef" @done="reload" />
<StudentImportForm ref="importFormRef" @success="getList" :task-id="props.taskId"/>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
@@ -90,6 +107,7 @@ 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 StudentImportForm from './components/StudentImportForm.vue'
import * as SmsChannelApi from '@/api/system/session';
import { fa } from 'element-plus/es/locale';
@@ -133,8 +151,7 @@ const queryFormRef = ref() // 搜索的表单
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
signature: undefined,
status: undefined,
batch: undefined,
createTime: [],
taskId: props.taskId
})
@@ -237,7 +254,10 @@ const openSearch = (type: string, row?: object) => {
})
}
const importFormRef = ref()
const handleImport = () => {
importFormRef.value.open()
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {