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

chore: update

上级 f517ef2b
......@@ -49,6 +49,11 @@ export function getProjectList(params: { organization_id?: string; project_id?:
return httpRequest.get('/api/resource/v1/util/members', { params })
}
// 获取赛项列表
export function getContestList(params?: { name?: string }) {
return httpRequest.get('/api/resource/v1/backend/competition/menu-list', { params })
}
// 获取待办消息
// https://gitlab.ezijing.com/root/api-documents/-/blob/master/messages/api/%E4%B8%80%E6%9C%9F%E7%AB%99%E5%86%85%E4%BF%A1%E6%8E%A5%E5%8F%A3%E6%96%87%E6%A1%A3.md#%E8%8E%B7%E5%8F%96%E7%AB%99%E5%86%85%E4%BF%A1%E6%B6%88%E6%81%AF%E5%88%97%E8%A1%A8
export function getMessages(params: {
......
import { getContestList } from '@/api/base'
// 赛项信息
interface ContestType {
id: string
name: string
}
const contests = ref<ContestType[]>([])
export function useGetContestList() {
!contests.value.length &&
getContestList().then((res: any) => {
contests.value = res.data.list
})
return { contests }
}
......@@ -33,7 +33,7 @@ watchEffect(() => {
<el-button type="primary" round :icon="Upload" @click="handleImport">本地上传</el-button>
<p>
<a
href="https://webapp-pub.ezijing.com/project/saas-lab/%E5%AE%9E%E9%AA%8C%E6%88%90%E7%BB%A9%E5%AF%BC%E5%85%A5%E6%A8%A1%E6%9D%BF.xlsx"
href="https://webapp-pub.ezijing.com/project/saas-lab/%E5%8F%82%E8%B5%9B%E9%80%89%E6%89%8B%E6%A8%A1%E6%9D%BF.xlsx"
download
>下载模板</a
>
......
......@@ -4,6 +4,7 @@ import { Upload } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import { getContestantList } from '../api'
import { useMapStore } from '@/stores/map'
import { useGetContestList } from '@/composables/useGetContestList'
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
const ViewDialog = defineAsyncComponent(() => import('../components/ViewDialog.vue'))
......@@ -12,8 +13,10 @@ const ImportDialog = defineAsyncComponent(() => import('../components/ImportDial
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 性别
const genderList = useMapStore().getMapValuesByKey('system_gender')
const { contests } = useGetContestList()
// 列表配置
const listOptions = {
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getContestantList,
params: { competition_id: '', student_name: '' }
......@@ -23,7 +26,10 @@ const listOptions = {
type: 'select',
prop: 'competition_id',
label: '赛项',
placeholder: '请选择赛项'
placeholder: '请选择赛项',
options: contests.value,
valueKey: 'id',
labelKey: 'name'
},
{ type: 'input', prop: 'student_name', label: '选手姓名', placeholder: '请输入选手姓名' }
],
......@@ -48,7 +54,8 @@ const listOptions = {
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 200 }
]
}
}
})
const importVisible = $ref(false)
let dialogVisible = $ref(false)
......
......@@ -78,3 +78,27 @@ export function updateContestVideo(data: ContestVideoUpdateParams) {
export function deleteContestVideo(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-video/delete', data)
}
// 获取赛项评分专家列表
export function getContestJudgeList(params: { id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition/experts', { params })
}
// 获取赛项未绑定的评分专家列表
export function getContestUnbindJudgeList(params: { competition_id: string }) {
return httpRequest.get('/api/resource/v1/backend/expert/unbind-competition-list', { params })
}
// 更新赛项评分专家
export function updateContestJudge(data: { id: string; experts: Record<string, any>[] }) {
return httpRequest.post('/api/resource/v1/backend/competition/bind-experts', data, {
headers: { 'Content-Type': 'application/json' }
})
}
// 获取评分专家详情
export function getJudge(params: { id: string }) {
return httpRequest.get('/api/resource/v1/backend/expert/detail', { params })
}
// 获取赛项选手列表
export function getContestContestantList(params?: { student_name?: string; page?: number; 'per-page'?: number }) {
return httpRequest.get('/api/resource/v1/backend/competition-competitor/list', { params })
}
<script setup lang="ts">
import type { ContestItem } from '../types'
import AppList from '@/components/base/AppList.vue'
import { getContestContestantList } from '../api'
import { useMapStore } from '@/stores/map'
const detail = $ref<ContestItem>(inject('detail'))
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 性别
const genderList = useMapStore().getMapValuesByKey('system_gender')
const listOptions = {
remote: {
httpRequest: getContestContestantList,
params: { competition_id: detail.id, student_name: '' }
},
filters: [{ type: 'input', prop: 'student_name', label: '选手姓名', placeholder: '请输入选手姓名' }],
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '赛项名称', prop: 'competition.name' },
{ label: '选手姓名', prop: 'student.name' },
{ label: '参赛ID', prop: 'login_id' },
{
label: '性别',
prop: 'student.gender',
computed({ row }: { row: any }) {
const found = genderList.find(item => item.value === row.student.gender)
return found?.label || row.student.gender
}
},
{ label: '学校', prop: 'student.org.department_name' },
{ label: '所在专业', prop: 'student.specialty.name' },
{ label: '所在年级', prop: 'grade' },
{ label: '所在班级', prop: 'student.class.name' },
{ label: '训练次数', prop: 'train_count' },
{ label: '更新时间', prop: 'updated_time' }
]
}
</script>
<template>
<el-dialog title="参赛选手列表">
<AppList v-bind="listOptions" ref="appList"></AppList>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</el-dialog>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ContestItem } from '../types'
import { getContestUnbindJudgeList, getJudge } from '../api'
import { useMapStore } from '@/stores/map'
const emit = defineEmits<{
(e: 'add', data: any): void
(e: 'update:modelValue', visible: boolean): void
}>()
// 角色列表
const roleList = useMapStore().getMapValuesByKey('expert_role')
const detail = $ref<ContestItem>(inject('detail'))
let list = $ref<any>([])
function fetchList() {
getContestUnbindJudgeList({ competition_id: detail.id }).then(res => {
list = res.data.list || []
})
}
let info = $ref<any>()
function fetchInfo(id: string) {
if (id) {
getJudge({ id }).then(res => {
info = res.data.detail
})
} else {
info = undefined
}
}
onMounted(() => {
fetchList()
})
const formRef = $ref<FormInstance>()
const form = reactive({
role: '',
id: ''
})
const rules = ref<FormRules>({
role: [{ required: true, message: '请选择角色', trigger: 'change' }],
id: [{ required: true, message: '请选择专家', trigger: 'change' }]
})
// 添加
function handleSubmit() {
formRef?.validate().then(() => {
emit('add', Object.assign(info, { info: { role: form.role, id: '0' }, expert_id: form.id, id: '0' }))
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog title="添加专家" width="500px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="100px" label-suffix=":">
<el-form-item label="赛项名称">{{ detail.name }}</el-form-item>
<el-form-item label="角色" prop="role">
<el-select v-model="form.role" style="width: 100%">
<el-option v-for="item in roleList" :key="item.id" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="专家" prop="id">
<el-select v-model="form.id" @change="fetchInfo" style="width: 100%">
<el-option v-for="item in list" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
<template v-if="info">
<el-form-item label="联系电话">{{ info.mobile }}</el-form-item>
<el-form-item label="所在单位">{{ info.company }}</el-form-item>
<el-form-item label="职务">{{ info.position.label }}</el-form-item>
</template>
</el-form>
<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-dialog>
</template>
<script setup lang="ts">
import type { ContestItem } from '../types'
import { CirclePlus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
import { getContestJudgeList, updateContestJudge } from '../api'
import { useMapStore } from '@/stores/map'
const emit = defineEmits<{
(e: 'update:modelValue', visible: boolean): void
}>()
const JudgeAddDialog = defineAsyncComponent(() => import('./JudgeAddDialog.vue'))
const detail = $ref<ContestItem>(inject('detail'))
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 性别
const genderList = useMapStore().getMapValuesByKey('system_gender')
// 角色列表
const roleList = useMapStore().getMapValuesByKey('expert_role')
let list = $ref<any>([])
function fetchList() {
getContestJudgeList({ id: detail.id }).then((res: any) => {
list = res.data.detail
})
}
onMounted(() => {
fetchList()
})
// 列表配置
const listOptions = $computed(() => {
return {
hasPagination: false,
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '专家姓名', prop: 'name' },
{ label: '所在单位', prop: 'company' },
{
label: '性别',
prop: 'sex',
computed({ row }: { row: any }) {
const found = genderList.find(item => item.value === row.sex)
return found?.label || row.sex
}
},
{ label: '职务', prop: 'position.label' },
{ label: '省', prop: 'province' },
{ label: '市', prop: 'city' },
{ label: '区/县', prop: 'area' },
{ label: '更新时间', prop: 'updated_time' },
{
label: '角色',
prop: 'info.role',
computed({ row }: { row: any }) {
const found = roleList.find(item => item.value === row.info.role)
return found?.label || row.info.role
}
},
{ label: '操作', slots: 'table-x', width: 80 }
],
data: list
}
})
const dialogVisible = $ref(false)
function handleAdd(data: any) {
list.push(data)
}
// 保存
function handleSubmit() {
const experts = list.map((item: any) => {
return { id: item.id, expert_id: item.expert_id, role: item.info.role }
})
updateContestJudge({ id: detail.id, experts }).then(() => {
ElMessage.success('保存成功')
emit('update:modelValue', false)
})
}
// 移除
function handleRemoveClass(index: number) {
ElMessageBox.confirm('确定要移除吗?', '提示').then(() => {
list.splice(index, 1)
})
}
</script>
<template>
<el-dialog title="评分专家列表" :close-on-click-modal="false" @update:modelValue="$emit('update:modelValue')">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-row justify="space-between">
<el-button type="primary" :icon="CirclePlus" @click="dialogVisible = true">添加专家</el-button>
<p>赛项名称: {{ detail.name }}</p>
</el-row>
</template>
<template #table-x="{ $index }">
<el-button link round type="danger" @click="handleRemoveClass($index)">移除</el-button>
</template>
</AppList>
<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>
<JudgeAddDialog v-model="dialogVisible" @add="handleAdd" v-if="dialogVisible"></JudgeAddDialog>
</el-dialog>
</template>
......@@ -7,6 +7,8 @@ import dayjs from 'dayjs'
const ViewBook = defineAsyncComponent(() => import('../components/ViewBook.vue'))
const ViewVideo = defineAsyncComponent(() => import('../components/ViewVideo.vue'))
const JudgingRulesDialog = defineAsyncComponent(() => import('../components/JudgingRulesDialog.vue'))
const JudgeDialog = defineAsyncComponent(() => import('../components/JudgeDialog.vue'))
const ContestantDialog = defineAsyncComponent(() => import('../components/ContestantDialog.vue'))
interface Props {
id: string
......@@ -52,15 +54,20 @@ function formatDate(timestamp: number): string {
function formatDateTime(timestamp: number): string {
return dayjs(timestamp * 1000).format('YYYY-MM-DD HH:mm:ss')
}
// 评分规则
const judgingRulesVisible = $ref(false)
// 评分专家
const judgeVisible = $ref(false)
// 参赛选手
const contestantVisible = $ref(false)
</script>
<template>
<AppCard title="查看赛项信息">
<template #header-aside>
<el-button type="primary" @click="judgingRulesVisible = true">评分规则</el-button>
<el-button type="primary">评分专家</el-button>
<el-button type="primary">参赛选手</el-button>
<el-button type="primary" @click="judgeVisible = true">评分专家</el-button>
<el-button type="primary" @click="contestantVisible = true">参赛选手</el-button>
<el-button type="primary">评分细则</el-button>
</template>
<div class="top" v-if="detail">
......@@ -91,7 +98,12 @@ const judgingRulesVisible = $ref(false)
<AppCard title="操作视频">
<ViewVideo :id="id"></ViewVideo>
</AppCard>
<!-- 评分规则 -->
<JudgingRulesDialog v-model="judgingRulesVisible" v-if="judgingRulesVisible && detail"></JudgingRulesDialog>
<!-- 评分专家 -->
<JudgeDialog v-model="judgeVisible" v-if="judgeVisible && detail"></JudgeDialog>
<!-- 参赛选手 -->
<ContestantDialog v-model="contestantVisible" v-if="contestantVisible && detail"></ContestantDialog>
</template>
<style lang="scss">
......
......@@ -4,6 +4,7 @@ import { CirclePlus, Upload } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import { getJudgeList } from '../api'
import { useMapStore } from '@/stores/map'
import { useGetContestList } from '@/composables/useGetContestList'
const ViewDialog = defineAsyncComponent(() => import('../components/ViewDialog.vue'))
const FormDialog = defineAsyncComponent(() => import('../components/FormDialog.vue'))
......@@ -13,8 +14,10 @@ const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 性别
const genderList = useMapStore().getMapValuesByKey('system_gender')
const { contests } = useGetContestList()
// 列表配置
const listOptions = {
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getJudgeList,
params: { competition_id: '', name: '' }
......@@ -24,7 +27,10 @@ const listOptions = {
type: 'select',
prop: 'competition_id',
label: '赛项',
placeholder: '请选择赛项'
placeholder: '请选择赛项',
options: contests.value,
valueKey: 'id',
labelKey: 'name'
},
{ type: 'input', prop: 'name', label: '专家姓名', placeholder: '请输入专家姓名' }
],
......@@ -48,7 +54,8 @@ const listOptions = {
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 200 }
]
}
}
})
const importVisible = $ref(false)
const rowData = ref<Judge | undefined | null>(null)
......
......@@ -5,6 +5,7 @@ import { useUserStore } from '@/stores/user'
interface State {
studentMenus: IMenuItem[]
adminMenus: IMenuItem[]
teacherMenus: IMenuItem[]
}
// 学生菜单
......@@ -13,21 +14,13 @@ const studentMenus: IMenuItem[] = [
name: '智能营销',
path: '/student/lab'
},
// {
// name: '智能陪练',
// path: '/student/ai'
// },
// {
// name: '成绩分析',
// path: '/admin/contest/score'
// },
{
name: '技能大赛',
path: '/student/contest'
}
]
// 教师、管理员菜单
const adminMenus: IMenuItem[] = [
// 教师、专家菜单
const teacherMenus: IMenuItem[] = [
{
name: '智能营销',
path: '/teacher/lab',
......@@ -55,34 +48,29 @@ const adminMenus: IMenuItem[] = [
}
]
},
// {
// name: '智能陪练',
// path: '/admin/ai'
// },
// {
// name: '成绩分析',
// path: '/admin/contest/score'
// },
{
name: '技能大赛',
path: '/admin/contest',
path: '/teacher/contest',
children: [
{
name: '赛项管理',
path: '/admin/contest/items'
name: '训练答疑',
path: '/teacher/contest/items'
},
{
name: '参赛选手管理',
path: '/admin/contest/contestants'
name: '大赛评分',
path: '/teacher/contest/contestants'
},
{
name: '评分专家管理',
path: '/admin/contest/judges'
name: '发布成绩',
path: '/teacher/contest/judges'
}
]
},
}
]
// 管理员菜单
const adminMenus: IMenuItem[] = [
{
name: '系统管理',
name: '智能营销',
path: '/admin/lab',
tag: 'v1-backend-experiment',
children: [
......@@ -92,6 +80,24 @@ const adminMenus: IMenuItem[] = [
tag: 'v1-backend-experiment'
}
]
},
{
name: '技能大赛',
path: '/admin/contest',
children: [
{
name: '赛项管理',
path: '/admin/contest/items'
},
{
name: '参赛选手管理',
path: '/admin/contest/contestants'
},
{
name: '评分专家管理',
path: '/admin/contest/judges'
}
]
}
]
......@@ -99,12 +105,19 @@ export const useMenuStore = defineStore({
id: 'menu',
state: (): State => ({
studentMenus,
adminMenus
adminMenus,
teacherMenus
}),
getters: {
menus: state => {
const userStore = useUserStore()
return userStore.role?.id === 1 ? state.studentMenus : state.adminMenus
if (userStore.role?.id === 6) {
return state.adminMenus
} else if (userStore.role?.id === 5) {
return state.teacherMenus
} else {
return state.studentMenus
}
}
},
actions: {}
......
......@@ -3,9 +3,9 @@ import { defineStore } from 'pinia'
import { getUser, logout } from '@/api/base'
import { useMapStore } from '@/stores/map'
// 角色信息(1学员;5教师)
// 角色信息(1学员;5教师; 6管理员)
interface Role {
id: 1 | 5
id: 1 | 5 | 6
name: string
}
interface State {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论