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

chore: 历史监控增加列表项

/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript/recommended',
'./.eslintrc-auto-import.json'
],
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off'
}
}
import pluginVue from 'eslint-plugin-vue'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import AutoImportJson from './.eslintrc-auto-import.json' with { type: 'json' }
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**', '**/lib/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig(),
{
languageOptions: {
...AutoImportJson,
},
rules: {
'vue/block-lang': 'off',
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
},
},
]
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -9,7 +9,7 @@
"build:pre": "vue-tsc --noEmit && vite build --mode pre",
"preview": "vite preview --port 4173",
"typecheck": "vue-tsc --noEmit",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"lint": "eslint . --fix",
"deploy": "node ./deploy.js",
"cert": "node ./cert.js"
},
......@@ -44,7 +44,6 @@
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@tsconfig/node20": "^20.1.4",
"@types/ali-oss": "^6.16.8",
"@types/blueimp-md5": "^2.18.0",
......@@ -54,18 +53,18 @@
"@types/ua-parser-js": "^0.7.36",
"@types/video.js": "^7.3.52",
"@vitejs/plugin-vue": "^5.1.4",
"@vue-macros/reactivity-transform": "^1.1.2",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1",
"@vue-macros/reactivity-transform": "^1.1.3",
"@vue/eslint-config-typescript": "^14.1.3",
"@vue/tsconfig": "^0.6.0",
"chalk": "^5.2.0",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.25.0",
"eslint": "^9.14.0",
"eslint-plugin-vue": "^9.30.0",
"sass": "^1.58.3",
"typescript": "~5.4.5",
"typescript": "~5.6.3",
"unplugin-auto-import": "^0.17.8",
"vite": "^5.4.9",
"vite-plugin-checker": "^0.6.4",
"vite": "^5.4.10",
"vite-plugin-checker": "^0.8.0",
"vite-plugin-mkcert": "^1.17.6",
"vue-tsc": "^1.8.27"
"vue-tsc": "^2.1.10"
}
}
差异被折叠。
......@@ -5,7 +5,7 @@ const emit = defineEmits<{
(e: 'resize'): void
}>()
const props = defineProps<{ isLeftShow?: number }>()
defineProps<{ isLeftShow?: number }>()
const leftPanelVisible = $ref<boolean>(true)
const leftPanelWidth = useStorage('leftPanelWidth', 400)
......@@ -19,10 +19,10 @@ let dragFlag = $ref(false)
function useDrag() {
const dragDom = document.getElementById('panel-resize')
if (dragDom) {
dragDom.addEventListener('mousedown', e => {
dragDom.addEventListener('mousedown', (e) => {
e.preventDefault()
dragFlag = true
document.onmousemove = e => {
document.onmousemove = (e) => {
leftPanelWidth.value = e.clientX
}
document.onmouseup = () => {
......@@ -52,14 +52,12 @@ onMounted(() => {
<path
class="path-wapper"
d="M0 0l14.12 8.825A4 4 0 0116 12.217v61.566a4 4 0 01-1.88 3.392L0 86V0z"
fill="#e1e4eb"
></path>
fill="#e1e4eb"></path>
<path
class="path-arrow"
d="M10.758 48.766a.778.778 0 000-1.127L6.996 43l3.762-4.639a.778.778 0 000-1.127.85.85 0 00-1.172 0l-4.344 5.202a.78.78 0 000 1.128l4.344 5.202a.85.85 0 001.172 0z"
fill="#8D9EA7"
fill-rule="nonzero"
></path>
fill-rule="nonzero"></path>
</g>
</svg>
</div>
......
......@@ -34,7 +34,7 @@ const defaultActive = computed(() => {
}
return result
}, [])
const found = flatMenuList.reverse().find(item => {
const found = flatMenuList.reverse().find((item) => {
return route.path.includes(item.path)
})
return found ? found.path : '/'
......@@ -139,7 +139,10 @@ function handleClick(path: string) {
height: 66px;
background-color: #fff;
.logo {
width: 145px;
height: 40px;
img {
height: 100%;
}
}
}
.app-header-left {
......
......@@ -20,7 +20,7 @@ const appConfigList = [
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的实验', path: '/student/lab' },
{ name: '理论学习', path: import.meta.env.VITE_SAAS_LEARN_URL }
{ name: '理论学习', path: import.meta.env.VITE_SAAS_LEARN_URL },
],
adminMenus: [
{ name: '首页', path: '/' },
......@@ -34,22 +34,22 @@ const appConfigList = [
{ name: '实验指导书管理', path: '/admin/lab/book', tag: 'v1-teacher-book' },
{ name: '实验操作视频管理', path: '/admin/lab/video', tag: 'v1-teacher-video' },
{ name: '实验讨论交流', path: '/admin/lab/discuss', tag: 'v1-teacher-discussion' },
{ name: '实验成绩管理', path: '/admin/lab/score', tag: 'v1-teacher-record' }
]
}
]
{ name: '实验成绩管理', path: '/admin/lab/score', tag: 'v1-teacher-record' },
],
},
],
},
{
system: 'game',
title: '商业数据分析竞赛平台',
hosts: ['saas-game']
hosts: ['saas-game'],
},
{
system: 'dml',
title: '数智营销实践教学平台',
logo: '/logo.svg',
hosts: ['saas-dml-web'],
dmlURL: import.meta.env.VITE_DML_PRO_URL
dmlURL: import.meta.env.VITE_DML_PRO_URL,
},
{
system: 'swsjfxs',
......@@ -60,13 +60,13 @@ const appConfigList = [
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
{ name: '大赛成绩查询', path: '/student/contest/score' }
{ name: '大赛成绩查询', path: '/student/contest/score' },
],
xTrainLabel: '商务数据分析理论训练',
labTrainLabel: '商务数据分析实操训练',
xExamLabel: '商务数据分析理论考试',
labExamLabel: '商务数据分析实操考试',
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
},
{
system: 'swsjfxs',
......@@ -77,7 +77,7 @@ const appConfigList = [
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
{ name: '大赛成绩查询', path: '/student/contest/score' }
{ name: '大赛成绩查询', path: '/student/contest/score' },
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
......@@ -86,7 +86,7 @@ const appConfigList = [
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
hideContestToolbar: true, // 隐藏大赛工具栏
},
{
system: 'amo',
......@@ -97,7 +97,7 @@ const appConfigList = [
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
{ name: '大赛成绩查询', path: '/student/contest/score' }
{ name: '大赛成绩查询', path: '/student/contest/score' },
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
......@@ -106,7 +106,7 @@ const appConfigList = [
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
hideContestToolbar: true, // 隐藏大赛工具栏
},
{
system: 'default',
......@@ -117,7 +117,7 @@ const appConfigList = [
hosts: ['saas-lab-bda2'],
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' }
{ name: '我的大赛', path: '/student/contest' },
// { name: '大赛成绩查询', path: '/student/contest/score' }
],
xTrainLabel: '理论训练',
......@@ -127,13 +127,35 @@ const appConfigList = [
// loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
}
hideContestToolbar: true, // 隐藏大赛工具栏
},
{
system: 'nac',
title: '首届网络视听行业职业技能大赛',
logo: '/images/nac_logo.png',
logoWidth: '40px',
// logo: 'https://webapp-pub.ezijing.com/website/base/images/logo_swsjfxs.png',
// favicon: 'https://webapp-pub.ezijing.com/website/base/images/favicon_swsjfxs.png',
hosts: ['saas-lab-nac'],
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
// { name: '大赛成绩查询', path: '/student/contest/score' }
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
xExamLabel: '理论考试',
labExamLabel: '实操考试',
// loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true, // 隐藏大赛工具栏
},
]
export function useAppConfig() {
const found = appConfigList.find(item => {
return item.hosts.find(host => location.host.split('.').includes(host))
const found = appConfigList.find((item) => {
return item.hosts.find((host) => location.host.split('.').includes(host))
})
const appConfig = found || appConfigList[0]
......
......@@ -51,3 +51,8 @@ export function importScore(data: { competition_id: string; file: File }) {
export function getReportList(params: { competition_id: string; student_id: string }) {
return httpRequest.get('/api/lab/v1/expert/report/list', { params })
}
// 获取学员的直播记录
export function getLiveList(params: { experiment_id: string; student_id: string }) {
return httpRequest.get('/api/lab/v1/expert/check/stu-live-practice-records', { params })
}
<script setup lang="ts">
import type { RecordItem, ReportItem } from '../types'
import { getReportList } from '../api'
import { saveAs } from 'file-saver'
interface Props {
data: RecordItem
}
......@@ -15,16 +15,26 @@ async function fetchReport() {
onMounted(() => {
fetchReport()
})
// office文件
const getUrl = (url: string) => {
const fileExtensionName = url.split('.').pop() || ''
return ['pptx', 'doc', 'docx', 'xls', 'xlsx'].includes(fileExtensionName)
? `https://view.officeapps.live.com/op/view.aspx?src=${url}`
: url
}
</script>
<template>
<el-dialog title="查看报告" width="500px">
<ul>
<li v-for="item in reportList" :key="item.id" style="line-height: 24px">
<a :href="`https://view.officeapps.live.com/op/view.aspx?src=${item.url}`" target="_blank">
<a :href="getUrl(item.url)" target="_blank">
{{ item.name }}
<el-button size="small" type="primary" style="margin-left: 10px">查看</el-button>
</a>
<el-button size="small" type="primary" style="margin-left: 10px" @click="saveAs(item.url, item.name)"
>下载</el-button
>
</li>
</ul>
<template #footer>
......
......@@ -21,7 +21,7 @@ watchEffect(() => {
...item,
old_score: parseFloat(item.old_score),
ratio: parseFloat(item.ratio),
score: isNaN(parseFloat(item.score)) ? null : parseFloat(item.score)
score: isNaN(parseFloat(item.score)) ? 'null' : parseFloat(item.score)
}
})
})
......@@ -43,6 +43,9 @@ const score = $computed(() => {
// 提交
function handleSubmit() {
const scores = tableList.map((item: any) => {
if (item.type === '2') {
item.score = 0
}
return { id: item.id, score: item.score }
})
const params = { id: detail.id, scores: JSON.stringify(scores) }
......@@ -53,28 +56,43 @@ function handleSubmit() {
})
}
const qaURL = import.meta.env.VITE_QA_CENTER_URL
// 查看评分规则
const handleScoreRule = function () {
if (detail?.competition_rubric && detail?.competition_rubric?.url) {
window.open(detail?.competition_rubric?.url)
}
}
</script>
<template>
<el-dialog title="评分" :close-on-click-modal="false" width="700px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-dialog
title="评分"
:close-on-click-modal="false"
width="700px"
@update:modelValue="value => $emit('update:modelValue', value)"
>
<el-form label-suffix=":" v-if="detail">
<el-form-item label="赛项名称">{{ detail.competition_id_name }}</el-form-item>
<el-form-item label="选手姓名">{{ detail.student_name }}</el-form-item>
<el-form-item label="选手ID">{{ detail.login_id }}</el-form-item>
<el-form-item label="报告">
<el-dropdown>
<el-button text>
查看报告<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu v-if="reportList.length">
<el-dropdown-item v-for="item in reportList" :key="item.id">
<a :href="item.url" target="_blank">{{ item.platform.name }}</a>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
<div style="display: flex; justify-content: space-between">
<el-form-item label="报告" v-if="reportList.length">
<el-dropdown>
<el-button text>
查看报告<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu v-if="reportList.length">
<el-dropdown-item v-for="item in reportList" :key="item.id">
<a :href="item.url" target="_blank">{{ item.platform.name }}</a>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-form-item>
<el-button type="primary" style="margin-bottom: 20px" @click="handleScoreRule">查看评分规则</el-button>
</div>
</el-form>
<el-table :data="tableList" :header-cell-style="{ background: '#ededed' }">
<el-table-column label="评分规则" prop="name" align="center"></el-table-column>
......@@ -83,13 +101,25 @@ const qaURL = import.meta.env.VITE_QA_CENTER_URL
<el-table-column label="占比(%)" prop="ratio" align="center"></el-table-column>
<el-table-column label="评分" prop="score" align="center">
<template #default="{ row }">
<el-input-number :controls="false" v-model="row.score" step-strictly :step="0.1" :min="0" :max="row.old_score" style="width: 100%" />
<el-input-number
:disabled="row.type === '2'"
:controls="false"
v-model="row.score"
step-strictly
:step="0.1"
:min="0"
:max="row.old_score"
style="width: 100%"
placeholder="--"
/>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<el-table-column label="操作" align="center" v-if="false">
<template #default="{ row }">
<el-button text type="primary" v-if="row.type === '2'">
<a :href="`${qaURL}/exam/markingPaper?exam_id=${row.exam_id}&id_number=${detail.id_number}`" target="_blank">查看</a>
<a :href="`${qaURL}/exam/markingPaper?exam_id=${row.exam_id}&id_number=${detail.id_number}`" target="_blank"
>查看</a
>
</el-button>
</template>
</el-table-column>
......
......@@ -29,7 +29,7 @@ function scoreMethodText(value: string) {
</script>
<template>
<el-dialog title="同步1+X考试成绩" :close-on-click-modal="false">
<el-dialog title="同步1+同步线上考试成绩" :close-on-click-modal="false">
<el-form>
<el-form-item label="赛项名称">
<el-select v-model="form.competition" value-key="id">
......
<script setup lang="ts">
import { getLiveList } from '../api'
const props = defineProps<{ data: any }>()
defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
function formatDuration(seconds: number) {
const minutes = Math.floor(seconds / 60)
const remainingSeconds = seconds % 60
return minutes > 0 ? `${minutes}m${remainingSeconds}s` : `${remainingSeconds}s`
}
// 列表配置
const listOptions = $computed(() => {
return {
remote: {
httpRequest: getLiveList,
params: {
experiment_id: props.data?.eid,
student_id: props.data?.sid,
},
callback(res: any) {
return { list: res.items }
},
},
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '选手姓名', prop: 'student_name' },
{ label: '商品名称', prop: 'live_commodity.title' },
{
label: '直播时长',
prop: 'live_duration',
computed({ row }: { row: any }) {
return formatDuration(row.live_duration)
},
},
{
label: '文件大小(M)',
prop: 'live_video_size',
computed({ row }: { row: any }) {
return convertBytes(row.live_video_size, 'MB')
},
},
{ label: '上传时间', prop: 'live_end_time' },
{
label: '视频状态',
prop: 'live_video_addres',
computed({ row }: { row: any }) {
return row.live_video_addres ? '已上传' : '未上传'
},
},
{ label: '操作', slots: 'table-x', width: 130 },
],
}
})
const handleViewLive = function (row: any) {
window.open(
`https://saas-dml-pro.ezijing.com/one/live/test/view?experiment_id=${props.data.eid}&id=${row.live_practice_id}&record_id=${row.id}&student_id=${props.data.sid}`
)
}
/**
* 将字节转换为指定单位
* @param {number} bytes - 字节数
* @param {string} unit - 转换目标单位 ('KB', 'MB', 'GB', 'TB')
* @param {number} [decimalPlaces=2] - 保留的小数位数
* @returns {number} - 转换后的值
*/
function convertBytes(bytes: any, unit: any, decimalPlaces = 2) {
const units: any = {
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
TB: 1024 * 1024 * 1024 * 1024,
}
if (!units[unit]) {
throw new Error("无效的单位,请选择 'KB', 'MB', 'GB', 或 'TB'")
}
const result = bytes / units[unit]
return parseFloat(result.toFixed(decimalPlaces))
}
console.log(convertBytes(1048576, 'MB'), 'convertBytes(1048576,')
</script>
<template>
<el-dialog
title="查看直播录像"
:close-on-click-modal="false"
width="700px"
@update:modelValue="(value) => $emit('update:modelValue', value)">
<AppList v-bind="listOptions" ref="appList">
<template #table-count="{ row }"> {{ row.checked_count }}/{{ row.need_check_count }} </template>
<template #table-score="{ row }">
<span :class="{ 'is-info': row.score_name !== '--' }">{{ row.score_name }}</span>
</template>
<template #table-x="{ row }">
<el-button text type="primary" @click="handleViewLive(row)">观看回放</el-button>
</template>
</AppList>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</el-dialog>
</template>
<style lang="scss" scoped>
.result {
padding: 40px 0;
display: flex;
align-items: center;
justify-content: center;
span {
padding: 0 10px;
font-size: 40px;
color: var(--main-color);
}
}
</style>
<script setup lang="ts">
import type { ReportItem } from '../types'
import { Refresh, Upload } from '@element-plus/icons-vue'
import AppList from '@/components/base/AppList.vue'
import { getCheckList } from '../api'
import { getCheckList, getReportList, getCheckView } from '../api'
import { useFilterList } from '../composables/useFilterList'
const SyncExamDialog = defineAsyncComponent(() => import('../components/SyncExamDialog.vue'))
......@@ -9,6 +10,8 @@ const ImportExamDialog = defineAsyncComponent(() => import('../components/Import
const ImportScoreDialog = defineAsyncComponent(() => import('../components/ImportScoreDialog.vue'))
const ScoreViewPicturesDialog = defineAsyncComponent(() => import('../components/ScoreViewPicturesDialog.vue'))
const ReportDialog = defineAsyncComponent(() => import('../components/ReportDialog.vue'))
const ScoreDialog = defineAsyncComponent(() => import('../components/ScoreDialog.vue'))
const ViewLiveDialog = defineAsyncComponent(() => import('../components/ViewLiveDialog.vue'))
const route = useRoute()
......@@ -117,7 +120,7 @@ const listOptions = $computed(() => {
// return getModuleStatus(row, 5)
// }
// },
{ label: '操作', slots: 'table-x', width: 100 }
{ label: '操作', slots: 'table-x', width: 140 }
]
}
})
......@@ -158,6 +161,35 @@ function handleViewReport(row: any) {
// }
// return '未开始'
// }
// 评分
let dialogVisible = $ref(false)
const reportList = ref<ReportItem[]>([])
// 左侧
let detail = $ref<any>()
provide('detail', $$(detail))
const handleScore = function (row: any) {
getCheckView({ id: row.id }).then((res: any) => {
detail = res.data
getReportList({ competition_id: res.data.competition_id, student_id: res.data.student_id }).then((r: any) => {
reportList.value = r.data.items
dialogVisible = true
})
})
}
// 直播录像
let liveDialogVisible = $ref(false)
let liveParams: any = $ref()
const handleViewLive = function (row: any) {
liveParams = {
sid: row.student_id,
eid: row.experiment_id
}
liveDialogVisible = true
}
</script>
<template>
......@@ -194,12 +226,36 @@ function handleViewReport(row: any) {
<span :class="{ 'is-info': row.score_name !== '--' }">{{ row.score_name }}</span>
</template>
<template #table-x="{ row }">
<el-button text type="primary" v-if="row.publish_status === '0'" v-permission="'v1-expert-check-set-score'">
<!-- 评分从详情里移出 -->
<!-- <el-button text type="primary" v-if="row.publish_status === '0'" v-permission="'v1-expert-check-set-score'">
<router-link :to="`/admin/contest/check/${row.id}`" target="_blank">评分</router-link>
</el-button>
</el-button> -->
<el-button text type="primary" @click="handleScore(row)" v-permission="'v1-expert-check-set-score'"
>评分</el-button
>
<br />
<el-button text type="primary" @click="handleViewReport(row)">查看报告</el-button><br />
<el-button text type="primary" @click="handleViewPicture(row)">查看截图</el-button>
<template v-if="row.competition_report_count !== '0'">
<el-button text type="primary" @click="handleViewReport(row)" v-if="row.competition_report_count !== '0'"
>查看报告</el-button
><br />
</template>
<template v-if="row.competition_competitor_pictures && row.competition_competitor_pictures?.pictures">
<el-button
text
type="primary"
@click="handleViewPicture(row)"
v-if="row.competition_competitor_pictures && row.competition_competitor_pictures?.pictures"
>查看截图</el-button
>
<br />
</template>
<el-button
text
type="primary"
@click="handleViewLive(row)"
v-if="row.experiment_id !== '0' && row.experiment_id"
>查看直播录像</el-button
>
</template>
</AppList>
</AppCard>
......@@ -211,11 +267,16 @@ function handleViewReport(row: any) {
<ImportScoreDialog
v-model="importScoreVisible"
@update="onUpdateSuccess"
v-if="importScoreVisible"></ImportScoreDialog>
v-if="importScoreVisible"
></ImportScoreDialog>
<!-- 查看截图 -->
<ScoreViewPicturesDialog v-model="viewPictureVisible" :data="rowData" v-if="rowData"></ScoreViewPicturesDialog>
<!-- 查看报告 -->
<ReportDialog v-model="viewReportVisible" :data="rowData" v-if="rowData && viewReportVisible"></ReportDialog>
<!-- 评分弹窗 -->
<ScoreDialog v-model="dialogVisible" :reportList="reportList"></ScoreDialog>
<!-- 查看直播视频录像 -->
<ViewLiveDialog v-model="liveDialogVisible" v-if="liveDialogVisible" :data="liveParams"></ViewLiveDialog>
</template>
<style lang="scss">
......
......@@ -42,6 +42,7 @@ const listOptions = $computed(() => {
{ label: '赛项名称', prop: 'competition.name' },
{ label: '选手姓名', prop: 'student.name' },
{ label: '参赛ID', prop: 'login_id' },
{ label: '手机号', prop: 'mobile' },
{
label: '性别',
prop: 'student.gender',
......
......@@ -29,7 +29,7 @@ onMounted(() => {
fetchCompetition()
})
const platformKey = ref('career_data_analysis')
const platformKey = ref('data_marketing')
const platformKeys = ref([
{ platform_key: 'career_data_analysis', name: '商业数据分析实验' },
{ platform_key: 'data_marketing', name: '数据营销实操' }
......@@ -59,7 +59,11 @@ onUnmounted(() => {
timer && clearInterval(timer)
})
const statistics = reactive({ competitor_count: 0, complete_answer_competitor_count: 0, starting_answer_competitor_count: 0 })
const statistics = reactive({
competitor_count: 0,
complete_answer_competitor_count: 0,
starting_answer_competitor_count: 0
})
async function fetchStatistics(competition_id: string, platform_key = 'career_data_analysis') {
const res = await getCompetitionStatistics({ competition_id, platform_key })
Object.assign(statistics, res.data.detail)
......@@ -182,7 +186,7 @@ function getModuleStatus(row: any, index: number) {
if (status == 3) return '<span class="is-completed2">部分完成</span>'
if (status == 4) return '<span class="is-completed">全部完成</span>'
} catch (error) {
// console.log(error)
console.log(error)
}
return '<span class="not-started">未开始</span>'
}
......@@ -197,8 +201,13 @@ function getModuleStatus(row: any, index: number) {
<el-select v-model="currentCompetition" value-key="id" size="large" style="margin-right: 20px">
<el-option v-for="item in competitionList" :key="item.id" :value="item" :label="item.name"></el-option>
</el-select>
<el-select v-model="platformKey" size="large">
<el-option v-for="item in platformKeys" :key="item.platform_key" :value="item.platform_key" :label="item.name"></el-option>
<el-select v-model="platformKey" size="large" v-if="false">
<el-option
v-for="item in platformKeys"
:key="item.platform_key"
:value="item.platform_key"
:label="item.name"
></el-option>
</el-select>
</el-row>
<ul class="statistics">
......
......@@ -37,6 +37,7 @@ const listOptions = $computed(() => {
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '专家姓名', prop: 'name' },
{ label: '电话', prop: 'mobile' },
{ label: '所在单位', prop: 'company' },
{
label: '性别',
......
......@@ -107,7 +107,7 @@ export function getContestExpertList(params: { id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition/experts', { params })
}
// 获取赛项未绑定的评分专家列表
export function getContestUnbindExpertList(params: { competition_id: string }) {
export function getContestUnbindExpertList(params: { competition_id: string; 'per-page'?: number }) {
return httpRequest.get('/api/resource/v1/backend/expert/unbind-competition-list', { params })
}
// 更新赛项评分专家
......@@ -193,3 +193,30 @@ export function exportDrawLotStudentList(params: { draw_rule_id: string }) {
export function getStudentDrawLotInfo(params: { draw_rule_id: string; student_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/student-draw-info', { params })
}
// 获取学员的抽签详情
export function getExperimentsList(params: {
type: number; name?: string; 'per-page': number
}) {
return httpRequest.get('/api/resource/v1/backend/competition/experiments', { params })
}
// 赛项绑定实验
export function bindExperiment(data: { id: string; experiment_id: any }) {
return httpRequest.post('/api/resource/v1/backend/competition/bind-experiment', data)
}
// 删除绑定的实验
export function unbindExperiment(data: { id: string, experiment_id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition/unbind-experiment', data)
}
// 获取赛项列表
export function getClassList(params?: { id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition/classes', { params })
}
// 评分专家绑定班级
export function bindClass(data: { id: string; expert_id: string; class_id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition/expert-bind-class', data)
}
......@@ -20,6 +20,7 @@ const listOptions = {
{ label: '序号', type: 'index', width: 60 },
{ label: '选手姓名', prop: 'student.name' },
{ label: '参赛ID', prop: 'login_id' },
{ label: '电话', prop: 'mobile' },
{
label: '性别',
prop: 'student.gender',
......
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论