提交 ad3aa40e authored 作者: 王鹏飞's avatar 王鹏飞

chore: 新增大赛模块试卷

上级 fff86883
import httpRequest from '@/utils/axios' import httpRequest from '@/utils/axios'
import type { import type { ContestCreateParams, ContestUpdateParams, ContestBookUpdateParams, ContestVideoCreateParams, ContestVideoUpdateParams } from './types'
ContestCreateParams,
ContestUpdateParams,
ContestBookUpdateParams,
ContestVideoCreateParams,
ContestVideoUpdateParams
} from './types'
// 获取赛项列表 // 获取赛项列表
export function getContestList(params?: { page?: number; 'per-page'?: number }) { export function getContestList(params?: { page?: number; 'per-page'?: number }) {
...@@ -28,13 +22,7 @@ export function updateContest(data: ContestUpdateParams) { ...@@ -28,13 +22,7 @@ export function updateContest(data: ContestUpdateParams) {
} }
// 获取指导老师列表 // 获取指导老师列表
export function getTeacherList(params?: { export function getTeacherList(params?: { name?: string; email?: string; sso_id?: string; page?: number; 'per-page'?: number }) {
name?: string
email?: string
sso_id?: string
page?: number
'per-page'?: number
}) {
return httpRequest.get('/api/resource/v1/backend/teacher/faculty-advisers', { params }) return httpRequest.get('/api/resource/v1/backend/teacher/faculty-advisers', { params })
} }
...@@ -49,13 +37,7 @@ export function updateContestRules(data: ContestUpdateParams) { ...@@ -49,13 +37,7 @@ export function updateContestRules(data: ContestUpdateParams) {
}) })
} }
// 获取考试列表 // 获取考试列表
export function getExamList(params: { export function getExamList(params: { project: string; q?: string; name?: string; page?: number; 'per-page'?: number }) {
project: string
q?: string
name?: string
page?: number
'per-page'?: number
}) {
return httpRequest.get('/api/resource/v1/backend/exam/search-all-exam', { params }) return httpRequest.get('/api/resource/v1/backend/exam/search-all-exam', { params })
} }
...@@ -153,7 +135,6 @@ export function getPlatformKeys(params?: { competition_id: string }) { ...@@ -153,7 +135,6 @@ export function getPlatformKeys(params?: { competition_id: string }) {
export function getQuestionList(params: { competition_id: string }) { export function getQuestionList(params: { competition_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-question/list', { params }) return httpRequest.get('/api/resource/v1/backend/competition-question/list', { params })
} }
// 添加大赛试题 // 添加大赛试题
export function createQuestion(data: ContestBookUpdateParams) { export function createQuestion(data: ContestBookUpdateParams) {
return httpRequest.post('/api/resource/v1/backend/competition-question/create', data) return httpRequest.post('/api/resource/v1/backend/competition-question/create', data)
...@@ -166,3 +147,20 @@ export function updateQuestion(data: ContestBookUpdateParams) { ...@@ -166,3 +147,20 @@ export function updateQuestion(data: ContestBookUpdateParams) {
export function deleteQuestion(data: { id: string }) { export function deleteQuestion(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-question/delete', data) return httpRequest.post('/api/resource/v1/backend/competition-question/delete', data)
} }
// 获取赛项模块试卷
export function getAllExamList(params: { competition_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-exam/list', { params })
}
// 添加大赛模块试卷
export function createExam(data: { competition_id: string; exam_id: string; sort: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-exam/create', data)
}
// 更新大赛模块试卷
export function updateExam(data: { id: string; exam_id: string; sort: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-exam/update', data)
}
// 删除大赛模块试卷
export function deleteExam(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-exam/delete', data)
}
<script setup lang="ts">
import { CirclePlus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
import { getAllExamList, deleteExam } from '../api'
const FormDialog = defineAsyncComponent(() => import('./ViewExamFormDialog.vue'))
interface Props {
id: string
}
const props = defineProps<Props>()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listOptions = {
hasPagination: false,
remote: {
httpRequest: getAllExamList,
params: { competition_id: props.id },
callback(res: any) {
return res?.list ? { list: res.list } : { list: [] }
}
},
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '试卷名称', prop: 'exam_info.name' },
{ label: '创建人', prop: 'created_operator_name' },
{ label: '创建时间', prop: 'created_time' },
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 180 }
]
}
let dialogVisible = $ref(false)
const rowData = ref<any>()
// 新增
function handleAdd() {
rowData.value = undefined
dialogVisible = true
}
// 编辑
function handleUpdate(row: any) {
rowData.value = row
dialogVisible = true
}
// 查阅
function handleView(row: any) {
const [paper] = row.exam_info.paper_list
if (!paper) return
const qaURL = `https://qa-center.ezijing.com/paper/detail/${paper.paper_id}`
window.open(qaURL)
}
// 删除
function handleRemoveClass(row: any) {
ElMessageBox.confirm('确定要删除吗?', '提示').then(() => {
deleteExam({ id: row.id }).then(() => {
ElMessage({ message: '删除成功', type: 'success' })
onUpdateSuccess()
})
})
}
function onUpdateSuccess() {
appList?.refetch()
}
</script>
<template>
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" :icon="CirclePlus" @click="handleAdd" v-permission="'competition-book-create'">新增</el-button>
</template>
<template #table-x="{ row }">
<el-button link round type="info" @click="handleView(row)" v-permission="'competition-book-detail'">查阅</el-button>
<el-button link round type="primary" @click="handleUpdate(row)" v-permission="'competition-book-update'">编辑</el-button>
<el-button link round type="danger" @click="handleRemoveClass(row)" v-permission="'competition-book-delete'">删除</el-button>
</template>
</AppList>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ContestItem } from '../types'
import { ElMessage } from 'element-plus'
import { pick } from 'lodash-es'
import { createExam, updateExam, getExamList } from '../api'
interface Props {
data?: any
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const detail = $ref(inject('detail') as ContestItem)
const formRef = $ref<FormInstance>()
const form = reactive<any>({
competition_id: detail.id,
exam_id: '',
sort: '1',
platform_key: 'career_data_analysis'
})
watchEffect(() => {
if (!props.data) return
Object.assign(form, props.data)
})
const rules = ref<FormRules>({
exam_id: [{ required: true, message: '请选择试卷', trigger: 'change' }]
})
const isUpdate = $computed(() => {
return !!props.data?.id
})
const title = $computed(() => {
return isUpdate ? '编辑大赛模块试卷' : '新增大赛模块试卷'
})
let examList = $ref<Record<string, any>[]>([])
// 获取关联考试列表
function fetchExamList() {
getExamList({ project: 'x1', 'per-page': 1000 }).then(res => {
examList = res.data.list || []
})
}
onMounted(() => {
fetchExamList()
})
// 提交
function handleSubmit() {
formRef?.validate().then(() => {
const params = Object.assign({}, pick(form, ['competition_id', 'exam_id', 'sort']))
isUpdate ? handleUpdate(params) : handleCreate(params)
})
}
// 新增
function handleCreate(params: any) {
createExam(params).then(() => {
ElMessage({ message: '创建成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
}
// 修改
function handleUpdate(params: any) {
params.id = props.data?.id
updateExam(params).then(() => {
ElMessage({ message: '修改成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
<el-form-item label="试卷名称" prop="exam_id">
<el-select v-model="form.exam_id" filterable style="width: 100%">
<el-option v-for="item in examList" :key="item.exam_id" :label="item.name" :value="item.exam_id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="模块" prop="sort">
<el-input v-model="form.sort"></el-input>
</el-form-item>
<el-form-item label="关联赛项">
<el-input :value="detail.name" disabled></el-input>
</el-form-item>
<el-form-item label="关联实验">
<el-radio-group v-model="form.platform_key">
<el-radio label="career_data_analysis">商业数据分析实验</el-radio>
</el-radio-group>
</el-form-item>
<el-row justify="center">
<el-button type="primary" round auto-insert-space @click="handleSubmit">保存</el-button>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row>
</el-form>
</el-dialog>
</template>
...@@ -8,6 +8,7 @@ import dayjs from 'dayjs' ...@@ -8,6 +8,7 @@ import dayjs from 'dayjs'
const ViewBook = defineAsyncComponent(() => import('../components/ViewBook.vue')) const ViewBook = defineAsyncComponent(() => import('../components/ViewBook.vue'))
const ViewVideo = defineAsyncComponent(() => import('../components/ViewVideo.vue')) const ViewVideo = defineAsyncComponent(() => import('../components/ViewVideo.vue'))
const ViewQuestion = defineAsyncComponent(() => import('../components/ViewQuestion.vue')) const ViewQuestion = defineAsyncComponent(() => import('../components/ViewQuestion.vue'))
const ViewExam = defineAsyncComponent(() => import('../components/ViewExam.vue'))
const ScoringRulesDialog = defineAsyncComponent(() => import('../components/ScoringRulesDialog.vue')) const ScoringRulesDialog = defineAsyncComponent(() => import('../components/ScoringRulesDialog.vue'))
const ScoringExpertsDialog = defineAsyncComponent(() => import('../components/ScoringExpertsDialog.vue')) const ScoringExpertsDialog = defineAsyncComponent(() => import('../components/ScoringExpertsDialog.vue'))
const ContestantDialog = defineAsyncComponent(() => import('../components/ContestantDialog.vue')) const ContestantDialog = defineAsyncComponent(() => import('../components/ContestantDialog.vue'))
...@@ -119,16 +120,10 @@ function handleExperts() { ...@@ -119,16 +120,10 @@ function handleExperts() {
<template> <template>
<AppCard title="查看赛项信息"> <AppCard title="查看赛项信息">
<template #header-aside> <template #header-aside>
<el-button type="primary" @click="scoringRulesVisible = true" v-permission="'competition-rule'" <el-button type="primary" @click="scoringRulesVisible = true" v-permission="'competition-rule'">评分规则</el-button>
>评分规则</el-button
>
<el-button type="primary" @click="handleExperts" v-permission="'competition-bind-experts'">评分专家</el-button> <el-button type="primary" @click="handleExperts" v-permission="'competition-bind-experts'">评分专家</el-button>
<el-button type="primary" @click="contestantVisible = true" v-permission="'competition-competitor-list'" <el-button type="primary" @click="contestantVisible = true" v-permission="'competition-competitor-list'">参赛选手</el-button>
>参赛选手</el-button <el-button type="primary" @click="scoringRulesBookVisible = true" v-permission="'competition-rubric-update'">评分细则</el-button>
>
<el-button type="primary" @click="scoringRulesBookVisible = true" v-permission="'competition-rubric-update'"
>评分细则</el-button
>
</template> </template>
<div class="top" v-if="detail"> <div class="top" v-if="detail">
<div class="top-cover"> <div class="top-cover">
...@@ -141,15 +136,11 @@ function handleExperts() { ...@@ -141,15 +136,11 @@ function handleExperts() {
<el-descriptions-item label="主办单位:">{{ detail.host_unit.label }}</el-descriptions-item> <el-descriptions-item label="主办单位:">{{ detail.host_unit.label }}</el-descriptions-item>
<el-descriptions-item label="指导教师:">{{ teacherText }}</el-descriptions-item> <el-descriptions-item label="指导教师:">{{ teacherText }}</el-descriptions-item>
<el-descriptions-item label="承办单位:">{{ orgText }}</el-descriptions-item> <el-descriptions-item label="承办单位:">{{ orgText }}</el-descriptions-item>
<el-descriptions-item label="赛项周期:" <el-descriptions-item label="赛项周期:">{{ formatDate(detail.start_range) }} ~ {{ formatDate(detail.end_range) }}</el-descriptions-item>
>{{ formatDate(detail.start_range) }} ~ {{ formatDate(detail.end_range) }}</el-descriptions-item
>
<el-descriptions-item label="技术支持单位:">{{ detail.technical_support_unit.label }}</el-descriptions-item> <el-descriptions-item label="技术支持单位:">{{ detail.technical_support_unit.label }}</el-descriptions-item>
<el-descriptions-item label="正式比赛日期:">{{ formatDate(detail.start_at) }}</el-descriptions-item> <el-descriptions-item label="正式比赛日期:">{{ formatDate(detail.start_at) }}</el-descriptions-item>
<el-descriptions-item label="生效状态:">{{ statusText }}</el-descriptions-item> <el-descriptions-item label="生效状态:">{{ statusText }}</el-descriptions-item>
<el-descriptions-item label="正式比赛时间:" <el-descriptions-item label="正式比赛时间:">{{ formatTime(detail.start_at) }} ~ {{ formatTime(detail.end_at) }}</el-descriptions-item>
>{{ formatTime(detail.start_at) }} ~ {{ formatTime(detail.end_at) }}</el-descriptions-item
>
<el-descriptions-item label="专家组长:">{{ expertLeadersText }}</el-descriptions-item> <el-descriptions-item label="专家组长:">{{ expertLeadersText }}</el-descriptions-item>
<el-descriptions-item label="专家:">{{ expertMembersText }}</el-descriptions-item> <el-descriptions-item label="专家:">{{ expertMembersText }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
...@@ -164,32 +155,17 @@ function handleExperts() { ...@@ -164,32 +155,17 @@ function handleExperts() {
<AppCard title="大赛试题"> <AppCard title="大赛试题">
<ViewQuestion :id="id"></ViewQuestion> <ViewQuestion :id="id"></ViewQuestion>
</AppCard> </AppCard>
<AppCard title="大赛模块试卷">
<ViewExam :id="id"></ViewExam>
</AppCard>
<!-- 评分规则 --> <!-- 评分规则 -->
<ScoringRulesDialog <ScoringRulesDialog v-model="scoringRulesVisible" :disabled="isStarted" @update="fetchRule" v-if="scoringRulesVisible && detail"></ScoringRulesDialog>
v-model="scoringRulesVisible"
:disabled="isStarted"
@update="fetchRule"
v-if="scoringRulesVisible && detail"
></ScoringRulesDialog>
<!-- 评分专家 --> <!-- 评分专家 -->
<ScoringExpertsDialog <ScoringExpertsDialog v-model="scoringExpertsVisible" :disabled="isStarted" @update="fetchExperts" v-if="scoringExpertsVisible && detail"></ScoringExpertsDialog>
v-model="scoringExpertsVisible"
:disabled="isStarted"
@update="fetchExperts"
v-if="scoringExpertsVisible && detail"
></ScoringExpertsDialog>
<!-- 参赛选手 --> <!-- 参赛选手 -->
<ContestantDialog <ContestantDialog v-model="contestantVisible" :disabled="isStarted" v-if="contestantVisible && detail"></ContestantDialog>
v-model="contestantVisible"
:disabled="isStarted"
v-if="contestantVisible && detail"
></ContestantDialog>
<!-- 评分细则 --> <!-- 评分细则 -->
<ScoringRulesBookDialog <ScoringRulesBookDialog v-model="scoringRulesBookVisible" :disabled="isStarted" v-if="scoringRulesBookVisible && detail"></ScoringRulesBookDialog>
v-model="scoringRulesBookVisible"
:disabled="isStarted"
v-if="scoringRulesBookVisible && detail"
></ScoringRulesBookDialog>
</template> </template>
<style lang="scss"> <style lang="scss">
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论